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