2011-03-30 12:46:40 +00:00
|
|
|
#include <u.h>
|
|
|
|
#include <libc.h>
|
|
|
|
#include <auth.h>
|
|
|
|
#include <fcall.h>
|
|
|
|
#include <thread.h>
|
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
#define NS(x) ((vlong)x)
|
|
|
|
#define US(x) (NS(x) * 1000LL)
|
|
|
|
#define MS(x) (US(x) * 1000LL)
|
|
|
|
#define S(x) (MS(x) * 1000LL)
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
enum {
|
|
|
|
Synctime = S(8),
|
|
|
|
Nbuf = 10,
|
|
|
|
K = 1024,
|
|
|
|
Bufsize = 8 * K,
|
|
|
|
Stacksize = 8 * K,
|
2012-06-27 10:36:15 +00:00
|
|
|
Timer = 0, // Alt channels.
|
2011-03-30 12:46:40 +00:00
|
|
|
Unsent = 1,
|
|
|
|
Maxto = 24 * 3600, // A full day to reconnect.
|
2013-03-06 13:11:21 +00:00
|
|
|
Hdrsz = 3*4,
|
2011-03-30 12:46:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct {
|
2012-06-27 10:36:15 +00:00
|
|
|
uchar nb[4]; // Number of data bytes in this message
|
|
|
|
uchar msg[4]; // Message number
|
|
|
|
uchar acked[4]; // Number of messages acked
|
2011-03-30 12:46:40 +00:00
|
|
|
} Hdr;
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
typedef struct {
|
2012-06-27 10:36:15 +00:00
|
|
|
Hdr hdr;
|
|
|
|
uchar buf[Bufsize];
|
2011-03-30 12:46:40 +00:00
|
|
|
} Buf;
|
|
|
|
|
|
|
|
static Channel *unsent;
|
|
|
|
static Channel *unacked;
|
|
|
|
static Channel *empty;
|
2012-06-27 10:36:15 +00:00
|
|
|
static int netfd;
|
2017-02-04 00:39:36 +00:00
|
|
|
static ulong inmsg;
|
|
|
|
static ulong outmsg;
|
2011-03-30 12:46:40 +00:00
|
|
|
static char *devdir;
|
2012-06-27 10:36:15 +00:00
|
|
|
static int debug;
|
|
|
|
static int done;
|
2011-03-30 12:46:40 +00:00
|
|
|
static char *dialstring;
|
2012-06-27 10:36:15 +00:00
|
|
|
static int maxto = Maxto;
|
|
|
|
static char *Logname = "aan";
|
|
|
|
static int client;
|
2016-03-10 17:50:29 +00:00
|
|
|
static int reader = -1;
|
|
|
|
static int lostsync;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
static Alt a[] = {
|
|
|
|
/* c v op */
|
|
|
|
{ nil, nil, CHANRCV }, // timer
|
|
|
|
{ nil, nil, CHANRCV }, // unsent
|
|
|
|
{ nil, nil, CHANEND },
|
|
|
|
};
|
|
|
|
|
|
|
|
static void fromnet(void*);
|
|
|
|
static void fromclient(void*);
|
2016-03-10 17:50:29 +00:00
|
|
|
static int reconnect(int);
|
2011-03-30 12:46:40 +00:00
|
|
|
static void synchronize(void);
|
|
|
|
static int writen(int, uchar *, int);
|
|
|
|
static void timerproc(void *);
|
|
|
|
|
|
|
|
static void
|
|
|
|
usage(void)
|
|
|
|
{
|
2020-03-10 17:09:34 +00:00
|
|
|
fprint(2, "usage: %s [-cd] [-m maxto] dialstring|netdir\n", argv0);
|
2011-03-30 12:46:40 +00:00
|
|
|
exits("usage");
|
|
|
|
}
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
static int
|
|
|
|
catch(void *, char *s)
|
|
|
|
{
|
|
|
|
if (!strcmp(s, "alarm")) {
|
2012-06-27 10:36:15 +00:00
|
|
|
syslog(0, Logname, "Timed out while waiting for reconnect, exiting...");
|
2011-03-30 12:46:40 +00:00
|
|
|
threadexitsall(nil);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
static void*
|
|
|
|
emalloc(int n)
|
|
|
|
{
|
2017-02-04 00:39:36 +00:00
|
|
|
uintptr pc;
|
2012-06-27 10:36:15 +00:00
|
|
|
void *v;
|
|
|
|
|
|
|
|
pc = getcallerpc(&n);
|
|
|
|
v = malloc(n);
|
|
|
|
if(v == nil)
|
2017-02-04 00:39:36 +00:00
|
|
|
sysfatal("Cannot allocate memory; pc=%#p", pc);
|
2012-06-27 10:36:15 +00:00
|
|
|
setmalloctag(v, pc);
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
void
|
|
|
|
threadmain(int argc, char **argv)
|
|
|
|
{
|
|
|
|
vlong synctime;
|
2012-06-27 10:36:15 +00:00
|
|
|
int i, n, failed;
|
|
|
|
Channel *timer;
|
2016-03-10 17:50:29 +00:00
|
|
|
Hdr hdr;
|
2012-06-27 10:36:15 +00:00
|
|
|
Buf *b;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
ARGBEGIN {
|
|
|
|
case 'c':
|
|
|
|
client++;
|
|
|
|
break;
|
|
|
|
case 'd':
|
|
|
|
debug++;
|
|
|
|
break;
|
|
|
|
case 'm':
|
2012-06-27 10:36:15 +00:00
|
|
|
maxto = (int)strtol(EARGF(usage()), nil, 0);
|
2011-03-30 12:46:40 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
} ARGEND;
|
|
|
|
|
|
|
|
if (argc != 1)
|
|
|
|
usage();
|
|
|
|
|
|
|
|
if (!client) {
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
devdir = argv[0];
|
|
|
|
if ((p = strstr(devdir, "/local")) != nil)
|
|
|
|
*p = '\0';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
dialstring = argv[0];
|
|
|
|
|
|
|
|
if (debug > 0) {
|
|
|
|
int fd = open("#c/cons", OWRITE|OCEXEC);
|
|
|
|
dup(fd, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
atnotify(catch, 1);
|
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
/*
|
|
|
|
* Set up initial connection. use short timeout
|
|
|
|
* of 60 seconds so we wont hang arround for too
|
|
|
|
* long if there is some general connection problem
|
|
|
|
* (like NAT).
|
|
|
|
*/
|
2016-03-10 17:50:29 +00:00
|
|
|
netfd = reconnect(60);
|
2012-06-27 10:36:15 +00:00
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
unsent = chancreate(sizeof(Buf *), Nbuf);
|
|
|
|
unacked = chancreate(sizeof(Buf *), Nbuf);
|
|
|
|
empty = chancreate(sizeof(Buf *), Nbuf);
|
|
|
|
timer = chancreate(sizeof(uchar *), 1);
|
2012-06-27 10:36:15 +00:00
|
|
|
if(unsent == nil || unacked == nil || empty == nil || timer == nil)
|
|
|
|
sysfatal("Cannot allocate channels");
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
for (i = 0; i < Nbuf; i++)
|
|
|
|
sendp(empty, emalloc(sizeof(Buf)));
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
reader = proccreate(fromnet, nil, Stacksize);
|
|
|
|
if (reader < 0)
|
2012-06-27 10:36:15 +00:00
|
|
|
sysfatal("Cannot start fromnet; %r");
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
if (proccreate(fromclient, nil, Stacksize) < 0)
|
2012-06-27 10:36:15 +00:00
|
|
|
sysfatal("Cannot start fromclient; %r");
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
if (proccreate(timerproc, timer, Stacksize) < 0)
|
2012-06-27 10:36:15 +00:00
|
|
|
sysfatal("Cannot start timerproc; %r");
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
a[Timer].c = timer;
|
|
|
|
a[Unsent].c = unsent;
|
|
|
|
a[Unsent].v = &b;
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
Restart:
|
2011-03-30 12:46:40 +00:00
|
|
|
synctime = nsec() + Synctime;
|
|
|
|
failed = 0;
|
2016-03-10 17:50:29 +00:00
|
|
|
lostsync = 0;
|
2011-03-30 12:46:40 +00:00
|
|
|
while (!done) {
|
2016-03-10 17:50:29 +00:00
|
|
|
if (netfd < 0 || failed) {
|
2011-03-30 12:46:40 +00:00
|
|
|
// Wait for the netreader to die.
|
|
|
|
while (netfd >= 0) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "main; waiting for netreader to die\n");
|
2016-03-10 17:50:29 +00:00
|
|
|
threadint(reader);
|
2011-03-30 12:46:40 +00:00
|
|
|
sleep(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// the reader died; reestablish the world.
|
2016-03-10 17:50:29 +00:00
|
|
|
netfd = reconnect(maxto);
|
2011-03-30 12:46:40 +00:00
|
|
|
synchronize();
|
2016-03-10 17:50:29 +00:00
|
|
|
goto Restart;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
switch (alt(a)) {
|
|
|
|
case Timer:
|
|
|
|
if (netfd < 0 || nsec() < synctime)
|
|
|
|
break;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
PBIT32(hdr.nb, 0);
|
|
|
|
PBIT32(hdr.acked, inmsg);
|
|
|
|
PBIT32(hdr.msg, -1);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2013-03-06 13:11:21 +00:00
|
|
|
if (writen(netfd, (uchar *)&hdr, Hdrsz) < 0) {
|
2011-03-30 12:46:40 +00:00
|
|
|
failed = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
if(++lostsync > 2){
|
|
|
|
syslog(0, Logname, "connection seems hung up...");
|
|
|
|
failed = 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
synctime = nsec() + Synctime;
|
2011-03-30 12:46:40 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Unsent:
|
|
|
|
sendp(unacked, b);
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
if (netfd < 0)
|
|
|
|
break;
|
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
PBIT32(b->hdr.acked, inmsg);
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2017-02-04 00:39:36 +00:00
|
|
|
if (writen(netfd, (uchar *)&b->hdr, Hdrsz) < 0)
|
2011-03-30 12:46:40 +00:00
|
|
|
failed = 1;
|
2017-02-04 00:39:36 +00:00
|
|
|
else {
|
|
|
|
n = GBIT32(b->hdr.nb);
|
|
|
|
if (writen(netfd, b->buf, n) < 0)
|
|
|
|
failed = 1;
|
|
|
|
if (n == 0)
|
|
|
|
done = 1;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
syslog(0, Logname, "exiting...");
|
|
|
|
threadexitsall(nil);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
fromclient(void*)
|
|
|
|
{
|
2012-06-27 10:36:15 +00:00
|
|
|
int n;
|
|
|
|
Buf *b;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
2012-06-27 13:54:42 +00:00
|
|
|
threadsetname("fromclient");
|
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
do {
|
|
|
|
b = recvp(empty);
|
|
|
|
n = read(0, b->buf, Bufsize);
|
2017-02-04 00:39:36 +00:00
|
|
|
if (n < 0)
|
2012-06-27 10:36:15 +00:00
|
|
|
n = 0;
|
|
|
|
PBIT32(b->hdr.nb, n);
|
|
|
|
PBIT32(b->hdr.msg, outmsg);
|
2011-03-30 12:46:40 +00:00
|
|
|
sendp(unsent, b);
|
2012-06-27 10:36:15 +00:00
|
|
|
outmsg++;
|
|
|
|
} while(n > 0);
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fromnet(void*)
|
|
|
|
{
|
2016-03-10 17:50:29 +00:00
|
|
|
extern void _threadnote(void *, char *);
|
2017-02-04 00:39:36 +00:00
|
|
|
ulong m, acked, lastacked = 0;
|
|
|
|
int n, len;
|
2011-03-30 12:46:40 +00:00
|
|
|
Buf *b;
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
notify(_threadnote);
|
|
|
|
|
2012-06-27 13:54:42 +00:00
|
|
|
threadsetname("fromnet");
|
|
|
|
|
2012-06-27 10:36:15 +00:00
|
|
|
b = emalloc(sizeof(Buf));
|
2011-03-30 12:46:40 +00:00
|
|
|
while (!done) {
|
|
|
|
while (netfd < 0) {
|
2012-06-27 10:36:15 +00:00
|
|
|
if(done)
|
|
|
|
return;
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "fromnet; waiting for connection... (inmsg %lud)\n", inmsg);
|
2011-03-30 12:46:40 +00:00
|
|
|
sleep(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the header.
|
2013-03-06 13:11:21 +00:00
|
|
|
len = readn(netfd, (uchar *)&b->hdr, Hdrsz);
|
2012-06-27 10:36:15 +00:00
|
|
|
if (len <= 0) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if (debug) {
|
|
|
|
if (len < 0)
|
|
|
|
fprint(2, "fromnet; (hdr) network failure; %r\n");
|
|
|
|
else
|
|
|
|
fprint(2, "fromnet; (hdr) network closed\n");
|
|
|
|
}
|
2011-03-30 12:46:40 +00:00
|
|
|
close(netfd);
|
|
|
|
netfd = -1;
|
|
|
|
continue;
|
|
|
|
}
|
2016-03-10 17:50:29 +00:00
|
|
|
lostsync = 0; // reset timeout
|
2012-06-27 10:36:15 +00:00
|
|
|
n = GBIT32(b->hdr.nb);
|
|
|
|
m = GBIT32(b->hdr.msg);
|
|
|
|
acked = GBIT32(b->hdr.acked);
|
|
|
|
if (n == 0) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if (m == (ulong)-1)
|
2017-02-02 19:53:05 +00:00
|
|
|
continue;
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "fromnet; network closed\n");
|
2017-02-02 19:53:05 +00:00
|
|
|
break;
|
|
|
|
} else if (n < 0 || n > Bufsize) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "fromnet; message too big %d > %d\n", n, Bufsize);
|
2012-06-27 10:36:15 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = readn(netfd, b->buf, n);
|
|
|
|
if (len <= 0 || len != n) {
|
2011-03-30 12:46:40 +00:00
|
|
|
if (len == 0)
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "fromnet; network closed\n");
|
2011-03-30 12:46:40 +00:00
|
|
|
else
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "fromnet; network failure; %r\n");
|
2011-03-30 12:46:40 +00:00
|
|
|
close(netfd);
|
|
|
|
netfd = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-02-04 00:39:36 +00:00
|
|
|
if (m != inmsg) {
|
|
|
|
if(debug) fprint(2, "fromnet; skipping message %lud, currently at %lud\n", m, inmsg);
|
2011-03-30 12:46:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-02-02 19:53:05 +00:00
|
|
|
inmsg++;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
// Process the acked list.
|
2017-02-04 00:39:36 +00:00
|
|
|
while((long)(acked - lastacked) > 0) {
|
2011-03-30 12:46:40 +00:00
|
|
|
Buf *rb;
|
|
|
|
|
2017-02-04 00:39:36 +00:00
|
|
|
if((rb = recvp(unacked)) == nil)
|
|
|
|
break;
|
2012-06-27 10:36:15 +00:00
|
|
|
m = GBIT32(rb->hdr.msg);
|
|
|
|
if (m != lastacked) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "fromnet; rb %p, msg %lud, lastacked %lud\n", rb, m, lastacked);
|
2012-06-27 10:36:15 +00:00
|
|
|
sysfatal("fromnet; bug");
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
2012-06-27 10:36:15 +00:00
|
|
|
PBIT32(rb->hdr.msg, -1);
|
2011-03-30 12:46:40 +00:00
|
|
|
sendp(empty, rb);
|
2012-06-27 10:36:15 +00:00
|
|
|
lastacked++;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (writen(1, b->buf, len) < 0)
|
|
|
|
sysfatal("fromnet; cannot write to client; %r");
|
|
|
|
}
|
|
|
|
done = 1;
|
|
|
|
}
|
|
|
|
|
2016-03-10 17:50:29 +00:00
|
|
|
static int
|
2012-06-27 10:36:15 +00:00
|
|
|
reconnect(int secs)
|
2011-03-30 12:46:40 +00:00
|
|
|
{
|
2012-06-27 13:54:42 +00:00
|
|
|
NetConnInfo *nci;
|
2011-03-30 12:46:40 +00:00
|
|
|
char ldir[40];
|
|
|
|
int lcfd, fd;
|
|
|
|
|
|
|
|
if (dialstring) {
|
|
|
|
syslog(0, Logname, "dialing %s", dialstring);
|
2012-06-27 10:36:15 +00:00
|
|
|
alarm(secs*1000);
|
2012-06-27 13:54:42 +00:00
|
|
|
while ((fd = dial(dialstring, nil, ldir, nil)) < 0) {
|
2011-03-30 12:46:40 +00:00
|
|
|
char err[32];
|
|
|
|
|
|
|
|
err[0] = '\0';
|
|
|
|
errstr(err, sizeof err);
|
|
|
|
if (strstr(err, "connection refused")) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "reconnect; server died...\n");
|
2011-03-30 12:46:40 +00:00
|
|
|
threadexitsall("server died...");
|
|
|
|
}
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "reconnect: dialed %s; %s\n", dialstring, err);
|
2011-03-30 12:46:40 +00:00
|
|
|
sleep(1000);
|
|
|
|
}
|
2012-06-27 10:36:15 +00:00
|
|
|
alarm(0);
|
2011-03-30 12:46:40 +00:00
|
|
|
syslog(0, Logname, "reconnected to %s", dialstring);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
syslog(0, Logname, "waiting for connection on %s", devdir);
|
2012-06-27 10:36:15 +00:00
|
|
|
alarm(secs*1000);
|
2011-03-30 12:46:40 +00:00
|
|
|
if ((lcfd = listen(devdir, ldir)) < 0)
|
|
|
|
sysfatal("reconnect; cannot listen; %r");
|
|
|
|
if ((fd = accept(lcfd, ldir)) < 0)
|
|
|
|
sysfatal("reconnect; cannot accept; %r");
|
|
|
|
alarm(0);
|
|
|
|
close(lcfd);
|
|
|
|
}
|
2012-06-27 13:54:42 +00:00
|
|
|
|
|
|
|
if(nci = getnetconninfo(ldir, fd)){
|
|
|
|
syslog(0, Logname, "connected from %s", nci->rsys);
|
|
|
|
threadsetname(client? "client %s %s" : "server %s %s", ldir, nci->rsys);
|
|
|
|
freenetconninfo(nci);
|
|
|
|
} else
|
|
|
|
syslog(0, Logname, "connected");
|
2016-03-10 17:50:29 +00:00
|
|
|
|
|
|
|
return fd;
|
2011-03-30 12:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
synchronize(void)
|
|
|
|
{
|
|
|
|
Channel *tmp;
|
|
|
|
Buf *b;
|
2012-06-27 10:36:15 +00:00
|
|
|
int n;
|
2011-03-30 12:46:40 +00:00
|
|
|
|
|
|
|
// Ignore network errors here. If we fail during
|
|
|
|
// synchronization, the next alarm will pick up
|
|
|
|
// the error.
|
|
|
|
|
|
|
|
tmp = chancreate(sizeof(Buf *), Nbuf);
|
|
|
|
while ((b = nbrecvp(unacked)) != nil) {
|
2012-06-27 10:36:15 +00:00
|
|
|
n = GBIT32(b->hdr.nb);
|
2013-03-06 13:11:21 +00:00
|
|
|
writen(netfd, (uchar *)&b->hdr, Hdrsz);
|
2012-06-27 10:36:15 +00:00
|
|
|
writen(netfd, b->buf, n);
|
2011-03-30 12:46:40 +00:00
|
|
|
sendp(tmp, b);
|
|
|
|
}
|
|
|
|
chanfree(unacked);
|
|
|
|
unacked = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
writen(int fd, uchar *buf, int nb)
|
|
|
|
{
|
|
|
|
int len = nb;
|
|
|
|
|
|
|
|
while (nb > 0) {
|
|
|
|
int n;
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if ((n = write(fd, buf, nb)) < 0) {
|
2017-02-04 00:39:36 +00:00
|
|
|
if(debug) fprint(2, "writen; Write failed; %r\n");
|
2011-03-30 12:46:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf += n;
|
|
|
|
nb -= n;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
timerproc(void *x)
|
|
|
|
{
|
|
|
|
Channel *timer = x;
|
2012-06-27 13:54:42 +00:00
|
|
|
|
|
|
|
threadsetname("timer");
|
|
|
|
|
2011-03-30 12:46:40 +00:00
|
|
|
while (!done) {
|
|
|
|
sleep((Synctime / MS(1)) >> 1);
|
|
|
|
sendp(timer, "timer");
|
|
|
|
}
|
|
|
|
}
|