try leaving echo on on tcgettattr < 0
[spider.git] / src / client.c
1 /*
2  * C Client for the DX Spider cluster program
3  *
4  * Eventually this program will be a complete replacement
5  * for the perl version.
6  *
7  * This program provides the glue necessary to talk between
8  * an input (eg from telnet or ax25) and the perl DXSpider
9  * node.
10  *
11  * Currently, this program connects STDIN/STDOUT to the
12  * message system used by cluster.pl
13  *
14  * Copyright (c) 2000 Dirk Koopman G1TLH
15  *
16  * $Id$
17  */
18
19 #include <stdio.h>
20 #include <sys/time.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include <ctype.h>
24 #include <stdlib.h>
25 #include <stdarg.h>
26 #include <netdb.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <string.h>
32 #include <termios.h>
33 #include <regex.h>
34
35 #include "sel.h"
36 #include "cmsg.h"
37 #include "debug.h"
38
39 #define TEXT 1
40 #define MSG 2
41 #define MAXBUFL 1024
42
43 #ifndef MAXPATHLEN 
44 #define MAXPATHLEN 256
45 #endif
46
47 #define DEFPACLEN 128
48 #define MAXPACLEN 236
49 #define MAXCALLSIGN 9
50
51 #define DBUF 1
52 #define DMSG 2
53
54 typedef struct 
55 {
56         int cnum;                                       /* the connection number */
57         int sort;                                       /* the type of connection either text or msg */
58         cmsg_t *in;                                     /* current input message being built up */
59         cmsg_t *out;                            /* current output message being sent */
60         cmsg_t *obuf;                           /* current output being buffered */
61         reft *inq;                                      /* input queue */
62         reft *outq;                                     /* output queue */
63         sel_t *sp;                                      /* my select fcb address */
64         struct termios t;                       /* any termios associated with this cnum */
65         char echo;                                      /* echo characters back to this cnum */
66         char t_set;                                     /* the termios structure is valid */
67         char buffer_it;                         /* buffer outgoing packets for paclen */
68 } fcb_t;
69
70 typedef struct 
71 {
72         char *in;
73         regex_t *regex;
74 } myregex_t;
75
76
77 char *node_addr = "localhost";  /* the node tcp address, can be overridden by DXSPIDER_HOST */
78 int node_port = 27754;                  /* the tcp port of the node at the above address can be overidden by DXSPIDER_PORT*/
79 char *call;                                             /* the caller's callsign */
80 char *connsort;                                 /* the type of connection */
81 fcb_t *in;                                              /* the fcb of 'stdin' that I shall use */
82 fcb_t *node;                                    /* the fcb of the msg system */
83 char nl = '\n';                                 /* line end character */
84 char ending = 0;                                /* set this to end the program */
85 char send_Z = 1;                                /* set a Z record to the node on termination */
86 char echo = 1;                                  /* echo characters on stdout from stdin */
87 char int_tabs = 0;                              /* interpret tabs -> spaces */
88 char *root = "/spider";         /* root of data tree, can be overridden by DXSPIDER_ROOT  */
89 int timeout = 60;                               /* default timeout for logins and things */
90 int paclen = DEFPACLEN;                 /* default buffer size for outgoing packets */
91 int tabsize = 8;                                /* default tabsize for text messages */
92
93 myregex_t iscallreg[] = {               /* regexes to determine whether this is a reasonable callsign */
94         {
95                 "^[A-Z]+[0-9]+[A-Z]+[1-9]?$", 0                /* G1TLH G1TLH1 */
96         },
97         {
98                 "^[0-9]+[A-Z]+[0-9]+[A-Z]+[1-9]?$", 0          /* 2E0AAA 2E0AAA1 */
99         },
100         {
101                 "^[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0                /* G1TLH-2 */
102         },
103         {
104                 "^[0-9]+[A-Z]+[0-9]+[A-Z]+-[1-9]$", 0          /* 2E0AAA-2 */
105         },
106         {
107                 "^[A-Z]+[0-9]+[A-Z]+-1[0-5]$", 0               /* G1TLH-11 */
108         },
109         {
110                 "^[0-9]+[A-Z]+[0-9]+[A-Z]+-1[0-5]$", 0         /* 2E0AAA-11 */
111         },
112         {
113                 0, 0
114         }
115 };
116
117 void terminate(int);
118
119 /*
120  * utility routines - various
121  */
122
123 void die(char *s, ...)
124 {
125         char buf[2000];
126         
127         va_list ap;
128         va_start(ap, s);
129         vsnprintf(buf, sizeof(buf)-1, s, ap);
130         va_end(ap);
131         fprintf(stderr,"%s\n", buf);
132         terminate(-1);
133 }
134
135 char *strupper(char *s)
136 {
137         char *d = malloc(strlen(s)+1);
138         char *p = d;
139         
140         if (!d)
141                 die("out of room in strupper");
142         while (*p++ = toupper(*s++)) ;
143         return d;
144 }
145
146 char *strlower(char *s)
147 {
148         char *d = malloc(strlen(s)+1);
149         char *p = d;
150         
151         if (!d)
152                 die("out of room in strlower");
153         while (*p++ = tolower(*s++)) ;
154         return d;
155 }
156
157 int eq(char *a, char *b)
158 {
159         return (strcmp(a, b) == 0);
160 }
161
162 int xopen(char *dir, char *name, int mode)
163 {
164         char fn[MAXPATHLEN+1];
165         snprintf(fn, MAXPATHLEN, "%s/%s/%s", root, dir, name);
166         return open(fn, mode);
167 }
168
169 int iscallsign(char *s)
170 {
171         myregex_t *rp;
172
173         if (strlen(s) > MAXCALLSIGN)
174                 return 0;
175         
176         for (rp = iscallreg; rp->in; ++rp) {
177                 if (regexec(rp->regex, s, 0, 0, 0) == 0)
178                         return 1;
179         }
180         return 0;
181 }
182
183 /*
184  * higher level send and receive routines
185  */
186
187 fcb_t *fcb_new(int cnum, int sort)
188 {
189         fcb_t *f = malloc(sizeof(fcb_t));
190         if (!f)
191                 die("no room in fcb_new");
192         memset (f, 0, sizeof(fcb_t));
193         f->cnum = cnum;
194         f->sort = sort;
195         f->inq = chain_new();
196         f->outq = chain_new();
197         return f;
198 }
199
200 void flush_text(fcb_t *f)
201 {
202         if (f->obuf) {
203                 cmsg_send(f->outq, f->obuf, 0);
204                 f->sp->flags |= SEL_OUTPUT;
205                 f->obuf = 0;
206         }
207 }
208
209 void send_text(fcb_t *f, char *s, int l)
210 {
211         cmsg_t *mp;
212         char *p;
213         
214         if (f->buffer_it && f->obuf) {
215                 mp = f->obuf;
216         } else {
217                 f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
218         }
219
220         /* remove trailing spaces  */
221         while (l > 0 &&isspace(s[l-1]))
222                 --l;
223
224         for (p = s; p < s+l; ) {
225                 if (mp->inp >= mp->data + paclen) {
226                         flush_text(f);
227                         f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
228                 }
229                 *mp->inp++ = *p++;
230         }
231         if (mp->inp >= mp->data + paclen) {
232                 flush_text(f);
233                 f->obuf = mp = cmsg_new(paclen+1, f->sort, f);
234         }
235         if (nl == '\r')
236                 *mp->inp++ = nl;
237         else {
238                 *mp->inp++ = '\r';
239                 *mp->inp++ = '\n';
240         }
241         if (!f->buffer_it)
242                 flush_text(f);
243 }
244
245 void send_msg(fcb_t *f, char let, unsigned char *s, int l)
246 {
247         cmsg_t *mp;
248         int ln;
249         int myl = strlen(call)+2+l;
250
251         mp = cmsg_new(myl+4+1, f->sort, f);
252         *mp->inp++ = let;
253         strcpy(mp->inp, call);
254         mp->inp += strlen(call);
255         *mp->inp++ = '|';
256         if (l > 0) {
257                 unsigned char *p;
258                 for (p = s; p < s+l; ++p) {
259                         if (mp->inp >= mp->data + (myl - 4)) {
260                                 int off = mp->inp - mp->data;
261                                 myl += 256;
262                                 mp = realloc(mp, myl);
263                                 mp->inp = mp->data + off;
264                         }
265                         
266                         if (*p < 0x20 || *p > 0x7e || *p == '%') {
267                                 sprintf(mp->inp, "%%%02X", *p & 0xff);
268                                 mp->inp += strlen(mp->inp);
269                         } else 
270                                 *mp->inp++ = *p;
271                 }
272         } 
273         *mp->inp++ = '\n';
274         *mp->inp = 0;
275         cmsg_send(f->outq, mp, 0);
276         f->sp->flags |= SEL_OUTPUT;
277 }
278
279 /*
280  * the callback (called by sel_run) that handles all the inputs and outputs
281  */
282
283 int fcb_handler(sel_t *sp, int in, int out, int err)
284 {
285         fcb_t *f = sp->fcb;
286         cmsg_t *mp, *omp;
287         unsigned char c;
288         
289         /* input modes */
290         if (in) {
291                 char *p, buf[MAXBUFL];
292                 int r;
293
294                 /* read what we have into a buffer */
295                 r = read(f->cnum, buf, MAXBUFL);
296                 if (r < 0) {
297                         switch (errno) {
298                         case EINTR:
299                         case EINPROGRESS:
300                         case EAGAIN:
301                                 goto lout;
302                         default:
303 /*                              if (f->sort == MSG)
304                                 send_Z = 0; */
305                                 dbg(DBUF,"got errno %d in input", errno);
306                                 ending++;
307                                 return 0;
308                         }
309                 } else if (r == 0) {
310 /*                      if (f->sort == MSG)
311                         send_Z = 0; */
312                         dbg(DBUF, "ending normally");
313                         ending++;
314                         return 0;
315                 }
316
317                 dbgdump(DBUF, "in ->", buf, r);
318                 
319                 /* create a new message buffer if required */
320                 if (!f->in)
321                         f->in = cmsg_new(MAXBUFL+1, f->sort, f);
322                 mp = f->in;
323
324                 switch (f->sort) {
325                 case TEXT:
326                         p = buf;
327                         if (f->echo)
328                                 omp = cmsg_new(3*r+1, f->sort, f);
329                         while (r > 0 && p < &buf[r]) {
330
331                                 /* echo processing */
332                                 if (f->echo) {
333                                         switch (*p) {
334                                         case '\b':
335                                         case 0x7f:
336                                                 strcpy(omp->inp, "\b \b");
337                                                 omp->inp += strlen(omp->inp);
338                                                 break;
339                                         default:
340                                                 *omp->inp++ = *p;
341                                         }
342                                 }
343                                 
344                                 /* character processing */
345                                 switch (*p) {
346                                 case '\t':
347                                         if (int_tabs) {
348                                                 memset(mp->inp, ' ', tabsize);
349                                                 mp->inp += tabsize;
350                                                 ++p;
351                                         } else {
352                                                 *mp->inp++ = *p++;
353                                         }
354                                         break;
355                                 case '\b':
356                                 case 0x7f:
357                                         if (mp->inp > mp->data)
358                                                 mp->inp--;
359                                         ++p;
360                                         break;
361                                 default:
362                                         if (nl == '\n' && *p == '\r') {   /* ignore \r in telnet mode (ugh) */
363                                                 p++;
364                                         } else if (*p == nl) {
365                                                 if (mp->inp == mp->data)
366                                                         *mp->inp++ = ' ';
367                                                 *mp->inp = 0;              /* zero terminate it, but don't include it in the length */
368                                                 dbgdump(DMSG, "QUEUE TEXT", mp->data, mp->inp-mp->data);
369                                                 cmsg_send(f->inq, mp, 0);
370                                                 f->in = mp = cmsg_new(MAXBUFL+1, f->sort, f);
371                                                 ++p;
372                                         } else {
373                                                 if (mp->inp < &mp->data[MAXBUFL-8])
374                                                         *mp->inp++ = *p++;
375                                                 else {
376                                                         mp->inp = mp->data;
377                                                 }
378                                         }
379                                 }
380                         }
381                         
382                         /* queue any echo text */
383                         if (f->echo) {
384                                 dbgdump(DMSG, "QUEUE ECHO TEXT", omp->data, omp->inp - omp->data);
385                                 cmsg_send(f->outq, omp, 0);
386                                 f->sp->flags |= SEL_OUTPUT;
387                         }
388                         
389                         break;
390
391                 case MSG:
392                         p = buf;
393                         while (r > 0 && p < &buf[r]) {
394                                 unsigned char ch = *p++;
395                                 
396                                 if (mp->inp >= mp->data + (MAXBUFL-1)) {
397                                         mp->state = 0;
398                                         mp->inp = mp->data;
399                                         dbg(DMSG, "Message longer than %d received", MAXBUFL);
400                                 }
401
402                                 switch (mp->state) {
403                                 case 0: 
404                                         if (ch == '%') {
405                                                 c = 0;
406                                                 mp->state = 1;
407                                         } else if (ch == '\n') {
408                                                 /* kick it upstairs */
409                                                 *mp->inp = 0;
410                                                 dbgdump(DMSG, "QUEUE MSG", mp->data, mp->inp - mp->data);
411                                                 cmsg_send(f->inq, mp, 0);
412                                                 mp = f->in = cmsg_new(MAXBUFL+1, f->sort, f);
413                                         } else if (ch < 0x20 || ch > 0x7e) {
414                                                 dbg(DMSG, "Illegal character (0x%02X) received", *p);
415                                                 mp->inp = mp->data;
416                                         } else {
417                                                 *mp->inp++ = ch;
418                                         }
419                                         break;
420
421                                 case 1:
422                                         mp->state = 2;
423                                         if (ch >= '0' && ch <= '9') 
424                                                 c = (ch - '0') << 4;
425                                         else if (ch >= 'A' && ch <= 'F')
426                                                 c = (ch - 'A' + 10) << 4;
427                                         else if (ch >= 'a' && ch <= 'a')
428                                                 c = (ch - 'a' + 10) << 4;
429                                         else {
430                                                 dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state);
431                                                 mp->inp = mp->data;
432                                                 mp->state = 0;
433                                         }
434                                         break;
435                                         
436                                 case 2:
437                                         if (ch >= '0' && ch <= '9') 
438                                                 *mp->inp++ = c | (ch - '0');
439                                         else if (ch >= 'A' && ch <= 'F')
440                                                 *mp->inp++ = c | (ch - 'A' + 10);
441                                         else if (ch >= 'a' && ch <= 'a')
442                                                 *mp->inp++ = c | (ch - 'a' + 10);
443                                         else {
444                                                 dbg(DMSG, "Illegal hex char (%c) received in state %d", ch, mp->state);
445                                                 mp->inp = mp->data;
446                                         }
447                                         mp->state = 0;
448                                 }
449                         }
450                         break;
451                         
452                 default:
453                         die("invalid sort (%d) in input handler", f->sort);
454                 }
455         }
456         
457         /* output modes */
458 lout:;
459         if (out) {
460                 int l, r;
461                 
462                 if (!f->out) {
463                         mp = f->out = cmsg_next(f->outq);
464                         if (!mp) {
465                                 sp->flags &= ~SEL_OUTPUT;
466                                 return 0;
467                         }
468                         mp->inp = mp->data;
469                 }
470                 l = mp->size - (mp->inp - mp->data);
471                 if (l > 0) {
472                         
473                         dbgdump(DBUF, "<-out", mp->inp, l);
474                         
475                         r = write(f->cnum, mp->inp, l);
476                         if (r < 0) {
477                                 switch (errno) {
478                                 case EINTR:
479                                 case EINPROGRESS:
480                                 case EAGAIN:
481                                         goto lend;
482                                 default:
483 /*                                      if (f->sort == MSG)
484                                         send_Z = 0; */
485                                         dbg(DBUF,"got errno %d in output", errno);
486                                         ending++;
487                                         return;
488                                 }
489                         } else if (r > 0) {
490                                 mp->inp += r;
491                         }
492                 } else if (l < 0) 
493                         die("got negative length in handler on node");
494                 if (mp->inp - mp->data >= mp->size) {
495                         cmsg_callback(mp, 0);
496                         f->out = 0;
497                 }
498         }
499 lend:;
500         return 0;
501 }
502
503 /*
504  * things to do with initialisation
505  */
506
507 void initargs(int argc, char *argv[])
508 {
509         int i, c, err = 0;
510
511         while ((c = getopt(argc, argv, "h:p:x:")) > 0) {
512                 switch (c) {
513                 case 'h':
514                         node_addr = optarg;
515                         break;
516                 case 'l':
517                         paclen = atoi(optarg);
518                         if (paclen < 80)
519                                 paclen = 80;
520                         if (paclen > MAXPACLEN)
521                                 paclen = MAXPACLEN;
522                         break;
523                 case 'p':
524                         node_port = atoi(optarg);
525                         break;
526                 case 'x':
527                         dbginit("client");
528                         dbgset(atoi(optarg));
529                         break;
530                 default:
531                         ++err;
532                         goto lerr;
533                 }
534         }
535
536 lerr:
537         if (err) {
538                 die("usage: client [-x n|-h<host>|-p<port>|-l<paclen>] <call>|login [local|telnet|ax25]");
539         }
540         
541         if (optind < argc) {
542                 call = strupper(argv[optind]);
543                 ++optind;
544         }
545         if (!call)
546                 die("Must have at least a callsign (for now)");
547
548         if (optind < argc) {
549                 connsort = strlower(argv[optind]);
550                 if (eq(connsort, "telnet") || eq(connsort, "local")) {
551                         nl = '\n';
552                         echo = 1;
553                 } else if (eq(connsort, "ax25")) {
554                         nl = '\r';
555                         echo = 0;
556                 } else {
557                         die("2nd argument must be \"telnet\" or \"ax25\" or \"local\"");
558                 }
559         } else {
560                 connsort = "local";
561                 nl = '\n';
562                 echo = 1;
563         }
564
565         /* this is kludgy, but hey so is the rest of this! */
566         if (!eq(connsort, "ax25") && paclen == DEFPACLEN) {
567                 paclen = MAXPACLEN;
568         }
569 }
570
571 void connect_to_node()
572 {
573         struct hostent *hp, *gethostbyname();
574         struct sockaddr_in server;
575         int nodef;
576         sel_t *sp;
577                                 
578         if ((hp = gethostbyname(node_addr)) == 0) 
579                 die("Unknown host tcp host %s for printer", node_addr);
580
581         memset(&server, 0, sizeof server);
582         server.sin_family = AF_INET;
583         memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
584         server.sin_port = htons(node_port);
585                                                 
586         nodef = socket(AF_INET, SOCK_STREAM, 0);
587         if (nodef < 0) 
588                 die("Can't open socket to %s port %d (%d)", node_addr, node_port, errno);
589
590         if (connect(nodef, (struct sockaddr *) &server, sizeof server) < 0) {
591                 die("Error on connect to %s port %d (%d)", node_addr, node_port, errno);
592         }
593         node = fcb_new(nodef, MSG);
594         node->sp = sel_open(nodef, node, "Msg System", fcb_handler, MSG, SEL_INPUT);
595         
596 }
597
598 /*
599  * things to do with going away
600  */
601
602 void term_timeout(int i)
603 {
604         /* none of this is going to be reused so don't bother cleaning up properly */
605         if (in && in->t_set)
606                 tcsetattr(0, TCSANOW, &in->t);
607         if (node) {
608                 shutdown(node->cnum, 3);
609                 close(node->cnum);
610         }
611         exit(i);
612 }
613
614 void terminate(int i)
615 {
616         if (node && send_Z && call) {
617                 send_msg(node, 'Z', "bye", 3);
618         }
619         
620         signal(SIGALRM, term_timeout);
621         alarm(10);
622         
623         while ((in && !is_chain_empty(in->outq)) ||
624                    (node && !is_chain_empty(node->outq))) {
625                 sel_run();
626         }
627         if (in && in->t_set)
628                 tcsetattr(0, TCSADRAIN, &in->t);
629         if (node) {
630                 shutdown(node->cnum, 3);
631                 close(node->cnum);
632         }
633         exit(i);
634 }
635
636 void login_timeout(int i)
637 {
638         write(0, "Timed Out", 10);
639         write(0, &nl, 1);
640         sel_run();                                      /* force a coordination */
641         if (in && in->t_set)
642                 tcsetattr(0, TCSANOW, &in->t);
643         exit(i);
644 }
645
646 /*
647  * things to do with ongoing processing of inputs
648  */
649
650 void process_stdin()
651 {
652         cmsg_t *mp = cmsg_next(in->inq);
653         if (mp) {
654                 dbg(DMSG, "MSG size: %d", mp->size);
655         
656                 if (mp->size > 0 && mp->inp > mp->data) {
657                         send_msg(node, 'I', mp->data, mp->size);
658                 }
659                 cmsg_callback(mp, 0);
660         }
661 }
662
663 void process_node()
664 {
665         cmsg_t *mp = cmsg_next(node->inq);
666         if (mp) {
667                 dbg(DMSG, "MSG size: %d", mp->size);
668         
669                 if (mp->size > 0 && mp->inp > mp->data) {
670                         char *p = strchr(mp->data, '|');
671                         if (p)
672                                 p++;
673                         switch (mp->data[0]) {
674                         case 'Z':
675                                 send_Z = 0;
676                                 ending++;
677                                 return;
678                         case 'E':
679                                 if (isdigit(*p))
680                                         in->echo = *p - '0';
681                                 break;
682                         case 'B':
683                                 if (isdigit(*p))
684                                         in->buffer_it = *p - '0';
685                                 break;
686                         case 'D':
687                                 if (p) {
688                                         int l = mp->inp - (unsigned char *) p;
689                                         send_text(in, p, l);
690                                 }
691                                 break;
692                         default:
693                                 break;
694                         }
695                 }
696                 cmsg_callback(mp, 0);
697         } else {
698                 flush_text(in);
699         }
700 }
701
702 /*
703  * the program itself....
704  */
705
706 main(int argc, char *argv[])
707 {
708         /* set up environment */
709         {
710                 char *p = getenv("DXSPIDER_ROOT");
711                 if (p)
712                         root = p;
713                 p = getenv("DXSPIDER_HOST");
714                 if (p)
715                         node_addr = p;
716                 p = getenv("DXSPIDER_PORT");
717                 if (p)
718                         node_port = atoi(p);
719                 p = getenv("DXSPIDER_PACLEN");
720                 if (p) {
721                         paclen = atoi(p);
722                         if (paclen < 80)
723                                 paclen = 80;
724                         if (paclen > MAXPACLEN)
725                                 paclen = MAXPACLEN;
726                 }
727         }
728         
729         /* get program arguments, initialise stuff */
730         initargs(argc, argv);
731         sel_init(10, 0, 10000);
732
733         /* trap signals */
734         signal(SIGHUP, SIG_IGN);
735         signal(SIGINT, terminate);
736         signal(SIGQUIT, terminate);
737         signal(SIGTERM, terminate);
738 #ifdef SIGPWR
739         signal(SIGPWR, terminate);
740 #endif
741
742         /* compile regexes for iscallsign */
743         {
744                 myregex_t *rp;
745                 for (rp = iscallreg; rp->in; ++rp) {
746                         regex_t reg;
747                         int r = regcomp(&reg, rp->in, REG_EXTENDED|REG_ICASE|REG_NOSUB);
748                         if (r)
749                                 die("regcomp returned %d for '%s'", r, rp->in);
750                         rp->regex = malloc(sizeof(regex_t));
751                         if (!rp->regex)
752                                 die("out of room - compiling regexes");
753                         *rp->regex = reg;
754                 }
755         }
756         
757         /* is this a login? */
758         if (eq(call, "LOGIN")) {
759                 char buf[MAXPACLEN+1];
760                 char callsign[MAXCALLSIGN+1];
761                 int r, i;
762                 int f = xopen("data", "issue", 0);
763                 if (f > 0) {
764                         while ((r = read(f, buf, paclen)) > 0) {
765                                 if (nl != '\n') {
766                                         char *p;
767                                         for (p = buf; p < &buf[r]; ++p) {
768                                                 if (*p == '\n')
769                                                         *p = nl;
770                                         }
771                                 }
772                                 write(0, buf, r);
773                         }
774                         close(f);
775                 }
776                 signal(SIGALRM, login_timeout);
777                 alarm(timeout);
778                 write(0, "login: ", 7);
779                 dbgdump(DBUF, "<-out", "login: ", 7);
780                 for (i = 0;;) {
781                         char *p;
782                     r = read(0, buf, 20);
783                         dbgdump(DBUF, "in ->", buf, r);
784                         if (r <= 0)
785                                 die("No login or error (%d)", errno);
786                         write(0, buf, r);
787                         dbgdump(DBUF, "<-out", buf, r);
788                         for (p = buf; p < buf+r; ++p) {
789                                 if (i < MAXCALLSIGN) {
790                                         if (*p == '\r' || *p == '\n')
791                                                 goto lgotcall;
792                                         else if (isalnum(*p))
793                                                 callsign[i++] = *p;
794                                         else
795                                                 die("%c is not a valid callsign character", *p);
796                                 } else 
797                                         die("callsign entered is too long");
798                         }
799                 }
800 lgotcall:
801                 signal(SIGALRM, SIG_IGN);
802                 alarm(0);
803                 callsign[i]= 0;
804                 call = strupper(callsign);
805         }
806
807         /* check the callsign */
808         if (!iscallsign(call)) {
809                 die("Sorry, %s isn't a valid callsign", call);
810         }
811         
812         /* connect up stdin */
813         in = fcb_new(0, TEXT);
814         in->sp = sel_open(0, in, "STDIN", fcb_handler, TEXT, SEL_INPUT);
815         if (tcgetattr(0, &in->t) < 0) {
816 /*              echo = 0; */
817                 in->t_set = 0;
818         } else {
819                 struct termios t = in->t;
820                 t.c_lflag &= ~(ECHO|ECHONL|ICANON);
821                 if (tcsetattr(0, TCSANOW, &t) < 0) 
822                         die("tcsetattr (%d)", errno);
823                 in->echo = echo;
824                 in->t_set = 1;
825         }
826         in->buffer_it = 1;
827
828         /* connect up node */
829         connect_to_node();
830
831         /* tell the cluster who I am */
832         send_msg(node, 'A', connsort, strlen(connsort));
833         
834         /* main processing loop */
835         while (!ending) {
836                 sel_run();
837                 if (!ending) {
838                         process_stdin();
839                         process_node();
840                 }
841         }
842         terminate(0);
843 }
844
845
846
847
848
849