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