|
|
1.1 root 1: /*
2: * ***********
3: * * XCHAT.C *
4: * ***********
5: *
6: * Extended chat processor for Taylor UUCP. See accompanying documentation.
7: *
8: * Written by:
9: * Bob Denny ([email protected])
10: * Based on code in DECUS UUCP (for VAX/VMS)
11: *
12: * History:
13: * Version 1.0 shipped with Taylor 1.03. No configuration info inside.
14: *
15: * Bob Denny - Sun Aug 30 18:41:30 1992
16: * V1.1 - long overdue changes for other systems. Rip out interval
17: * timer code, use timer code from Taylor UUCP, use select()
18: * for timed reads. Use Taylor UUCP "conf.h" file to set
19: * configuration for this program. Add defaulting of script
20: * and log file paths.
21: *
22: * Bugs:
23: * Does not support BSD terminal I/O. Anyone care to add it?
24: */
25:
26: #include <sys/types.h>
27: #include <stdio.h>
28: #include <string.h>
29: #include <ctype.h>
30: #include <signal.h>
31: #include <time.h>
32: #include <sys/ioctl.h>
33: #include <sys/termio.h>
34:
35: #include "xc-conf.h"
36:
37: /*
38: * Pick a timing routine to use, as done in Taylor UUCP.
39: */
40: #if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS || HAVE_POLL
41: #define USE_SELECT_TIMER 0
42: #else
43: #define USE_SELECT_TIMER HAVE_SELECT
44: #if USE_SELECT_TIMER
45: #include <sys/time.h>
46: #endif
47: #endif
48:
49: #if HAVE_USLEEP || HAVE_NAP || HAVE_NAPMS
50: #undef HAVE_POLL
51: #define HAVE_POLL 0
52: #endif
53:
54: #if HAVE_USLEEP || HAVE_NAP
55: #undef HAVE_NAPMS
56: #define HAVE_NAPMS 0
57: #endif
58:
59: #if HAVE_USLEEP
60: #undef HAVE_NAP
61: #define HAVE_NAP 0
62: #endif
63:
64: static int ttblind();
65: static int ttcd();
66:
67: /* script entry -- "compiled" form of dial, hangup, or login script */
68:
69: struct script {
70: struct script *next; /* pointer to next entry, or null */
71: int opcode; /* numeric opcode */
72: char *strprm; /* pointer to string param */
73: long intprm; /* integer parameter */
74: char *newstate; /* new state name */
75: };
76:
77: /* opcode definition array element -- one for each possible opcode */
78:
79: struct script_opdef {
80: char *opname;
81: int opcode; /* numeric opcode -- same as array index */
82: int prmtype; /* one of SC_NONE, SC_STR, SC_XSTR, SC_INT */
83: int newstate; /* one of SC_NONE, SC_NWST */
84: };
85:
86: /* values for opcode */
87:
88: #define SC_LABEL 0 /* "label" (state name) */
89: #define SC_CDLY 1 /* set char output delay in msec */
90: #define SC_PCHR 2 /* pause char for dial string (from P in input) */
91: #define SC_PTIM 3 /* seconds to allow for pause char */
92: #define SC_WCHR 4 /* wait char for dial string (from W in input) */
93: #define SC_WTIM 5 /* seconds to allow for wait char */
94: #define SC_ZERO 6 /* zero counter */
95: #define SC_INCR 7 /* increment counter */
96: #define SC_IFGT 8 /* change state if counter > int param */
97: #define SC_WAIT 9 /* wait for int param seconds */
98: #define SC_GOTO 10 /* unconditional change to new state */
99: #define SC_SEND 11 /* send strparam (after sprintf substitutions) */
100: #define SC_BRK 12 /* send a break */
101: #define SC_HANG 13 /* drop DTR */
102: #define SC_DIAL 14 /* send telno string (after subst PCHR & WCHR) */
103: #define SC_DTIM 15 /* time in msec per digit (for timeout calculations) */
104: /* default = 100 (one tenth second) */
105: #define SC_CTIM 16 /* additional time (in seconds) to wait for carrier */
106: /* default = 45 seconds */
107: #define SC_EXIT 17 /* script done, success */
108: #define SC_FAIL 18 /* script done, failure */
109: #define SC_LOG 19 /* write strparam to uucp.log */
110: #define SC_LOGE 20 /* write strparam to uucp.log w/error ind */
111: #define SC_DBG 21 /* write strparam to debug log if debug lvl = LGI */
112: #define SC_DBGE 22 /* write strparam to debug log if debug lvl = LGIE */
113: #define SC_DBST 23 /* 'or' intparam into debug mask */
114: #define SC_DBCL 24 /* 'bicl' intparam into debug mask */
115: #define SC_TIMO 25 /* newstate if no match in intparam secs */
116: /* (uses calculated dial time if intparam is 0) */
117: #define SC_XPCT 26 /* wait for strparam, goto _newstate if found */
118: #define SC_CARR 27 /* goto _newstate if carrier detected */
119: #define SC_FLSH 28 /* flush typeahead buffer */
120: #define SC_IFBL 29 /* change state if controller is blind w/o CD */
121: #define SC_IFBG 30 /* chg state if ctlr is blind and counter > intprm */
122: #define SC_SNDP 31 /* send parameter n */
123: #define SC_IF1P 32 /* if parameter n present */
124: #define SC_IF0P 33 /* if parameter n absent */
125: #define SC_DBOF 34 /* open debugging file */
126: #define SC_TELN 35 /* Set telno from parameter n */
127: #define SC_7BIT 36 /* Set port to 7-bit stripping */
128: #define SC_8BIT 37 /* Set port for 8-bit characters */
129: #define SC_PNON 38 /* Set port for 8-bit, no parity */
130: #define SC_PEVN 39 /* Set port for 7-bit, even parity */
131: #define SC_PODD 40 /* Set port for 7-bit, odd parity */
132: #define SC_HUPS 41 /* Change state on HUP signal */
133: #define SC_END 42 /* end of array */
134:
135: /* values for prmtype, prm2type */
136:
137: #define SC_NONE 0 /* no parameter */
138: #define SC_STR 1 /* simple string */
139: #define SC_INT 2 /* integer */
140: #define SC_NWST 3 /* new state name */
141: #define SC_XSTR 4 /* translated string */
142:
143: /* opcode definition table for dial/login/hangup scripts */
144:
145: static struct script_opdef sc_opdef[] =
146: {
147: {"label", SC_LABEL, SC_NONE, SC_NONE},
148: {"chrdly", SC_CDLY, SC_INT, SC_NONE},
149: {"pchar", SC_PCHR, SC_STR, SC_NONE},
150: {"ptime", SC_PTIM, SC_INT, SC_NONE},
151: {"wchar", SC_WCHR, SC_STR, SC_NONE},
152: {"wtime", SC_WTIM, SC_INT, SC_NONE},
153: {"zero", SC_ZERO, SC_NONE, SC_NONE},
154: {"count", SC_INCR, SC_NONE, SC_NONE},
155: {"ifgtr", SC_IFGT, SC_INT, SC_NWST},
156: {"sleep", SC_WAIT, SC_INT, SC_NONE},
157: {"goto", SC_GOTO, SC_NONE, SC_NWST},
158: {"send", SC_SEND, SC_XSTR, SC_NONE},
159: {"break", SC_BRK, SC_NONE, SC_NONE},
160: {"hangup", SC_HANG, SC_NONE, SC_NONE},
161: {"7bit", SC_7BIT, SC_NONE, SC_NONE},
162: {"8bit", SC_8BIT, SC_NONE, SC_NONE},
163: {"nopar", SC_PNON, SC_NONE, SC_NONE},
164: {"evenpar", SC_PEVN, SC_NONE, SC_NONE},
165: {"oddpar", SC_PODD, SC_NONE, SC_NONE},
166: {"telno", SC_TELN, SC_INT, SC_NONE},
167: {"dial", SC_DIAL, SC_NONE, SC_NONE},
168: {"dgttime", SC_DTIM, SC_INT, SC_NONE},
169: {"ctime", SC_CTIM, SC_INT, SC_NONE},
170: {"success", SC_EXIT, SC_NONE, SC_NONE},
171: {"failed", SC_FAIL, SC_NONE, SC_NONE},
172: {"log", SC_LOG, SC_XSTR, SC_NONE},
173: {"logerr", SC_LOGE, SC_XSTR, SC_NONE},
174: {"debug", SC_DBG, SC_XSTR, SC_NONE},
175: {"debuge", SC_DBGE, SC_XSTR, SC_NONE},
176: {"dbgset", SC_DBST, SC_INT, SC_NONE},
177: {"dbgclr", SC_DBCL, SC_INT, SC_NONE},
178: {"dbgfile", SC_DBOF, SC_XSTR, SC_NONE},
179: {"timeout", SC_TIMO, SC_INT, SC_NWST},
180: {"expect", SC_XPCT, SC_XSTR, SC_NWST},
181: {"ifcarr", SC_CARR, SC_NONE, SC_NWST},
182: {"ifhang", SC_HUPS, SC_NONE, SC_NWST},
183: {"flush", SC_FLSH, SC_NONE, SC_NONE},
184: {"ifblind", SC_IFBL, SC_NONE, SC_NWST},
185: {"ifblgtr", SC_IFBG, SC_INT, SC_NWST},
186: {"sendstr", SC_SNDP, SC_INT, SC_NONE},
187: {"ifstr", SC_IF1P, SC_INT, SC_NWST},
188: {"ifnstr", SC_IF0P, SC_INT, SC_NWST},
189: {"table end", SC_END, SC_NONE, SC_NONE}
190: };
191:
192: #define SUCCESS 0
193: #define FAIL 1
194: #define ERROR -1
195: #define MAX_SCLINE 255 /* max length of a line in a script file */
196: #define MAX_EXPCT 127 /* max length of an expect string */
197: #define CTL_DELIM " \t\n\r" /* Delimiters for tokens */
198: #define SAME 0 /* if (strcmp(a,b) == SAME) ... */
199: #define SLOP 10 /* Slop space on arrays */
200: #define MAX_STRING 200 /* Max length string to send/expect */
201:
202: #define DEBUG_LEVEL(level) \
203: (Debug & (1 << level))
204:
205: #define DB_LOG 0 /* error messages and a copy of the LOGFILE output */
206: #define DB_LGIE 1 /* dial,login,init trace -- errors only */
207: #define DB_LGI 2 /* dial,login,init trace -- nonerrors (incl chr I/O) */
208: #define DB_LGII 3 /* script processing internals */
209:
210: #define TRUE 1
211: #define FALSE 0
212:
213: #define NONE 0
214: #define EVEN 1
215: #define ODD 2
216:
217: #define logit(m, p1) fprintf(stderr, "%s %s\n", m, p1)
218:
219: static char **paramv; /* Parameter vector */
220: static int paramc; /* Parameter count */
221: static char telno[64]; /* Telephone number w/meta-chars */
222: static int Debug;
223: static int fShangup = FALSE; /* TRUE if HUP signal received */
224: static FILE *dbf = NULL;
225: static struct termio old, new;
226:
227: extern int usignal();
228: extern int uhup();
229:
230: static struct siglist
231: {
232: int signal;
233: int (*o_catcher) ();
234: int (*n_catcher) ();
235: } sigtbl[] = {
236: { SIGHUP, NULL, uhup },
237: { SIGINT, NULL, usignal },
238: { SIGIOT, NULL, usignal },
239: { SIGQUIT, NULL, usignal },
240: { SIGTERM, NULL, usignal },
241: { SIGALRM, NULL, usignal },
242: { 0, NULL, NULL } /* Table end */
243: };
244:
245: extern struct script *read_script();
246: extern void msleep();
247: extern char xgetc();
248: extern void charlog();
249: extern void setup_tty();
250: extern void restore_tty();
251: extern void ttoslow();
252: extern void ttflui();
253: extern void tthang();
254: extern void ttbreak();
255: extern void tt7bit();
256: extern void ttpar();
257: extern void DEBUG();
258:
259: extern void *malloc();
260:
261:
262: /*
263: * **********************************
264: * * BEGIN EXECUTION - MAIN PROGRAM *
265: * **********************************
266: *
267: * This program is called by Taylor UUCP with a list of
268: * arguments in argc/argv, and stdin/stdout mapped to the
269: * tty device, and stderr mapped to the Taylor logfile, where
270: * anything written to stdout will be logged as an error.
271: *
272: */
273: int main(argc, argv)
274: int argc;
275: char *argv[];
276: {
277: int i, stat;
278: FILE *sf;
279: char sfname[256];
280: struct script *script;
281: struct siglist *sigs;
282:
283: /*
284: * The following is needed because my cpp does not have the
285: * #error directive...
286: */
287: #if ! HAVE_SELECT
288: no_select_sorry(); /* Sad way to fail make */
289: #endif
290:
291: paramv = &argv[2]; /* Parameters start at 2nd arg */
292: paramc = argc - 2; /* Number of live parameters */
293:
294: telno[0] = '\0';
295:
296: if (argc < 2)
297: {
298: fprintf(stderr, "%s: no script file supplied\n", argv[0]);
299: exit(FAIL);
300: }
301:
302: /*
303: * If the script file argument begins with '/', then we assume
304: * it is an absolute pathname, otherwise, we prepend the
305: * SCRIPT_DIR path.
306: */
307: *sfname = '\0'; /* Empty name string */
308: if(argv[1][0] != '/') /* If relative path */
309: strcat(sfname, SCRIPT_DIR); /* Prepend the default dir. */
310: strcat(sfname, argv[1]); /* Add the script file name */
311:
312: /*
313: * Now open the script file.
314: */
315: if ((sf = fopen(sfname, "r")) == NULL)
316: {
317: fprintf(stderr, "%s: Failed to open script %s\n", argv[0], sfname);
318: perror(" ");
319: exit(FAIL);
320: }
321:
322: /*
323: * COMPILE SCRIPT
324: */
325: if ((script = read_script(sf)) == NULL)
326: {
327: fprintf(stderr, "%s: script error in \"%s\"\n", argv[0], argv[1]);
328: exit(FAIL);
329: }
330:
331: /*
332: * Set up a signal catcher so the line can be returned to
333: * it's current state if something nasty happens.
334: */
335: sigs = &sigtbl[0];
336: while(sigs->signal)
337: {
338: sigs->o_catcher = (int (*) ())signal(sigs->signal, sigs->n_catcher);
339: sigs += 1;
340: }
341:
342: /*
343: * Save current tty settings, then set up raw, single
344: * character input processing, with 7-bit stripping.
345: */
346: setup_tty();
347:
348: /*
349: * EXECUTE SCRIPT
350: */
351: if ((stat = do_script(script)) != SUCCESS)
352: fprintf(stderr, "%s: script %s failed.\n", argv[0], argv[1]);
353:
354: /*
355: * Clean up and exit.
356: */
357: restore_tty();
358: #ifdef FIXSIGS
359: sigs = &sigtbl[0];
360: while(sigs->signal)
361: if(sigs->o_catcher != -1)
362: signal(sigs->signal, sigs->o_catcher);
363: #endif
364: exit(stat);
365: }
366:
367: /*
368: * deal_script - deallocate a script and all strings it points to
369: */
370: int deal_script(loc)
371: struct script *loc;
372: {
373: /*
374: * If pointer is null, just exit
375: */
376: if (loc == (struct script *)NULL)
377: return SUCCESS;
378:
379: /*
380: * Deallocate the rest of the script
381: */
382: deal_script(loc->next);
383:
384: /*
385: * Deallocate the string parameter, if any
386: */
387: if (loc->strprm != (char *)NULL)
388: free(loc->strprm);
389:
390: /*
391: * Deallocate the new state name parameter, if any
392: */
393: if (loc->newstate != (char *)NULL)
394: free(loc->newstate);
395:
396: /*
397: * Deallocate this entry
398: */
399: free(loc);
400:
401: return SUCCESS;
402: }
403:
404:
405: /*
406: * read_script
407: *
408: * Read & compile a script, return pointer to first entry, or null if bad
409: */
410: struct script *read_script(fd)
411: FILE *fd;
412: {
413: struct script *this = NULL;
414: struct script *prev = NULL;
415: struct script *first = NULL;
416: long len, i;
417: char inpline[MAX_SCLINE];
418: char inpcopy[MAX_SCLINE];
419: char *c, *cln, *opc, *cp;
420:
421: /*
422: * MAIN COMPILATION LOOP
423: */
424: while ((c = fgets(inpline, (sizeof inpline - 1), fd)) != (char *)NULL)
425: {
426: /*
427: * Skip comments and blank lines
428: */
429: if (*c == '#' || *c == '\n')
430: continue;
431:
432: /*
433: * Get rid of the trailing newline, and copy the string
434: */
435: inpline[strlen(inpline)-1] = '\0';
436: strcpy(inpcopy, inpline);
437:
438: /*
439: * Look for text starting in the first col (a label)
440: */
441: if ((!isspace(inpline[0])) &&
442: (cln = strchr (inpline, ':')) != (char *)NULL) {
443: this = (struct script *)malloc (sizeof (struct script));
444: if (prev != (struct script *)NULL)
445: prev->next = this;
446: prev = this;
447: if (first == (struct script *)NULL)
448: first = this;
449: this->next = (struct script *)NULL;
450: this->opcode = SC_LABEL;
451: len = cln - c;
452: this->strprm = (char *)malloc(len+1);
453: strncpy(this->strprm, c, len);
454: (this->strprm)[len] = '\0';
455: this->intprm = 0;
456: this->newstate = (char *)NULL;
457: c = cln + 1;
458: }
459:
460: /*
461: * Now handle the opcode. Fold it to lower case.
462: */
463: opc = strtok(c, CTL_DELIM);
464: if (opc == (char *)NULL) /* If no opcode... */
465: continue; /* ...read the next line */
466: cp = opc;
467: while(*cp)
468: tolower(*cp++);
469:
470: /*
471: * If we have an opcode but we haven't seen anything
472: * else (like a label) yet, i.e., this is the first
473: * entry, and there was no label. We need to
474: * cobble up a label so that read_script is happy
475: */
476: if (first == (struct script *)NULL)
477: {
478: this = (struct script *)malloc (sizeof (struct script));
479: prev = this;
480: first = this;
481: this->next = (struct script *)NULL;
482: this->opcode = SC_LABEL;
483: this->strprm = (char *)malloc(2);
484: strcpy(this->strprm, ":");
485: this->intprm = 0;
486: this->newstate = (char *)NULL;
487: }
488:
489: /*
490: * Find opcode - ndex through the opcode definition table
491: */
492: for (i=1; sc_opdef[i].opcode != SC_END; i++)
493: if (strcmp(opc, sc_opdef[i].opname) == SAME)
494: break;
495: if ((sc_opdef[i].opcode) == SC_END)
496: {
497: logit ("Bad opcode in script", opc);
498: deal_script(first);
499: return (struct script *)NULL;
500: }
501:
502: /*
503: * Found opcode. Allocate a new command node and initialize
504: */
505: this = (struct script *)malloc(sizeof (struct script));
506: prev->next = this;
507: prev = this;
508: this->next = (struct script *)NULL;
509: this->opcode = sc_opdef[i].opcode;
510: this->strprm = (char *)NULL;
511: this->intprm = 0;
512: this->newstate = (char *)NULL;
513:
514: /*
515: * Pick up new state parameter, if any
516: */
517: if (sc_opdef[i].newstate == SC_NWST)
518: {
519: c = strtok((char *)NULL, CTL_DELIM);
520: if (c == (char *)NULL)
521: {
522: logit("Missing new state", opc);
523: deal_script(first);
524: return (struct script *)NULL;
525: }
526: else
527: {
528: this->newstate = (char *)malloc(strlen(c)+1);
529: strcpy(this->newstate, c);
530: }
531: }
532:
533: /*
534: * Pick up the string or integer parameter. Handle missing
535: * parameter gracefully.
536: */
537: switch (sc_opdef[i].prmtype)
538: {
539: /*
540: * INT parameter - convert and store in node
541: */
542: case SC_INT:
543: c = strtok((char *)NULL, CTL_DELIM);
544: if (c == (char *)NULL)
545: {
546: logit("Missing script param", opc);
547: deal_script(first);
548: return (struct script *)NULL;
549: }
550: /*
551: * If this is the parameter to DBST or DBCL, force
552: * base-10 conversion, else convert per parameter.
553: */
554: if (sc_opdef[i].opcode == SC_DBST ||
555: sc_opdef[i].opcode == SC_DBCL)
556: this->intprm = strtol(c, (char **)NULL, 0);
557: else
558: this->intprm = strtol(c, (char **)NULL, 10);
559: break;
560:
561: /*
562: * STR/XSTR strings.
563: */
564: case SC_STR:
565: case SC_XSTR:
566: c = strtok((char *)NULL, CTL_DELIM);
567: if (c == (char *)NULL)
568: {
569: logit("Missing script param", opc);
570: deal_script(first);
571: return (struct script *)NULL;
572: }
573: /*
574: * For XSTR opcode, use c to find out where
575: * the string param begins in the copy of the
576: * input line, and pick up all that's left of
577: * the line (to allow imbedded blanks, etc.).
578: */
579: if (sc_opdef[i].prmtype == SC_XSTR)
580: c = &inpcopy[0] + (c - &inpline[0]);
581:
582: /*
583: * Allocate a buffer for the string parameter
584: */
585: this->strprm = (char *)malloc(strlen(c)+1);
586:
587: /*
588: * For XSTR, Translate the string and store its
589: * length. Note that, after escape sequences are
590: * compressed, the resulting string may well be a
591: * few bytes shorter than the input string (whose
592: * length was the basis for the malloc above),
593: * but it will never be longer.
594: */
595: if (sc_opdef[i].prmtype == SC_XSTR)
596: {
597: this->intprm = xlat_str(this->strprm, c);
598: this->strprm[this->intprm] = '\0';
599: }
600: else
601: strcpy(this->strprm, c);
602: break;
603:
604: }
605: }
606:
607: /*
608: * EOF
609: */
610: return first;
611: }
612:
613:
614: /*
615: * xlat_str
616: *
617: * Translate embedded escape characters in a "send" or "expect" string.
618: *
619: * Called by read_script(), above.
620: *
621: * Returns the actual length of the resulting string. Note that imbedded
622: * nulls (specified by \000 in the input) ARE allowed in the result.
623: */
624: xlat_str(out, in)
625: char *out, *in;
626: {
627: register int i = 0, j = 0;
628: int byte, k;
629:
630: while (in[i])
631: {
632: if (in[i] != '\\')
633: {
634: out[j++] = in[i++];
635: }
636: else
637: {
638: switch (in[++i])
639: {
640: case 'd': /* EOT */
641: out[j++] = 0x04;
642: break;
643: case 'N': /* null */
644: out[j++] = 0x00;
645: break;
646: case 'n': /* line feed */
647: out[j++] = 0x0a;
648: break;
649: case 'r': /* carriage return */
650: out[j++] = 0x0d;
651: break;
652: case 's': /* space */
653: out[j++] = ' ';
654: break;
655: case 't': /* tab */
656: out[j++] = '\t';
657: break;
658: case '-': /* hyphen */
659: out[j++] = '-';
660: break;
661: case '\\': /* back slash */
662: out[j++] = '\\';
663: break;
664: case '0': /* '\nnn' format */
665: case '1':
666: case '2':
667: case '3':
668: case '4':
669: case '5':
670: case '6':
671: case '7':
672: byte = in[i] - '0';
673: k = 0;
674:
675: while (3 > ++k)
676: if ((in[i+1] < '0') || (in[i+1] > '7'))
677: break;
678: else
679: {
680: byte = (byte<<3) + in[i+1] - '0';
681: ++i;
682: }
683: out[j++] = byte;
684: break;
685: default: /* don't know so skip it */
686: break;
687: }
688: ++i;
689: }
690: }
691: return j;
692: }
693:
694:
695: /* find a state within a script */
696:
697: struct script *
698: find_state(begin, newstate)
699: struct script *begin;
700: char *newstate;
701: {
702: struct script *here;
703:
704: for (here=begin; here != (struct script *)NULL; here=here->next) {
705: if (here->opcode == SC_LABEL &&
706: strcmp(here->strprm, newstate) == SAME)
707: return here;
708: }
709: return (struct script *)NULL;
710: }
711:
712:
713: /*
714: * do_script() - execute a script
715: */
716: int do_script(begin)
717: struct script *begin;
718: {
719: struct script *curstate, *newstate, *curscr;
720: int dbgsave;
721: char tempstr[MAX_SCLINE];
722: char dfname[256];
723: char *c, chr;
724: int prmlen;
725: int dbfd;
726:
727: time_t sc_carrtime = 45000; /* time to wf carr after dial */
728: time_t sc_chrdly = 100; /* delay time for ttoslow */
729: time_t sc_ptime = 2000; /* time to allow for pause char */
730: time_t sc_wtime = 10000; /* time to allow for wait char */
731: time_t sc_dtime = 100; /* time to allow for each digit */
732: time_t sc_dtmo; /* total time to dial number */
733: int sc_counter; /* random counter */
734: char sc_pchar = ','; /* modem pause character */
735: char sc_wchar = 'W'; /* modem wait-for-dialtone character */
736: time_t sc_begwait; /* time at beg of wait */
737: time_t sc_secs; /* timeout period */
738:
739: int expcnt;
740: int expin;
741: static char expbuf[MAX_EXPCT];
742:
743: dbgsave = Debug;
744: curstate = begin;
745:
746: if (curstate == (struct script *)NULL)
747: return SUCCESS;
748:
749: _newstate:
750: /*
751: * do all of curstate's actions. Enter with curstate pointing
752: * to a label entry
753: */
754: expin = 0;
755:
756: for (curscr = curstate->next; /* point to 1st scr after label */
757: (curscr != (struct script *)NULL) && /* do until end of scr */
758: (curscr->opcode != SC_LABEL); /* or next label */
759: curscr = curscr->next)
760: {
761: expcnt = 0;
762: switch (curscr->opcode)
763: {
764: case SC_LABEL:
765: logit("Script proc err", curstate->strprm);
766: return FAIL;
767:
768: case SC_FLSH:
769: DEBUG(DB_LGII, "Flushing typeahead buffer\n", 0);
770: ttflui();
771: break;
772:
773: case SC_CDLY:
774: sc_chrdly = curscr->intprm;
775: DEBUG(DB_LGII, "Set chrdly to %d\n", sc_chrdly);
776: break;
777:
778: case SC_PCHR:
779: sc_pchar = *(curscr->strprm);
780: DEBUG(DB_LGII, "Set pause char to %c\n", sc_pchar);
781: break;
782:
783: case SC_PTIM:
784: sc_ptime = curscr->intprm;
785: DEBUG(DB_LGII, "Set pause time to %d\n", sc_ptime);
786: break;
787:
788: case SC_WCHR:
789: sc_wchar = *(curscr->strprm);
790: DEBUG(DB_LGII, "Set wait char to %c\n", sc_wchar);
791: break;
792:
793: case SC_WTIM:
794: sc_wtime = curscr->intprm;
795: DEBUG(DB_LGII, "Set wait time to %d\n", sc_wtime);
796: break;
797:
798: case SC_ZERO:
799: sc_counter = 0;
800: DEBUG(DB_LGII, "Set counter to %d\n", sc_counter);
801: break;
802:
803: case SC_INCR:
804: sc_counter++;
805: DEBUG(DB_LGII, "Incr counter to %d\n", sc_counter);
806: break;
807:
808: case SC_WAIT:
809: DEBUG(DB_LGII, "Sleeping %d tenth-secs\n", curscr->intprm);
810: msleep(curscr->intprm);
811: break;
812:
813: case SC_DTIM:
814: sc_dtime = curscr->intprm;
815: DEBUG(DB_LGII, "Digit time is %d\n", sc_dtime);
816: break;
817:
818: case SC_CTIM:
819: sc_carrtime = curscr->intprm;
820: DEBUG(DB_LGII, "Carrier time is %d\n", sc_carrtime);
821: break;
822:
823: case SC_EXIT:
824: Debug = dbgsave;
825: DEBUG(DB_LGI, "Script ended successfully\n", 0);
826: return SUCCESS;
827:
828: case SC_FAIL:
829: Debug = dbgsave;
830: if (DEBUG_LEVEL(DB_LGI) && dbf != NULL)
831: fprintf(dbf, "Script failed\n");
832: else if (expin)
833: charlog(expbuf, expin, DB_LOG,
834: "Script failed. Last received data");
835: return FAIL;
836:
837: case SC_LOG:
838: logit(curscr->strprm, "");
839: break;
840:
841: case SC_LOGE:
842: logit("ERROR: ", curscr->strprm);
843: break;
844:
845: case SC_DBOF:
846: /*
847: * If the debug file name does not begin with "/", then
848: * we prepend the LOG_DIR to the string. Then CREATE the
849: * file. This WIPES OUT previous logs.
850: */
851: *dfname = '\0'; /* Zero name string */
852: if(curscr->strprm[0] != '/')
853: strcat(dfname, LOG_DIR); /* Prepend default directory */
854: strcat(dfname, curscr->strprm); /* Add given string */
855: DEBUG(DB_LGII, "Open debug file %s\n", dfname);
856: if ((dbfd = creat (dfname, 0600)) <= 0)
857: {
858: logit("Failed to create debug log %s", dfname);
859: perror("");
860: return FAIL;
861: }
862: if ((dbf = fdopen(dbfd, "w")) == NULL)
863: {
864: logit("Failed to open debug log fildes.", "");
865: perror("");
866: return FAIL;
867: }
868: break;
869:
870: case SC_DBG:
871: DEBUG(DB_LGI, "<%s>\n", curscr->strprm);
872: break;
873:
874: case SC_DBGE:
875: DEBUG(DB_LGIE, "ERROR: <%s>\n", curscr->strprm);
876: break;
877:
878: case SC_DBST:
879: Debug |= curscr->intprm;
880: DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug);
881: break;
882:
883: case SC_DBCL:
884: Debug &= ~(curscr->intprm);
885: DEBUG(DB_LGII, "Debug mask set to %04o (octal)\n", Debug);
886: break;
887:
888: case SC_BRK:
889: DEBUG(DB_LGI, "Sending break\n", 0);
890: ttbreak();
891: break;
892:
893: case SC_HANG:
894: DEBUG(DB_LGI, "Dropping DTR\n", 0);
895: tthang();
896: break;
897:
898: case SC_7BIT:
899: DEBUG(DB_LGI, "Enabling 7-bit stripping\n", 0);
900: tt7bit(TRUE);
901: break;
902:
903: case SC_8BIT:
904: DEBUG(DB_LGI, "Disabling 7-bit stripping\n", 0);
905: tt7bit(FALSE);
906: break;
907:
908: case SC_PNON:
909: DEBUG(DB_LGI, "Setting 8-bit, no parity\n", 0);
910: ttpar(NONE);
911: break;
912:
913: case SC_PEVN:
914: DEBUG(DB_LGI, "Setting 7-bit, even parity\n", 0);
915: ttpar(EVEN);
916: break;
917:
918: case SC_PODD:
919: DEBUG(DB_LGI, "Setting 7-bit, odd parity\n", 0);
920: ttpar(ODD);
921: break;
922:
923: case SC_IFBL:
924: if (ttblind())
925: {
926: DEBUG(DB_LGI, "Blind mux,\n", 0);
927: goto _chgstate;
928: }
929: break;
930:
931: case SC_IFBG:
932: if (ttblind() && sc_counter > curscr->intprm)
933: {
934: DEBUG(DB_LGI, "Blind mux & ctr > %d\n",
935: curscr->intprm);
936: goto _chgstate;
937: }
938: break;
939:
940: case SC_IFGT:
941: if (sc_counter > curscr->intprm)
942: {
943: DEBUG(DB_LGI, "Counter > %d\n", curscr->intprm);
944: goto _chgstate;
945: }
946: break;
947:
948: case SC_GOTO:
949: _chgstate:
950: DEBUG(DB_LGI, "Changing to state %s\n",
951: curscr->newstate);
952: curstate = find_state(begin, curscr->newstate);
953: if (curstate == NULL)
954: {
955: logit("New state not found",
956: curscr->newstate);
957: return FAIL;
958: }
959: goto _newstate;
960:
961: case SC_SEND:
962: ttoslow(curscr->strprm, curscr->intprm, sc_chrdly);
963: break;
964:
965: case SC_TELN:
966: if (curscr->intprm > paramc - 1)
967: {
968: sprintf(tempstr, "telno - param #%d", curscr->intprm);
969: logit(tempstr, " not present");
970: return FAIL;
971: }
972: strcpy(telno, paramv[curscr->intprm]);
973: DEBUG(DB_LGII, "telno set to %s\n", telno);
974: break;
975:
976: case SC_SNDP:
977: if (curscr->intprm > paramc - 1)
978: {
979: sprintf(tempstr, "sendstr - param #%d", curscr->intprm);
980: logit(tempstr, " not present");
981: return FAIL;
982: }
983: prmlen = xlat_str(tempstr, paramv[curscr->intprm]);
984: ttoslow(tempstr, prmlen, sc_chrdly);
985: break;
986:
987: case SC_IF1P:
988: if (curscr->intprm < paramc)
989: goto _chgstate;
990: break;
991:
992: case SC_IF0P:
993: if (curscr->intprm >= paramc)
994: goto _chgstate;
995: break;
996:
997: case SC_DIAL:
998: if(telno[0] == '\0')
999: {
1000: logit("telno not set", "");
1001: return(FAIL);
1002: }
1003: /*
1004: * Compute and set a default timeout for the 'timeout'
1005: * command. Some parameters in this computation may be
1006: * changed by the script. See the man page xchat(8) for
1007: * details.
1008: */
1009: sc_dtmo = (sc_dtime+sc_chrdly)*strlen(telno)
1010: + sc_carrtime;
1011: c=strcpy(tempstr, telno);
1012: for (; *c!='\0'; c++)
1013: {
1014: if (*c == 'W')
1015: {
1016: *c = sc_wchar;
1017: sc_dtmo += sc_wtime;
1018: }
1019: else if (*c == 'P')
1020: {
1021: *c = sc_pchar;
1022: sc_dtmo += sc_ptime;
1023: }
1024: }
1025: DEBUG(DB_LGI, "Dialing, default timeout is %d millisecs\n", sc_dtmo);
1026: ttoslow(tempstr, 0, sc_chrdly);
1027: break;
1028:
1029: case SC_TIMO: /* these are "expects", don't bother */
1030: case SC_XPCT: /* with them yet, other than noting that */
1031: case SC_CARR: /* they exist */
1032: expcnt++;
1033: break;
1034: }
1035:
1036: }
1037:
1038: /* we've done the current state's actions, now do its expects, if any */
1039:
1040: if (expcnt == 0)
1041: {
1042: if (curscr != (struct script *)NULL &&
1043: (curscr->opcode == SC_LABEL))
1044: {
1045: curstate = curscr;
1046: DEBUG(DB_LGI, "Fell through to state %s\n",
1047: curstate->strprm);
1048: goto _newstate;
1049: }
1050: else
1051: {
1052: logit("No way out of state", curstate->strprm);
1053: return FAIL;
1054: }
1055: }
1056:
1057: time(&sc_begwait); /* log time at beg of expect */
1058: DEBUG(DB_LGI, "Doing expects for state %s\n", curstate->strprm);
1059: charlog((char *)NULL, 0, DB_LGI, "Received");
1060:
1061: while (1)
1062: {
1063: chr = xgetc(1); /* Returns upon char input or 1 sec. tmo */
1064:
1065: charlog(&chr, 1, DB_LGI, (char *)NULL);
1066:
1067: if (chr != EOF)
1068: {
1069: if (expin < MAX_EXPCT)
1070: {
1071: expbuf[expin++] = chr & 0x7f;
1072: }
1073: else
1074: {
1075: strncpy(expbuf, &expbuf[1], MAX_EXPCT-1);
1076: expbuf[MAX_EXPCT-1] = chr & 0x7f;
1077: }
1078: }
1079:
1080: /* for each entry in the current state... */
1081:
1082: for (curscr = curstate->next;
1083: (curscr != (struct script *)NULL) &&
1084: (curscr->opcode != SC_LABEL);
1085: curscr = curscr->next)
1086: {
1087:
1088: switch (curscr->opcode)
1089: {
1090: case SC_TIMO:
1091: sc_secs = curscr->intprm;
1092: if (sc_secs == 0)
1093: sc_secs = sc_dtmo;
1094: sc_secs /= 1000;
1095: if (time(NULL)-sc_begwait > sc_secs)
1096: {
1097: DEBUG(DB_LGI,
1098: "\nTimed out (%d secs)\n", sc_secs);
1099: goto _chgstate;
1100: }
1101: break;
1102:
1103: case SC_CARR:
1104: if (ttcd())
1105: {
1106: DEBUG(DB_LGI, "\nGot carrier\n", 0);
1107: goto _chgstate;
1108: }
1109: break;
1110:
1111: case SC_HUPS:
1112: if (fShangup)
1113: {
1114: DEBUG(DB_LGI, "\nGot data set hangup\n", 0);
1115: goto _chgstate;
1116: }
1117: break;
1118:
1119: case SC_XPCT:
1120: if ((expin >= curscr->intprm) &&
1121: (strncmp(curscr->strprm,
1122: &expbuf[expin - curscr->intprm],
1123: curscr->intprm) == SAME))
1124: {
1125: charlog(curscr->strprm, curscr->intprm,
1126: DB_LGI, "Matched");
1127: goto _chgstate;
1128: }
1129: break;
1130:
1131: }
1132: }
1133: }
1134: }
1135:
1136: /*
1137: * SIGNAL HANDLERS
1138: */
1139:
1140: /*
1141: * usignal - generic signal catcher
1142: */
1143: static int usignal(isig)
1144: int isig;
1145: {
1146: DEBUG(DB_LOG, "Caught signal %d. Exiting...\n", isig);
1147: restore_tty();
1148: exit(FAIL);
1149: }
1150:
1151: /*
1152: * uhup - HUP catcher
1153: */
1154: static int uhup(isig)
1155: int isig;
1156: {
1157: DEBUG(DB_LOG, "Data set hangup.\n");
1158: fShangup = TRUE;
1159: }
1160:
1161: /*
1162: * TERMINAL I/O ROUTINES
1163: */
1164:
1165: /*
1166: * xgetc - get a character with timeout
1167: *
1168: * Assumes that stdin is opened on a terminal or TCP socket
1169: * with O_NONBLOCK.
1170: */
1171: static char xgetc(tmo)
1172: int tmo; /* Timeout, seconds */
1173: {
1174: char c;
1175: struct timeval s;
1176: int f = 1; /* Select on stdin */
1177: int result;
1178:
1179: if(read(0, &c, 1) <= 0) /* If no data available */
1180: {
1181: s.tv_sec = (long)tmo;
1182: s.tv_usec = 0L;
1183: if(select (1, &f, (int *) NULL, &f, &s) == 1)
1184: read(0, &c, 1);
1185: else
1186: c = '\377';
1187: }
1188:
1189: return(c);
1190: }
1191:
1192: /*
1193: * Pause for an interval in milliseconds
1194: */
1195: void msleep(msec)
1196: long msec;
1197: {
1198:
1199: #if HAVE_USLEEP
1200: if(msec == 0) /* Skip all of this if delay = 0 */
1201: return;
1202: usleep (msec * (long)1000);
1203: #endif /* HAVE_USLEEP */
1204:
1205: #if HAVE_NAPMS
1206: if(msec == 0) /* Skip all of this if delay = 0 */
1207: return;
1208: napms (msec);
1209: #endif /* HAVE_NAPMS */
1210:
1211: #if HAVE_NAP
1212: if(msec == 0) /* Skip all of this if delay = 0 */
1213: return;
1214: nap (msec);
1215: #endif /* HAVE_NAP */
1216:
1217: #if HAVE_POLL
1218: struct pollfd sdummy;
1219:
1220: if(msec == 0)
1221: return;
1222: /*
1223: * We need to pass an unused pollfd structure because poll checks
1224: * the address before checking the number of elements.
1225: */
1226: poll (&sdummy, 0, msec);
1227: #endif /* HAVE_POLL */
1228:
1229: #if USE_SELECT_TIMER
1230: struct timeval s;
1231:
1232: if(msec == 0)
1233: return;
1234: s.tv_sec = msec / 1000L;
1235: s.tv_usec = (msec % 1000L) * 1000L;
1236: select (0, (int *) NULL, (int *) NULL, (int *) NULL, &s);
1237: #endif /* USE_SELECT_TIMER */
1238:
1239: #if ! HAVE_NAPMS && ! HAVE_NAP && ! HAVE_USLEEP && \
1240: ! HAVE_POLL && ! USE_SELECT_TIMER
1241: if(msec == 0)
1242: return;
1243: sleep (1); /* Sleep for a whole second (UGH!) */
1244: #endif /* HAVE_ and USE_ nothing */
1245: }
1246:
1247: /*
1248: * Debugging output
1249: */
1250: static void DEBUG(level, msg1, msg2)
1251: int level;
1252: char *msg1, *msg2;
1253: {
1254: if ((dbf != NULL) && DEBUG_LEVEL(level))
1255: fprintf(dbf, msg1, msg2);
1256: }
1257:
1258: /*
1259: * charlog - log a string of characters
1260: *
1261: * SPECIAL CASE: msg=NULL, len=1 and msg[0]='\377' gets logged
1262: * when read does its 1 sec. timeout. Log "<1 sec.>"
1263: * so user can see elapsed time
1264: */
1265: static void charlog(buf, len, mask, msg)
1266: char *buf;
1267: int len, mask;
1268: char *msg;
1269: {
1270: char tbuf[256];
1271:
1272: if (DEBUG_LEVEL(mask) && dbf != NULL)
1273: {
1274: if(msg == (char *)NULL)
1275: msg = "";
1276: strncpy(tbuf, buf, len);
1277: tbuf[len] = '\0';
1278: if(len == 1 && tbuf[0] == '\377')
1279: strcpy(tbuf, "<1 sec.>");
1280: fprintf(dbf, "%s %s\n", msg, tbuf);
1281: }
1282: }
1283:
1284: /*
1285: * setup_tty()
1286: *
1287: * Save current tty settings, then set up raw, single
1288: * character input processing, with 7-bit stripping.
1289: */
1290: static void setup_tty()
1291: {
1292: register int i;
1293:
1294: ioctl(0, TCGETA, &old);
1295:
1296: new = old;
1297:
1298: for(i = 0; i < 7; i++)
1299: new.c_cc[i] = '\0';
1300: new.c_cc[VMIN] = 0; /* MIN = 0, use requested count */
1301: new.c_cc[VTIME] = 10; /* TIME = 1 sec. */
1302: new.c_iflag = ISTRIP; /* Raw mode, 7-bit stripping */
1303: new.c_lflag = 0; /* No special line discipline */
1304:
1305: ioctl(0, TCSETA, &new);
1306: }
1307:
1308: /*
1309: * restore_tty() - restore signal handlers and tty modes on exit.
1310: */
1311: static void restore_tty(sig)
1312: int sig;
1313: {
1314: ioctl(0, TCSETA, &old);
1315: return;
1316: }
1317:
1318: /*
1319: * ttoslow() - Send characters with pacing delays
1320: */
1321: static void ttoslow(s, len, delay)
1322: char *s;
1323: int len;
1324: time_t delay;
1325: {
1326: int i;
1327:
1328: if (len == 0)
1329: len = strlen(s);
1330:
1331: charlog (s, len, DB_LGI, "Sending slowly");
1332:
1333: for (i = 0; i < len; i++, s++)
1334: {
1335: write(1, s, 1);
1336: msleep(delay);
1337: }
1338: }
1339:
1340: /*
1341: * ttflui - flush input buffer
1342: */
1343: static void ttflui()
1344: {
1345: if(isatty(0))
1346: (void) ioctl ( 0, TCFLSH, 0);
1347: }
1348:
1349: /*
1350: * ttcd - Test if carrier is present
1351: *
1352: * NOT IMPLEMENTED. I don't know how!!!
1353: */
1354: static int ttcd()
1355: {
1356: return TRUE;
1357: }
1358:
1359: /*
1360: * tthang - Force DTR low for 1-2 sec.
1361: */
1362: static void tthang()
1363: {
1364: if(!isatty())
1365: return;
1366:
1367: #ifdef TCCLRDTR
1368: (void) ioctl (1, TCCLRDTR, 0);
1369: sleep (2);
1370: (void) ioctl (1, TCSETDTR, 0);
1371: #endif
1372:
1373: return;
1374: }
1375:
1376: /*
1377: * ttbreak - Send a "break" on the line
1378: */
1379: static void ttbreak()
1380: {
1381: (void) ioctl (1, TCSBRK, 0);
1382: }
1383:
1384: /*
1385: * ttblind - return TRUE if tty is "blind"
1386: *
1387: * NOT IMPLEMENTED - Don't know how!!!
1388: */
1389: static int ttblind()
1390: {
1391: return FALSE;
1392: }
1393:
1394: /*
1395: * tt7bit - enable/disable 7-bit stripping on line
1396: */
1397: static void tt7bit(enable)
1398: int enable;
1399: {
1400: if(enable)
1401: new.c_iflag |= ISTRIP;
1402: else
1403: new.c_iflag &= ~ISTRIP;
1404:
1405: ioctl(0, TCSETA, &new);
1406: }
1407:
1408: /*
1409: * ttpar - Set parity mode on line. Ignore parity errors on input.
1410: */
1411: static void ttpar(mode)
1412: int mode;
1413: {
1414: switch(mode)
1415: {
1416: case NONE:
1417: new.c_iflag &= ~(INPCK | IGNPAR);
1418: new.c_cflag &= ~(CSIZE | PARENB | PARODD);
1419: new.c_cflag |= CS8;
1420: break;
1421:
1422: case EVEN:
1423: new.c_iflag |= (INPCK | IGNPAR);
1424: new.c_cflag &= ~(CSIZE | PARODD);
1425: new.c_cflag |= (CS7 | PARENB);
1426:
1427: break;
1428:
1429: case ODD:
1430: new.c_iflag |= (INPCK | IGNPAR);
1431: new.c_cflag &= ~(CSIZE);
1432: new.c_cflag |= (CS7 | PARENB | PARODD);
1433: break;
1434: }
1435:
1436: ioctl(0, TCSETA, &new);
1437: }
1438:
1439:
1440:
1441:
1442:
1443:
1444:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.