X-Git-Url: http://dxcluster.net/gitweb/gitweb.cgi?a=blobdiff_plain;f=src%2Fclient.c;h=5ff3401097726493f6a0c7b0533a0698139e336e;hb=refs%2Fheads%2Fnewpc92timings;hp=1473682a4acaf8f2cfea3d145b16220735a95396;hpb=3a94546b3091bc3208094da8222de2c91c9263c6;p=spider.git diff --git a/src/client.c b/src/client.c index 1473682a..5ff34010 100644 --- a/src/client.c +++ b/src/client.c @@ -16,9 +16,10 @@ * $Id$ */ +#include #include #include -#include +#include #include #include #include @@ -34,6 +35,7 @@ #include "sel.h" #include "cmsg.h" +#include "chain.h" #include "debug.h" #define TEXT 1 @@ -44,12 +46,15 @@ #define MAXPATHLEN 256 #endif -#define DEFPACLEN 128 +#define DEFPACLEN 236 #define MAXPACLEN 236 #define MAXCALLSIGN 9 #define DBUF 1 #define DMSG 2 +#define DSTS 4 + +#define UC unsigned char typedef struct { @@ -83,7 +88,6 @@ fcb_t *node; /* the fcb of the msg system */ char nl = '\n'; /* line end character */ char mode = 1; /* 0 - ax25, 1 - normal telnet, 2 - nlonly telnet */ char ending = 0; /* set this to end the program */ -char send_Z = 1; /* set a Z record to the node on termination */ char echo = 1; /* echo characters on stdout from stdin */ char int_tabs = 0; /* interpret tabs -> spaces */ char *root = "/spider"; /* root of data tree, can be overridden by DXSPIDER_ROOT */ @@ -91,7 +95,18 @@ int timeout = 60; /* default timeout for logins and things */ int paclen = DEFPACLEN; /* default buffer size for outgoing packets */ int tabsize = 8; /* default tabsize for text messages */ char *connsort = "local"; /* the connection variety */ - +int state = 0; /* the current state of the connection */ +int laststate = 0; /* the last state we were in */ +char echocancel = 1; /* echo cancelling */ +reft echobase; /* the anti echo queue */ +int maxecho = 5; /* the depth of the anti echo queue */ +int echon; /* no of entries in the anti echo queue */ + +#define CONNECTED 100 +#define WAITLOGIN 1 +#define WAITPASSWD 2 +#define WAITINPUT 10 +#define DOCHAT 20 myregex_t iscallreg[] = { /* regexes to determine whether this is a reasonable callsign */ { @@ -101,10 +116,10 @@ myregex_t iscallreg[] = { /* regexes to determine whether this is a reasonable "^[0-9]+[A-Z]+[0-9]+[A-Z]+[1-9]?$", 0 /* 2E0AAA 2E0AAA1 */ }, { - "^[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0 /* G1TLH-2 */ + "^[A-Z]+[0-9]+[A-Z]+-[0-9]$", 0 /* G1TLH-2 */ }, { - "^[0-9]+[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0 /* 2E0AAA-2 */ + "^[0-9]+[A-Z]+[0-9]+[A-Z]+-[0-9]$", 0 /* 2E0AAA-2 */ }, { "^[A-Z]+[0-9]+[A-Z]+-1[0-5]$", 0 /* G1TLH-11 */ @@ -123,6 +138,13 @@ void terminate(int); * utility routines - various */ +void chgstate(int new) +{ + laststate = state; + state = new; + dbg(DSTS, "chg state %d->%d", laststate, state); +} + void die(char *s, ...) { char buf[2000]; @@ -162,11 +184,11 @@ int eq(char *a, char *b) return (strcmp(a, b) == 0); } -int xopen(char *dir, char *name, int mode) +FILE *xopen(char *dir, char *name, char *mode) { char fn[MAXPATHLEN+1]; snprintf(fn, MAXPATHLEN, "%s/%s/%s", root, dir, name); - return open(fn, mode); + return fopen(fn, mode); } int iscallsign(char *s) @@ -183,6 +205,14 @@ int iscallsign(char *s) return 0; } +void reaper(int i) +{ + pid_t mypid; + while ((mypid = waitpid(-1, 0, WNOHANG)) > 0) { + ; + } +} + /* * higher level send and receive routines */ @@ -203,13 +233,30 @@ fcb_t *fcb_new(int cnum, int sort) void flush_text(fcb_t *f) { if (f->obuf) { - cmsg_send(f->outq, f->obuf, 0); + /* save this onto the front of the echo chain */ + cmsg_t *imp = f->obuf; + int size = imp->inp - imp->data; + cmsg_t *emp = cmsg_new(size, imp->sort, imp->portp); + + emp->size = size; + memcpy(emp->data, imp->data, size); + emp->inp = emp->data + size; /* just in case */ + if (echocancel) { + chain_add(&echobase, emp); + if (++echon > maxecho) { + emp = cmsg_prev(&echobase); + cmsg_free(emp); + } + } + + /* queue it for sending */ + cmsg_send(f->outq, imp, 0); f->sp->flags |= SEL_OUTPUT; f->obuf = 0; } } -void send_text(fcb_t *f, char *s, int l) +void send_text(fcb_t *f, char *s, int l, int nlreq) { cmsg_t *mp; char *p; @@ -235,18 +282,20 @@ void send_text(fcb_t *f, char *s, int l) flush_text(f); f->obuf = mp = cmsg_new(paclen+1, f->sort, f); } - if (nl == '\r') - *mp->inp++ = nl; - else { - if (mode != 2) - *mp->inp++ = '\r'; - *mp->inp++ = '\n'; + if (nlreq) { + if (nl == '\r') + *mp->inp++ = nl; + else { + if (mode != 2) + *mp->inp++ = '\r'; + *mp->inp++ = '\n'; + } } if (!f->buffer_it) flush_text(f); } -void send_msg(fcb_t *f, char let, unsigned char *s, int l) +void send_msg(fcb_t *f, char let, UC *s, int l) { cmsg_t *mp; int ln; @@ -258,7 +307,7 @@ void send_msg(fcb_t *f, char let, unsigned char *s, int l) mp->inp += strlen(call); *mp->inp++ = '|'; if (l > 0) { - unsigned char *p; + UC *p; for (p = s; p < s+l; ++p) { if (mp->inp >= mp->data + (myl - 4)) { int off = mp->inp - mp->data; @@ -280,6 +329,26 @@ void send_msg(fcb_t *f, char let, unsigned char *s, int l) f->sp->flags |= SEL_OUTPUT; } +/* + * send a file out to the user + */ +void send_file(char *name) +{ + int i; + char buf[MAXPACLEN+1]; + + FILE *f = xopen("data", name, "r"); + if (f) { + while (fgets(buf, paclen, f)) { + i = strlen(buf); + if (i && buf[i-1] == '\n') + buf[--i] = 0; + send_text(in, buf, i, 1); + } + fclose(f); + } +} + /* * the callback (called by sel_run) that handles all the inputs and outputs */ @@ -288,7 +357,7 @@ int fcb_handler(sel_t *sp, int in, int out, int err) { fcb_t *f = sp->fcb; cmsg_t *mp, *omp; - unsigned char c; + UC c; /* input modes */ if (ending == 0 && in) { @@ -304,18 +373,14 @@ int fcb_handler(sel_t *sp, int in, int out, int err) case EAGAIN: goto lout; default: -/* if (f->sort == MSG) - send_Z = 0; */ dbg(DBUF,"got errno %d in input", errno); ending++; - return 0; + return 1; } } else if (r == 0) { -/* if (f->sort == MSG) - send_Z = 0; */ dbg(DBUF, "ending normally"); ending++; - return 0; + return 1; } dbgdump(DBUF, "in ->", buf, r); @@ -340,6 +405,12 @@ int fcb_handler(sel_t *sp, int in, int out, int err) strcpy(omp->inp, "\b \b"); omp->inp += strlen(omp->inp); break; + case '\r': + break; + case '\n': + strcpy(omp->inp, "\r\n"); + omp->inp += 2; + break; default: *omp->inp++ = *p; } @@ -365,6 +436,8 @@ int fcb_handler(sel_t *sp, int in, int out, int err) default: if (nl == '\n' && *p == '\r') { /* ignore \r in telnet mode (ugh) */ p++; + } else if (nl == '\r' && *p == '\n') { /* and ignore \n in ax25 mode (double ugh) */ + p++; } else if (*p == nl) { if (mp->inp == mp->data) *mp->inp++ = ' '; @@ -395,7 +468,7 @@ int fcb_handler(sel_t *sp, int in, int out, int err) case MSG: p = buf; while (r > 0 && p < &buf[r]) { - unsigned char ch = *p++; + UC ch = *p++; if (mp->inp >= mp->data + (MAXBUFL-1)) { mp->state = 0; @@ -428,10 +501,8 @@ int fcb_handler(sel_t *sp, int in, int out, int err) c = (ch - '0') << 4; else if (ch >= 'A' && ch <= 'F') c = (ch - 'A' + 10) << 4; - else if (ch >= 'a' && ch <= 'a') - c = (ch - 'a' + 10) << 4; else { - dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state); + dbg(DMSG, "Illegal hex char (%c) received in state 1", ch); mp->inp = mp->data; mp->state = 0; } @@ -442,10 +513,8 @@ int fcb_handler(sel_t *sp, int in, int out, int err) *mp->inp++ = c | (ch - '0'); else if (ch >= 'A' && ch <= 'F') *mp->inp++ = c | (ch - 'A' + 10); - else if (ch >= 'a' && ch <= 'a') - *mp->inp++ = c | (ch - 'a' + 10); else { - dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state); + dbg(DMSG, "Illegal hex char (%c) received in state 2", ch); mp->inp = mp->data; } mp->state = 0; @@ -484,8 +553,6 @@ lout:; case EAGAIN: goto lend; default: -/* if (f->sort == MSG) - send_Z = 0; */ dbg(DBUF,"got errno %d in output", errno); ending++; return; @@ -507,7 +574,7 @@ lend:; /* * set up the various mode flags, NL endings and things */ -void setmode(char *m) +void setconntype(char *m) { connsort = strlower(m); if (eq(connsort, "telnet") || eq(connsort, "local") || eq(connsort, "nlonly")) { @@ -527,6 +594,171 @@ void setmode(char *m) } } + +/* + * things to do with ongoing processing of inputs + */ + +void process_stdin() +{ + cmsg_t *wmp, *mp = cmsg_next(in->inq); + char *p, hasa, hasn, i; + char callsign[MAXCALLSIGN+1]; + + if (mp) { + dbg(DMSG, "MSG size: %d", mp->size); + + /* check for echos */ + if (echocancel) { + for (wmp = 0; wmp = chain_get_next(&echobase, wmp); ) { + if (!memcmp(wmp->data, mp->data, wmp->size)) { + cmsg_callback(mp, 0); + return; + } + } + } + + switch (state) { + case CONNECTED: + if (mp->size > 0) { + send_msg(node, 'I', mp->data, mp->size); + } + break; + case WAITLOGIN: + for (i = 0; i < mp->size; ++i) { + UC ch = mp->data[i]; + if (i < MAXCALLSIGN) { + if (isalpha(ch)) + ++hasa; + if (isdigit(ch)) + ++hasn; + if (isalnum(ch) || ch == '-') + callsign[i] = ch; + else + die("invalid callsign"); + } else + die("invalid callsign"); + } + callsign[i]= 0; + if (strlen(callsign) < 3) + die("invalid callsign"); + if (hasa && hasn) + ; + else + die("invalid callsign"); + call = strupper(callsign); + + /* check the callsign against the regexes */ + if (!iscallsign(call)) { + die("Sorry, %s isn't a valid callsign", call); + } + + /* strip off a '-0' at the end */ + i = strlen(call); + if (call[i-1] == '0' && call[i-2] == '-') + call[i-2] = 0; + + alarm(0); + signal(SIGALRM, SIG_IGN); + + /* tell the cluster who I am */ + send_msg(node, 'A', connsort, strlen(connsort)); + + chgstate(CONNECTED); + send_file("connected"); + break; + + case DOCHAT: + + break; + } + + cmsg_callback(mp, 0); + + } +} + +void process_node() +{ + cmsg_t *mp = cmsg_next(node->inq); + if (mp) { + dbg(DMSG, "MSG size: %d", mp->size); + + if (mp->size > 0 && mp->inp > mp->data) { + char *p = strchr(mp->data, '|'); + if (p) + p++; + switch (mp->data[0]) { + case 'Z': + ending++; + return; + case 'E': + if (isdigit(*p)) + in->echo = *p - '0'; + break; + case 'B': + if (isdigit(*p)) + in->buffer_it = *p - '0'; + break; + case 'D': + if (p) { + int l = mp->inp - (UC *) p; + send_text(in, p, l, 1); + } + break; + default: + break; + } + } + cmsg_callback(mp, 0); + } else { + flush_text(in); + } +} + +/* + * things to do with going away + */ + +void term_timeout(int i) +{ + /* none of this is going to be reused so don't bother cleaning up properly */ + if (isatty(0)) + tcflush(0, TCIOFLUSH); + kill(getpid(), 9); /* commit suicide */ +} + +void terminate(int i) +{ + signal(SIGALRM, term_timeout); + alarm(10); + + if (node && node->sp->sort) { + sel_close(node->sp); + } + while (in && in->sp->sort && !is_chain_empty(in->outq)) { + sel_run(); + } + sel_run(); + sel_run(); + sel_run(); + sel_run(); + sel_run(); + sel_run(); + sel_run(); + sel_run(); + if (in && in->t_set) + tcsetattr(0, TCSADRAIN, &in->t); + exit(i); +} + +void login_timeout(int i) +{ + write(0, "Timed Out", 10); + write(0, &nl, 1); + terminate(0); +} + /* * things to do with initialisation */ @@ -535,8 +767,11 @@ void initargs(int argc, char *argv[]) { int i, c, err = 0; - while ((c = getopt(argc, argv, "h:p:x:")) > 0) { + while ((c = getopt(argc, argv, "eh:l:p:x:")) > 0) { switch (c) { + case 'e': + echocancel ^= 1; + break; case 'h': node_addr = optarg; break; @@ -562,7 +797,7 @@ void initargs(int argc, char *argv[]) lerr: if (err) { - die("usage: client [-x n|-h|-p|-l] |login [local|telnet|ax25]"); + die("usage: client [-e|-x n|-h|-p|-l] |login [local|telnet|ax25]"); } if (optind < argc) { @@ -573,9 +808,9 @@ lerr: die("Must have at least a callsign (for now)"); if (optind < argc) { - setmode(argv[optind]); + setconntype(argv[optind]); } else { - setmode("local"); + setconntype("local"); } /* this is kludgy, but hey so is the rest of this! */ @@ -589,8 +824,10 @@ void connect_to_node() struct hostent *hp, *gethostbyname(); struct sockaddr_in server; int nodef; + int one = 1; sel_t *sp; - + struct linger lg; + if ((hp = gethostbyname(node_addr)) == 0) die("Unknown host tcp host %s for printer", node_addr); @@ -606,121 +843,41 @@ void connect_to_node() if (connect(nodef, (struct sockaddr *) &server, sizeof server) < 0) { die("Error on connect to %s port %d (%d)", node_addr, node_port, errno); } - node = fcb_new(nodef, MSG); - node->sp = sel_open(nodef, node, "Msg System", fcb_handler, MSG, SEL_INPUT); - -} -/* - * things to do with going away - */ - -void term_timeout(int i) -{ - /* none of this is going to be reused so don't bother cleaning up properly */ - if (in && in->t_set) - tcsetattr(0, TCSANOW, &in->t); - if (node) { - shutdown(node->cnum, 3); - close(node->cnum); + memset(&lg, 0, sizeof lg); + if (setsockopt(nodef, SOL_SOCKET, SO_LINGER, &lg, sizeof lg) < 0) { + die("Error on SO_LINGER to %s port %d (%d)", node_addr, node_port, errno); } - exit(i); -} - -void terminate(int i) -{ - if (node && send_Z && call) { - send_msg(node, 'Z', "bye", 3); + if (setsockopt(nodef, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof one) < 0) { + die("Error on SO_KEEPALIVE to %s port %d (%d)", node_addr, node_port, errno); } - signal(SIGALRM, term_timeout); - alarm(10); + node = fcb_new(nodef, MSG); + node->sp = sel_open(nodef, node, "Msg System", fcb_handler, MSG, SEL_INPUT); - while ((in && !is_chain_empty(in->outq)) || - (node && !is_chain_empty(node->outq))) { - sel_run(); - } - if (in && in->t_set) - tcsetattr(0, TCSADRAIN, &in->t); - if (node) { - shutdown(node->cnum, 3); - close(node->cnum); - } - exit(i); -} - -void login_timeout(int i) -{ - write(0, "Timed Out", 10); - write(0, &nl, 1); - sel_run(); /* force a coordination */ - if (in && in->t_set) - tcsetattr(0, TCSANOW, &in->t); - exit(i); } /* - * things to do with ongoing processing of inputs + * the program itself.... */ -void process_stdin() +main(int argc, char *argv[]) { - cmsg_t *mp = cmsg_next(in->inq); - if (mp) { - dbg(DMSG, "MSG size: %d", mp->size); - - if (mp->size > 0 && mp->inp > mp->data) { - send_msg(node, 'I', mp->data, mp->size); + /* compile regexes for iscallsign */ + { + myregex_t *rp; + for (rp = iscallreg; rp->in; ++rp) { + regex_t reg; + int r = regcomp(®, rp->in, REG_EXTENDED|REG_ICASE|REG_NOSUB); + if (r) + die("regcomp returned %d for '%s'", r, rp->in); + rp->regex = malloc(sizeof(regex_t)); + if (!rp->regex) + die("out of room - compiling regexes"); + *rp->regex = reg; } - cmsg_callback(mp, 0); } -} - -void process_node() -{ - cmsg_t *mp = cmsg_next(node->inq); - if (mp) { - dbg(DMSG, "MSG size: %d", mp->size); - if (mp->size > 0 && mp->inp > mp->data) { - char *p = strchr(mp->data, '|'); - if (p) - p++; - switch (mp->data[0]) { - case 'Z': - send_Z = 0; - ending++; - return; - case 'E': - if (isdigit(*p)) - in->echo = *p - '0'; - break; - case 'B': - if (isdigit(*p)) - in->buffer_it = *p - '0'; - break; - case 'D': - if (p) { - int l = mp->inp - (unsigned char *) p; - send_text(in, p, l); - } - break; - default: - break; - } - } - cmsg_callback(mp, 0); - } else { - flush_text(in); - } -} - -/* - * the program itself.... - */ - -main(int argc, char *argv[]) -{ /* set up environment */ { char *p = getenv("DXSPIDER_ROOT"); @@ -754,88 +911,20 @@ main(int argc, char *argv[]) #ifdef SIGPWR signal(SIGPWR, terminate); #endif - - /* compile regexes for iscallsign */ - { - myregex_t *rp; - for (rp = iscallreg; rp->in; ++rp) { - regex_t reg; - int r = regcomp(®, rp->in, REG_EXTENDED|REG_ICASE|REG_NOSUB); - if (r) - die("regcomp returned %d for '%s'", r, rp->in); - rp->regex = malloc(sizeof(regex_t)); - if (!rp->regex) - die("out of room - compiling regexes"); - *rp->regex = reg; - } - } - /* is this a login? */ - if (eq(call, "LOGIN") || eq(call, "login")) { - - char buf[MAXPACLEN+1]; - char callsign[MAXCALLSIGN+1]; - int r, i; - int f = xopen("data", "issue", 0); - if (f > 0) { - while ((r = read(f, buf, paclen)) > 0) { - if (nl != '\n') { - char *p; - for (p = buf; p < &buf[r]; ++p) { - if (*p == '\n') - *p = nl; - } - } - write(0, buf, r); - } - close(f); - } - signal(SIGALRM, login_timeout); - alarm(timeout); - write(0, "login: ", 7); - dbgdump(DBUF, "<-out", "login: ", 7); - for (i = 0;;) { - char *p; - r = read(0, buf, 20); - dbgdump(DBUF, "in ->", buf, r); - if (r <= 0) - die("No login or error (%d)", errno); - write(0, buf, r); - dbgdump(DBUF, "<-out", buf, r); - for (p = buf; p < buf+r; ++p) { - if (i < MAXCALLSIGN) { - if (*p == '\r' || *p == '\n') - goto lgotcall; - else if (isalnum(*p) || *p == '-') - callsign[i++] = *p; - else - die("%c is not a valid callsign character", *p); - } else - die("callsign entered is too long"); - } - } -lgotcall: - signal(SIGALRM, SIG_IGN); - alarm(0); - callsign[i]= 0; - call = strupper(callsign); - } + /* init a few things */ + chain_init(&echobase); - /* check the callsign */ - if (!iscallsign(call)) { - die("Sorry, %s isn't a valid callsign", call); - } - /* connect up stdin */ in = fcb_new(0, TEXT); in->sp = sel_open(0, in, "STDIN", fcb_handler, TEXT, SEL_INPUT); - if (tcgetattr(0, &in->t) < 0) { -/* echo = 0; */ + if (!isatty(0) || tcgetattr(0, &in->t) < 0) { in->echo = echo; in->t_set = 0; } else { struct termios t = in->t; t.c_lflag &= ~(ECHO|ECHONL|ICANON); + t.c_oflag = 0; if (tcsetattr(0, TCSANOW, &t) < 0) die("tcsetattr (%d)", errno); in->echo = echo; @@ -846,9 +935,34 @@ lgotcall: /* connect up node */ connect_to_node(); - /* tell the cluster who I am */ - send_msg(node, 'A', connsort, strlen(connsort)); + /* is this a login? */ + if (eq(call, "LOGIN") || eq(call, "login")) { + send_file("issue"); + signal(SIGALRM, login_timeout); + alarm(timeout); + send_text(in, "login: ", 7, 0); + chgstate(WAITLOGIN); + } else { + int i; + + /* check the callsign against the regexes */ + if (!iscallsign(call)) { + die("Sorry, %s isn't a valid callsign", call); + } + + /* strip off a '-0' at the end */ + i = strlen(call); + if (call[i-1] == '0' && call[i-2] == '-') + call[i-2] = 0; + + /* tell the cluster who I am */ + send_msg(node, 'A', connsort, strlen(connsort)); + + chgstate(CONNECTED); + send_file("connected"); + } + /* main processing loop */ while (ending == 0) { sel_run();