1 /* 2 * Chat -- a program for automatic session establishment (i.e. dial 3 * the phone and log in). 4 * 5 * Standard termination codes: 6 * 0 - successful completion of the script 7 * 1 - invalid argument, expect string too large, etc. 8 * 2 - error on an I/O operation or fatal error condition. 9 * 3 - timeout waiting for a simple string. 10 * 4 - the first string declared as "ABORT" 11 * 5 - the second string declared as "ABORT" 12 * 6 - ... and so on for successive ABORT strings. 13 * 14 * This software is in the public domain. 15 * 16 * ----------------- 17 * added -T and -U option and \T and \U substitution to pass a phone 18 * number into chat script. Two are needed for some ISDN TA applications. 19 * Keith Dart <kdart@cisco.com> 20 * 21 * 22 * Added SAY keyword to send output to stderr. 23 * This allows to turn ECHO OFF and to output specific, user selected, 24 * text to give progress messages. This best works when stderr 25 * exists (i.e.: pppd in nodetach mode). 26 * 27 * Added HANGUP directives to allow for us to be called 28 * back. When HANGUP is set to NO, chat will not hangup at HUP signal. 29 * We rely on timeouts in that case. 30 * 31 * Added CLR_ABORT to clear previously set ABORT string. This has been 32 * dictated by the HANGUP above as "NO CARRIER" (for example) must be 33 * an ABORT condition until we know the other host is going to close 34 * the connection for call back. As soon as we have completed the 35 * first stage of the call back sequence, "NO CARRIER" is a valid, non 36 * fatal string. As soon as we got called back (probably get "CONNECT"), 37 * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. 38 * Note that CLR_ABORT packs the abort_strings[] array so that we do not 39 * have unused entries not being reclaimed. 40 * 41 * In the same vein as above, added CLR_REPORT keyword. 42 * 43 * Allow for comments. Line starting with '#' are comments and are 44 * ignored. If a '#' is to be expected as the first character, the 45 * expect string must be quoted. 46 * 47 * 48 * Francis Demierre <Francis@SwissMail.Com> 49 * Thu May 15 17:15:40 MET DST 1997 50 * 51 * 52 * Added -r "report file" switch & REPORT keyword. 53 * Robert Geer <bgeer@xmission.com> 54 * 55 * Added -s "use stderr" and -S "don't use syslog" switches. 56 * June 18, 1997 57 * Karl O. Pinc <kop@meme.com> 58 * 59 * 60 * Added -e "echo" switch & ECHO keyword 61 * Dick Streefland <dicks@tasking.nl> 62 * 63 * 64 * Considerable updates and modifications by 65 * Al Longyear <longyear@pobox.com> 66 * Paul Mackerras <paulus@cs.anu.edu.au> 67 * 68 * 69 * The original author is: 70 * 71 * Karl Fox <karl@MorningStar.Com> 72 * Morning Star Technologies, Inc. 73 * 1760 Zollinger Road 74 * Columbus, OH 43221 75 * (614)451-1883 76 * 77 * 78 */ 79 80 #ifndef lint 81 static const char rcsid[] = 82 "$FreeBSD$"; 83 #endif 84 85 #include <stdio.h> 86 #include <ctype.h> 87 #include <time.h> 88 #include <fcntl.h> 89 #include <signal.h> 90 #include <errno.h> 91 #include <string.h> 92 #include <stdlib.h> 93 #include <unistd.h> 94 #include <sys/types.h> 95 #include <sys/stat.h> 96 #include <syslog.h> 97 98 #ifndef TERMIO 99 #undef TERMIOS 100 #define TERMIOS 101 #endif 102 103 #ifdef TERMIO 104 #include <termio.h> 105 #endif 106 #ifdef TERMIOS 107 #include <termios.h> 108 #endif 109 110 #define STR_LEN 1024 111 112 #ifndef SIGTYPE 113 #define SIGTYPE void 114 #endif 115 116 #undef __P 117 #undef __V 118 119 #ifdef __STDC__ 120 #include <stdarg.h> 121 #define __V(x) x 122 #define __P(x) x 123 #else 124 #include <varargs.h> 125 #define __V(x) (va_alist) va_dcl 126 #define __P(x) () 127 #define const 128 #endif 129 130 #ifndef O_NONBLOCK 131 #define O_NONBLOCK O_NDELAY 132 #endif 133 134 #ifdef SUNOS 135 extern int sys_nerr; 136 extern char *sys_errlist[]; 137 #define memmove(to, from, n) bcopy(from, to, n) 138 #define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\ 139 "unknown error") 140 #endif 141 142 /*************** Micro getopt() *********************************************/ 143 #define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ 144 (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ 145 &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) 146 #define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ 147 (_O=4,(char*)0):(char*)0) 148 #define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0) 149 #define ARG(c,v) (c?(--c,*v++):(char*)0) 150 151 static int _O = 0; /* Internal state */ 152 /*************** Micro getopt() *********************************************/ 153 154 #define MAX_ABORTS 50 155 #define MAX_REPORTS 50 156 #define DEFAULT_CHAT_TIMEOUT 45 157 158 int echo = 0; 159 int verbose = 0; 160 int to_log = 1; 161 int to_stderr = 0; 162 int Verbose = 0; 163 int quiet = 0; 164 int report = 0; 165 int exit_code = 0; 166 FILE* report_fp = (FILE *) 0; 167 char *report_file = (char *) 0; 168 char *chat_file = (char *) 0; 169 char *phone_num = (char *) 0; 170 char *phone_num2 = (char *) 0; 171 int timeout = DEFAULT_CHAT_TIMEOUT; 172 173 int have_tty_parameters = 0; 174 175 #ifdef TERMIO 176 #define term_parms struct termio 177 #define get_term_param(param) ioctl(0, TCGETA, param) 178 #define set_term_param(param) ioctl(0, TCSETA, param) 179 struct termio saved_tty_parameters; 180 #endif 181 182 #ifdef TERMIOS 183 #define term_parms struct termios 184 #define get_term_param(param) tcgetattr(0, param) 185 #define set_term_param(param) tcsetattr(0, TCSANOW, param) 186 struct termios saved_tty_parameters; 187 #endif 188 189 char *abort_string[MAX_ABORTS], *fail_reason = (char *)0, 190 fail_buffer[50]; 191 int n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; 192 int clear_abort_next = 0; 193 194 char *report_string[MAX_REPORTS] ; 195 char report_buffer[50] ; 196 int n_reports = 0, report_next = 0, report_gathering = 0 ; 197 int clear_report_next = 0; 198 199 int say_next = 0, hup_next = 0; 200 201 void *dup_mem __P((void *b, size_t c)); 202 void *copy_of __P((char *s)); 203 static void usage __P((void)); 204 void logf __P((const char *fmt, ...)); 205 void fatal __P((int code, const char *fmt, ...)); 206 SIGTYPE sigalrm __P((int signo)); 207 SIGTYPE sigint __P((int signo)); 208 SIGTYPE sigterm __P((int signo)); 209 SIGTYPE sighup __P((int signo)); 210 void unalarm __P((void)); 211 void init __P((void)); 212 void set_tty_parameters __P((void)); 213 void echo_stderr __P((int)); 214 void break_sequence __P((void)); 215 void terminate __P((int status)); 216 void do_file __P((char *chat_file)); 217 int get_string __P((register char *string)); 218 int put_string __P((register char *s)); 219 int write_char __P((int c)); 220 int put_char __P((int c)); 221 int get_char __P((void)); 222 void chat_send __P((register char *s)); 223 char *character __P((int c)); 224 void chat_expect __P((register char *s)); 225 char *clean __P((register char *s, int sending)); 226 void break_sequence __P((void)); 227 void terminate __P((int status)); 228 void pack_array __P((char **array, int end)); 229 char *expect_strtok __P((char *, char *)); 230 int vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */ 231 232 int main __P((int, char *[])); 233 234 void *dup_mem(b, c) 235 void *b; 236 size_t c; 237 { 238 void *ans = malloc (c); 239 if (!ans) 240 fatal(2, "memory error!"); 241 242 memcpy (ans, b, c); 243 return ans; 244 } 245 246 void *copy_of (s) 247 char *s; 248 { 249 return dup_mem (s, strlen (s) + 1); 250 } 251 252 /* 253 * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \ 254 * [ -r report-file ] \ 255 * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] 256 * 257 * Perform a UUCP-dialer-like chat script on stdin and stdout. 258 */ 259 int 260 main(argc, argv) 261 int argc; 262 char **argv; 263 { 264 int option; 265 char *arg; 266 267 tzset(); 268 269 while ((option = OPTION(argc, argv)) != 0) { 270 switch (option) { 271 case 'e': 272 ++echo; 273 break; 274 275 case 'v': 276 ++verbose; 277 break; 278 279 case 'V': 280 ++Verbose; 281 break; 282 283 case 's': 284 ++to_stderr; 285 break; 286 287 case 'S': 288 to_log = 0; 289 break; 290 291 case 'f': 292 if ((arg = OPTARG(argc, argv)) != NULL) 293 chat_file = copy_of(arg); 294 else 295 usage(); 296 break; 297 298 case 't': 299 if ((arg = OPTARG(argc, argv)) != NULL) 300 timeout = atoi(arg); 301 else 302 usage(); 303 break; 304 305 case 'r': 306 arg = OPTARG (argc, argv); 307 if (arg) { 308 if (report_fp != NULL) 309 fclose (report_fp); 310 report_file = copy_of (arg); 311 report_fp = fopen (report_file, "a"); 312 if (report_fp != NULL) { 313 if (verbose) 314 fprintf (report_fp, "Opening \"%s\"...\n", 315 report_file); 316 report = 1; 317 } 318 } 319 break; 320 321 case 'T': 322 if ((arg = OPTARG(argc, argv)) != NULL) 323 phone_num = copy_of(arg); 324 else 325 usage(); 326 break; 327 328 case 'U': 329 if ((arg = OPTARG(argc, argv)) != NULL) 330 phone_num2 = copy_of(arg); 331 else 332 usage(); 333 break; 334 335 default: 336 usage(); 337 break; 338 } 339 } 340 /* 341 * Default the report file to the stderr location 342 */ 343 if (report_fp == NULL) 344 report_fp = stderr; 345 346 if (to_log) { 347 #ifdef ultrix 348 openlog("chat", LOG_PID); 349 #else 350 openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); 351 352 if (verbose) 353 setlogmask(LOG_UPTO(LOG_INFO)); 354 else 355 setlogmask(LOG_UPTO(LOG_WARNING)); 356 #endif 357 } 358 359 init(); 360 361 if (chat_file != NULL) { 362 arg = ARG(argc, argv); 363 if (arg != NULL) 364 usage(); 365 else 366 do_file (chat_file); 367 } else { 368 while ((arg = ARG(argc, argv)) != NULL) { 369 chat_expect(arg); 370 371 if ((arg = ARG(argc, argv)) != NULL) 372 chat_send(arg); 373 } 374 } 375 376 terminate(0); 377 return 0; 378 } 379 380 /* 381 * Process a chat script when read from a file. 382 */ 383 384 void do_file (chat_file) 385 char *chat_file; 386 { 387 int linect, sendflg; 388 char *sp, *arg, quote; 389 char buf [STR_LEN]; 390 FILE *cfp; 391 392 cfp = fopen (chat_file, "r"); 393 if (cfp == NULL) 394 fatal(1, "%s -- open failed: %m", chat_file); 395 396 linect = 0; 397 sendflg = 0; 398 399 while (fgets(buf, STR_LEN, cfp) != NULL) { 400 sp = strchr (buf, '\n'); 401 if (sp) 402 *sp = '\0'; 403 404 linect++; 405 sp = buf; 406 407 /* lines starting with '#' are comments. If a real '#' 408 is to be expected, it should be quoted .... */ 409 if ( *sp == '#' ) 410 continue; 411 412 while (*sp != '\0') { 413 if (*sp == ' ' || *sp == '\t') { 414 ++sp; 415 continue; 416 } 417 418 if (*sp == '"' || *sp == '\'') { 419 quote = *sp++; 420 arg = sp; 421 while (*sp != quote) { 422 if (*sp == '\0') 423 fatal(1, "unterminated quote (line %d)", linect); 424 425 if (*sp++ == '\\') { 426 if (*sp != '\0') 427 ++sp; 428 } 429 } 430 } 431 else { 432 arg = sp; 433 while (*sp != '\0' && *sp != ' ' && *sp != '\t') 434 ++sp; 435 } 436 437 if (*sp != '\0') 438 *sp++ = '\0'; 439 440 if (sendflg) 441 chat_send (arg); 442 else 443 chat_expect (arg); 444 sendflg = !sendflg; 445 } 446 } 447 fclose (cfp); 448 } 449 450 /* 451 * We got an error parsing the command line. 452 */ 453 static void 454 usage() 455 { 456 fprintf(stderr, "\ 457 Usage: chat [-e] [-v] [-V] [-t timeout] [-r report-file] [-T phone-number]\n\ 458 [-U phone-number2] {-f chat-file | chat-script}\n"); 459 exit(1); 460 } 461 462 char line[1024]; 463 464 /* 465 * Send a message to syslog and/or stderr. 466 */ 467 void logf __V((const char *fmt, ...)) 468 { 469 va_list args; 470 471 #ifdef __STDC__ 472 va_start(args, fmt); 473 #else 474 char *fmt; 475 va_start(args); 476 fmt = va_arg(args, char *); 477 #endif 478 479 vfmtmsg(line, sizeof(line), fmt, args); 480 if (to_log) 481 syslog(LOG_INFO, "%s", line); 482 if (to_stderr) 483 fprintf(stderr, "%s\n", line); 484 } 485 486 /* 487 * Print an error message and terminate. 488 */ 489 490 void fatal __V((int code, const char *fmt, ...)) 491 { 492 va_list args; 493 494 #ifdef __STDC__ 495 va_start(args, fmt); 496 #else 497 int code; 498 char *fmt; 499 va_start(args); 500 code = va_arg(args, int); 501 fmt = va_arg(args, char *); 502 #endif 503 504 vfmtmsg(line, sizeof(line), fmt, args); 505 if (to_log) 506 syslog(LOG_ERR, "%s", line); 507 if (to_stderr) 508 fprintf(stderr, "%s\n", line); 509 terminate(code); 510 } 511 512 int alarmed = 0; 513 514 SIGTYPE sigalrm(signo) 515 int signo; 516 { 517 int flags; 518 519 alarm(1); 520 alarmed = 1; /* Reset alarm to avoid race window */ 521 signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 522 523 if ((flags = fcntl(0, F_GETFL, 0)) == -1) 524 fatal(2, "Can't get file mode flags on stdin: %m"); 525 526 if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 527 fatal(2, "Can't set file mode flags on stdin: %m"); 528 529 if (verbose) 530 logf("alarm"); 531 } 532 533 void unalarm() 534 { 535 int flags; 536 537 if ((flags = fcntl(0, F_GETFL, 0)) == -1) 538 fatal(2, "Can't get file mode flags on stdin: %m"); 539 540 if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1) 541 fatal(2, "Can't set file mode flags on stdin: %m"); 542 } 543 544 SIGTYPE sigint(signo) 545 int signo; 546 { 547 fatal(2, "SIGINT"); 548 } 549 550 SIGTYPE sigterm(signo) 551 int signo; 552 { 553 fatal(2, "SIGTERM"); 554 } 555 556 SIGTYPE sighup(signo) 557 int signo; 558 { 559 fatal(2, "SIGHUP"); 560 } 561 562 void init() 563 { 564 signal(SIGINT, sigint); 565 signal(SIGTERM, sigterm); 566 signal(SIGHUP, sighup); 567 568 set_tty_parameters(); 569 signal(SIGALRM, sigalrm); 570 alarm(0); 571 alarmed = 0; 572 } 573 574 void set_tty_parameters() 575 { 576 #if defined(get_term_param) 577 term_parms t; 578 579 if (get_term_param (&t) < 0) 580 fatal(2, "Can't get terminal parameters: %m"); 581 582 saved_tty_parameters = t; 583 have_tty_parameters = 1; 584 585 t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 586 t.c_oflag = 0; 587 t.c_lflag = 0; 588 t.c_cc[VERASE] = 589 t.c_cc[VKILL] = 0; 590 t.c_cc[VMIN] = 1; 591 t.c_cc[VTIME] = 0; 592 593 if (set_term_param (&t) < 0) 594 fatal(2, "Can't set terminal parameters: %m"); 595 #endif 596 } 597 598 void break_sequence() 599 { 600 #ifdef TERMIOS 601 tcsendbreak (0, 0); 602 #endif 603 } 604 605 void terminate(status) 606 int status; 607 { 608 echo_stderr(-1); 609 if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 610 /* 611 * Allow the last of the report string to be gathered before we terminate. 612 */ 613 if (report_gathering) { 614 int c, rep_len; 615 616 rep_len = strlen(report_buffer); 617 while (rep_len + 1 <= sizeof(report_buffer)) { 618 alarm(1); 619 c = get_char(); 620 alarm(0); 621 if (c < 0 || iscntrl(c)) 622 break; 623 report_buffer[rep_len] = c; 624 ++rep_len; 625 } 626 report_buffer[rep_len] = 0; 627 fprintf (report_fp, "chat: %s\n", report_buffer); 628 } 629 if (verbose) 630 fprintf (report_fp, "Closing \"%s\".\n", report_file); 631 fclose (report_fp); 632 report_fp = (FILE *) NULL; 633 } 634 635 #if defined(get_term_param) 636 if (have_tty_parameters) { 637 if (set_term_param (&saved_tty_parameters) < 0) 638 fatal(2, "Can't restore terminal parameters: %m"); 639 } 640 #endif 641 642 exit(status); 643 } 644 645 /* 646 * 'Clean up' this string. 647 */ 648 char *clean(s, sending) 649 register char *s; 650 int sending; /* set to 1 when sending (putting) this string. */ 651 { 652 char temp[STR_LEN], cur_chr; 653 register char *s1, *phchar; 654 int add_return = sending; 655 #define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 656 657 s1 = temp; 658 /* Don't overflow buffer, leave room for chars we append later */ 659 while (*s && s1 - temp < sizeof(temp) - 2 - add_return) { 660 cur_chr = *s++; 661 if (cur_chr == '^') { 662 cur_chr = *s++; 663 if (cur_chr == '\0') { 664 *s1++ = '^'; 665 break; 666 } 667 cur_chr &= 0x1F; 668 if (cur_chr != 0) { 669 *s1++ = cur_chr; 670 } 671 continue; 672 } 673 674 if (cur_chr != '\\') { 675 *s1++ = cur_chr; 676 continue; 677 } 678 679 cur_chr = *s++; 680 if (cur_chr == '\0') { 681 if (sending) { 682 *s1++ = '\\'; 683 *s1++ = '\\'; 684 } 685 break; 686 } 687 688 switch (cur_chr) { 689 case 'b': 690 *s1++ = '\b'; 691 break; 692 693 case 'c': 694 if (sending && *s == '\0') 695 add_return = 0; 696 else 697 *s1++ = cur_chr; 698 break; 699 700 case '\\': 701 case 'K': 702 case 'p': 703 case 'd': 704 if (sending) 705 *s1++ = '\\'; 706 707 *s1++ = cur_chr; 708 break; 709 710 case 'T': 711 if (sending && phone_num) { 712 for ( phchar = phone_num; *phchar != '\0'; phchar++) 713 *s1++ = *phchar; 714 } 715 else { 716 *s1++ = '\\'; 717 *s1++ = 'T'; 718 } 719 break; 720 721 case 'U': 722 if (sending && phone_num2) { 723 for ( phchar = phone_num2; *phchar != '\0'; phchar++) 724 *s1++ = *phchar; 725 } 726 else { 727 *s1++ = '\\'; 728 *s1++ = 'U'; 729 } 730 break; 731 732 case 'q': 733 quiet = 1; 734 break; 735 736 case 'r': 737 *s1++ = '\r'; 738 break; 739 740 case 'n': 741 *s1++ = '\n'; 742 break; 743 744 case 's': 745 *s1++ = ' '; 746 break; 747 748 case 't': 749 *s1++ = '\t'; 750 break; 751 752 case 'N': 753 if (sending) { 754 *s1++ = '\\'; 755 *s1++ = '\0'; 756 } 757 else 758 *s1++ = 'N'; 759 break; 760 761 default: 762 if (isoctal (cur_chr)) { 763 cur_chr &= 0x07; 764 if (isoctal (*s)) { 765 cur_chr <<= 3; 766 cur_chr |= *s++ - '0'; 767 if (isoctal (*s)) { 768 cur_chr <<= 3; 769 cur_chr |= *s++ - '0'; 770 } 771 } 772 773 if (cur_chr != 0 || sending) { 774 if (sending && (cur_chr == '\\' || cur_chr == 0)) 775 *s1++ = '\\'; 776 *s1++ = cur_chr; 777 } 778 break; 779 } 780 781 if (sending) 782 *s1++ = '\\'; 783 *s1++ = cur_chr; 784 break; 785 } 786 } 787 788 if (add_return) 789 *s1++ = '\r'; 790 791 *s1++ = '\0'; /* guarantee closure */ 792 *s1++ = '\0'; /* terminate the string */ 793 return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ 794 } 795 796 /* 797 * A modified version of 'strtok'. This version skips \ sequences. 798 */ 799 800 char *expect_strtok (s, term) 801 char *s, *term; 802 { 803 static char *str = ""; 804 int escape_flag = 0; 805 char *result; 806 807 /* 808 * If a string was specified then do initial processing. 809 */ 810 if (s) 811 str = s; 812 813 /* 814 * If this is the escape flag then reset it and ignore the character. 815 */ 816 if (*str) 817 result = str; 818 else 819 result = (char *) 0; 820 821 while (*str) { 822 if (escape_flag) { 823 escape_flag = 0; 824 ++str; 825 continue; 826 } 827 828 if (*str == '\\') { 829 ++str; 830 escape_flag = 1; 831 continue; 832 } 833 834 /* 835 * If this is not in the termination string, continue. 836 */ 837 if (strchr (term, *str) == (char *) 0) { 838 ++str; 839 continue; 840 } 841 842 /* 843 * This is the terminator. Mark the end of the string and stop. 844 */ 845 *str++ = '\0'; 846 break; 847 } 848 return (result); 849 } 850 851 /* 852 * Process the expect string 853 */ 854 855 void chat_expect (s) 856 char *s; 857 { 858 char *expect; 859 char *reply; 860 861 if (strcmp(s, "HANGUP") == 0) { 862 ++hup_next; 863 return; 864 } 865 866 if (strcmp(s, "ABORT") == 0) { 867 ++abort_next; 868 return; 869 } 870 871 if (strcmp(s, "CLR_ABORT") == 0) { 872 ++clear_abort_next; 873 return; 874 } 875 876 if (strcmp(s, "REPORT") == 0) { 877 ++report_next; 878 return; 879 } 880 881 if (strcmp(s, "CLR_REPORT") == 0) { 882 ++clear_report_next; 883 return; 884 } 885 886 if (strcmp(s, "TIMEOUT") == 0) { 887 ++timeout_next; 888 return; 889 } 890 891 if (strcmp(s, "ECHO") == 0) { 892 ++echo_next; 893 return; 894 } 895 896 if (strcmp(s, "SAY") == 0) { 897 ++say_next; 898 return; 899 } 900 901 /* 902 * Fetch the expect and reply string. 903 */ 904 for (;;) { 905 expect = expect_strtok (s, "-"); 906 s = (char *) 0; 907 908 if (expect == (char *) 0) 909 return; 910 911 reply = expect_strtok (s, "-"); 912 913 /* 914 * Handle the expect string. If successful then exit. 915 */ 916 if (get_string (expect)) 917 return; 918 919 /* 920 * If there is a sub-reply string then send it. Otherwise any condition 921 * is terminal. 922 */ 923 if (reply == (char *) 0 || exit_code != 3) 924 break; 925 926 chat_send (reply); 927 } 928 929 /* 930 * The expectation did not occur. This is terminal. 931 */ 932 if (fail_reason) 933 logf("Failed (%s)", fail_reason); 934 else 935 logf("Failed"); 936 terminate(exit_code); 937 } 938 939 /* 940 * Translate the input character to the appropriate string for printing 941 * the data. 942 */ 943 944 char *character(c) 945 int c; 946 { 947 static char string[10]; 948 char *meta; 949 950 meta = (c & 0x80) ? "M-" : ""; 951 c &= 0x7F; 952 953 if (c < 32) 954 sprintf(string, "%s^%c", meta, (int)c + '@'); 955 else if (c == 127) 956 sprintf(string, "%s^?", meta); 957 else 958 sprintf(string, "%s%c", meta, c); 959 960 return (string); 961 } 962 963 /* 964 * process the reply string 965 */ 966 void chat_send (s) 967 register char *s; 968 { 969 if (say_next) { 970 say_next = 0; 971 s = clean(s,0); 972 write(2, s, strlen(s)); 973 free(s); 974 return; 975 } 976 977 if (hup_next) { 978 hup_next = 0; 979 if (strcmp(s, "OFF") == 0) 980 signal(SIGHUP, SIG_IGN); 981 else 982 signal(SIGHUP, sighup); 983 return; 984 } 985 986 if (echo_next) { 987 echo_next = 0; 988 echo = (strcmp(s, "ON") == 0); 989 return; 990 } 991 992 if (abort_next) { 993 char *s1; 994 995 abort_next = 0; 996 997 if (n_aborts >= MAX_ABORTS) 998 fatal(2, "Too many ABORT strings"); 999 1000 s1 = clean(s, 0); 1001 1002 if (strlen(s1) > strlen(s) 1003 || strlen(s1) + 1 > sizeof(fail_buffer)) 1004 fatal(1, "Illegal or too-long ABORT string ('%v')", s); 1005 1006 abort_string[n_aborts++] = s1; 1007 1008 if (verbose) 1009 logf("abort on (%v)", s); 1010 return; 1011 } 1012 1013 if (clear_abort_next) { 1014 char *s1; 1015 int i; 1016 int old_max; 1017 int pack = 0; 1018 1019 clear_abort_next = 0; 1020 1021 s1 = clean(s, 0); 1022 1023 if (strlen(s1) > strlen(s) 1024 || strlen(s1) + 1 > sizeof(fail_buffer)) 1025 fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 1026 1027 old_max = n_aborts; 1028 for (i=0; i < n_aborts; i++) { 1029 if ( strcmp(s1,abort_string[i]) == 0 ) { 1030 free(abort_string[i]); 1031 abort_string[i] = NULL; 1032 pack++; 1033 n_aborts--; 1034 if (verbose) 1035 logf("clear abort on (%v)", s); 1036 } 1037 } 1038 free(s1); 1039 if (pack) 1040 pack_array(abort_string,old_max); 1041 return; 1042 } 1043 1044 if (report_next) { 1045 char *s1; 1046 1047 report_next = 0; 1048 if (n_reports >= MAX_REPORTS) 1049 fatal(2, "Too many REPORT strings"); 1050 1051 s1 = clean(s, 0); 1052 1053 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 1054 fatal(1, "Illegal or too-long REPORT string ('%v')", s); 1055 1056 report_string[n_reports++] = s1; 1057 1058 if (verbose) 1059 logf("report (%v)", s); 1060 return; 1061 } 1062 1063 if (clear_report_next) { 1064 char *s1; 1065 int i; 1066 int old_max; 1067 int pack = 0; 1068 1069 clear_report_next = 0; 1070 1071 s1 = clean(s, 0); 1072 1073 if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 1074 fatal(1, "Illegal or too-long REPORT string ('%v')", s); 1075 1076 old_max = n_reports; 1077 for (i=0; i < n_reports; i++) { 1078 if ( strcmp(s1,report_string[i]) == 0 ) { 1079 free(report_string[i]); 1080 report_string[i] = NULL; 1081 pack++; 1082 n_reports--; 1083 if (verbose) 1084 logf("clear report (%v)", s); 1085 } 1086 } 1087 free(s1); 1088 if (pack) 1089 pack_array(report_string,old_max); 1090 1091 return; 1092 } 1093 1094 if (timeout_next) { 1095 timeout_next = 0; 1096 timeout = atoi(s); 1097 1098 if (timeout <= 0) 1099 timeout = DEFAULT_CHAT_TIMEOUT; 1100 1101 if (verbose) 1102 logf("timeout set to %d seconds", timeout); 1103 1104 return; 1105 } 1106 1107 if (strcmp(s, "EOT") == 0) 1108 s = "^D\\c"; 1109 else if (strcmp(s, "BREAK") == 0) 1110 s = "\\K\\c"; 1111 1112 if (!put_string(s)) 1113 fatal(1, "Failed"); 1114 } 1115 1116 int get_char() 1117 { 1118 int status; 1119 char c; 1120 1121 status = read(0, &c, 1); 1122 1123 switch (status) { 1124 case 1: 1125 return ((int)c & 0x7F); 1126 1127 default: 1128 logf("warning: read() on stdin returned %d", status); 1129 1130 case -1: 1131 if ((status = fcntl(0, F_GETFL, 0)) == -1) 1132 fatal(2, "Can't get file mode flags on stdin: %m"); 1133 1134 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 1135 fatal(2, "Can't set file mode flags on stdin: %m"); 1136 1137 return (-1); 1138 } 1139 } 1140 1141 int put_char(c) 1142 int c; 1143 { 1144 int status; 1145 char ch = c; 1146 1147 usleep(10000); /* inter-character typing delay (?) */ 1148 1149 status = write(1, &ch, 1); 1150 1151 switch (status) { 1152 case 1: 1153 return (0); 1154 1155 default: 1156 logf("warning: write() on stdout returned %d", status); 1157 1158 case -1: 1159 if ((status = fcntl(0, F_GETFL, 0)) == -1) 1160 fatal(2, "Can't get file mode flags on stdin, %m"); 1161 1162 if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 1163 fatal(2, "Can't set file mode flags on stdin: %m"); 1164 1165 return (-1); 1166 } 1167 } 1168 1169 int write_char (c) 1170 int c; 1171 { 1172 if (alarmed || put_char(c) < 0) { 1173 alarm(0); 1174 alarmed = 0; 1175 1176 if (verbose) { 1177 if (errno == EINTR || errno == EWOULDBLOCK) 1178 logf(" -- write timed out"); 1179 else 1180 logf(" -- write failed: %m"); 1181 } 1182 return (0); 1183 } 1184 return (1); 1185 } 1186 1187 int put_string (s) 1188 register char *s; 1189 { 1190 quiet = 0; 1191 s = clean(s, 1); 1192 1193 if (verbose) { 1194 if (quiet) 1195 logf("send (??????)"); 1196 else 1197 logf("send (%v)", s); 1198 } 1199 1200 alarm(timeout); alarmed = 0; 1201 1202 while (*s) { 1203 register char c = *s++; 1204 1205 if (c != '\\') { 1206 if (!write_char (c)) 1207 return 0; 1208 continue; 1209 } 1210 1211 c = *s++; 1212 switch (c) { 1213 case 'd': 1214 sleep(1); 1215 break; 1216 1217 case 'K': 1218 break_sequence(); 1219 break; 1220 1221 case 'p': 1222 usleep(10000); /* 1/100th of a second (arg is microseconds) */ 1223 break; 1224 1225 default: 1226 if (!write_char (c)) 1227 return 0; 1228 break; 1229 } 1230 } 1231 1232 alarm(0); 1233 alarmed = 0; 1234 return (1); 1235 } 1236 1237 /* 1238 * Echo a character to stderr. 1239 * When called with -1, a '\n' character is generated when 1240 * the cursor is not at the beginning of a line. 1241 */ 1242 void echo_stderr(n) 1243 int n; 1244 { 1245 static int need_lf; 1246 char *s; 1247 1248 switch (n) { 1249 case '\r': /* ignore '\r' */ 1250 break; 1251 case -1: 1252 if (need_lf == 0) 1253 break; 1254 /* fall through */ 1255 case '\n': 1256 write(2, "\n", 1); 1257 need_lf = 0; 1258 break; 1259 default: 1260 s = character(n); 1261 write(2, s, strlen(s)); 1262 need_lf = 1; 1263 break; 1264 } 1265 } 1266 1267 /* 1268 * 'Wait for' this string to appear on this file descriptor. 1269 */ 1270 int get_string(string) 1271 register char *string; 1272 { 1273 char temp[STR_LEN]; 1274 int c, printed = 0, len, minlen; 1275 register char *s = temp, *end = s + STR_LEN; 1276 char *logged = temp; 1277 1278 fail_reason = (char *)0; 1279 1280 if (strlen(string) > STR_LEN) { 1281 logf("expect string is too long"); 1282 exit_code = 1; 1283 return 0; 1284 } 1285 1286 string = clean(string, 0); 1287 len = strlen(string); 1288 minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 1289 1290 if (verbose) 1291 logf("expect (%v)", string); 1292 1293 if (len == 0) { 1294 if (verbose) 1295 logf("got it"); 1296 return (1); 1297 } 1298 1299 alarm(timeout); 1300 alarmed = 0; 1301 1302 while ( ! alarmed && (c = get_char()) >= 0) { 1303 int n, abort_len, report_len; 1304 1305 if (echo) 1306 echo_stderr(c); 1307 if (verbose && c == '\n') { 1308 if (s == logged) 1309 logf(""); /* blank line */ 1310 else 1311 logf("%0.*v", s - logged, logged); 1312 logged = s + 1; 1313 } 1314 1315 *s++ = c; 1316 1317 if (verbose && s >= logged + 80) { 1318 logf("%0.*v", s - logged, logged); 1319 logged = s; 1320 } 1321 1322 if (Verbose) { 1323 if (c == '\n') 1324 fputc( '\n', stderr ); 1325 else if (c != '\r') 1326 fprintf( stderr, "%s", character(c) ); 1327 } 1328 1329 if (!report_gathering) { 1330 for (n = 0; n < n_reports; ++n) { 1331 if ((report_string[n] != (char*) NULL) && 1332 s - temp >= (report_len = strlen(report_string[n])) && 1333 strncmp(s - report_len, report_string[n], report_len) == 0) { 1334 time_t time_now = time ((time_t*) NULL); 1335 struct tm* tm_now = localtime (&time_now); 1336 1337 strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 1338 strcat (report_buffer, report_string[n]); 1339 1340 report_string[n] = (char *) NULL; 1341 report_gathering = 1; 1342 break; 1343 } 1344 } 1345 } 1346 else { 1347 if (!iscntrl (c)) { 1348 int rep_len = strlen (report_buffer); 1349 report_buffer[rep_len] = c; 1350 report_buffer[rep_len + 1] = '\0'; 1351 } 1352 else { 1353 report_gathering = 0; 1354 fprintf (report_fp, "chat: %s\n", report_buffer); 1355 } 1356 } 1357 1358 if (s - temp >= len && 1359 c == string[len - 1] && 1360 strncmp(s - len, string, len) == 0) { 1361 if (verbose) { 1362 if (s > logged) 1363 logf("%0.*v", s - logged, logged); 1364 logf(" -- got it\n"); 1365 } 1366 1367 alarm(0); 1368 alarmed = 0; 1369 return (1); 1370 } 1371 1372 for (n = 0; n < n_aborts; ++n) { 1373 if (s - temp >= (abort_len = strlen(abort_string[n])) && 1374 strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 1375 if (verbose) { 1376 if (s > logged) 1377 logf("%0.*v", s - logged, logged); 1378 logf(" -- failed"); 1379 } 1380 1381 alarm(0); 1382 alarmed = 0; 1383 exit_code = n + 4; 1384 strcpy(fail_reason = fail_buffer, abort_string[n]); 1385 return (0); 1386 } 1387 } 1388 1389 if (s >= end) { 1390 if (logged < s - minlen) { 1391 logf("%0.*v", s - logged, logged); 1392 logged = s; 1393 } 1394 s -= minlen; 1395 memmove(temp, s, minlen); 1396 logged = temp + (logged - s); 1397 s = temp + minlen; 1398 } 1399 1400 if (alarmed && verbose) 1401 logf("warning: alarm synchronization problem"); 1402 } 1403 1404 alarm(0); 1405 1406 if (verbose && printed) { 1407 if (alarmed) 1408 logf(" -- read timed out"); 1409 else 1410 logf(" -- read failed: %m"); 1411 } 1412 1413 exit_code = 3; 1414 alarmed = 0; 1415 return (0); 1416 } 1417 1418 /* 1419 * Gross kludge to handle Solaris versions >= 2.6 having usleep. 1420 */ 1421 #ifdef SOL2 1422 #include <sys/param.h> 1423 #if MAXUID > 65536 /* then this is Solaris 2.6 or later */ 1424 #undef NO_USLEEP 1425 #endif 1426 #endif /* SOL2 */ 1427 1428 #ifdef NO_USLEEP 1429 #include <sys/types.h> 1430 #include <sys/time.h> 1431 1432 /* 1433 usleep -- support routine for 4.2BSD system call emulations 1434 last edit: 29-Oct-1984 D A Gwyn 1435 */ 1436 1437 extern int select(); 1438 1439 int 1440 usleep( usec ) /* returns 0 if ok, else -1 */ 1441 long usec; /* delay in microseconds */ 1442 { 1443 static struct { /* `timeval' */ 1444 long tv_sec; /* seconds */ 1445 long tv_usec; /* microsecs */ 1446 } delay; /* _select() timeout */ 1447 1448 delay.tv_sec = usec / 1000000L; 1449 delay.tv_usec = usec % 1000000L; 1450 1451 return select(0, (long *)0, (long *)0, (long *)0, &delay); 1452 } 1453 #endif 1454 1455 void 1456 pack_array (array, end) 1457 char **array; /* The address of the array of string pointers */ 1458 int end; /* The index of the next free entry before CLR_ */ 1459 { 1460 int i, j; 1461 1462 for (i = 0; i < end; i++) { 1463 if (array[i] == NULL) { 1464 for (j = i+1; j < end; ++j) 1465 if (array[j] != NULL) 1466 array[i++] = array[j]; 1467 for (; i < end; ++i) 1468 array[i] = NULL; 1469 break; 1470 } 1471 } 1472 } 1473 1474 /* 1475 * vfmtmsg - format a message into a buffer. Like vsprintf except we 1476 * also specify the length of the output buffer, and we handle the 1477 * %m (error message) format. 1478 * Doesn't do floating-point formats. 1479 * Returns the number of chars put into buf. 1480 */ 1481 #define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 1482 1483 int 1484 vfmtmsg(buf, buflen, fmt, args) 1485 char *buf; 1486 int buflen; 1487 const char *fmt; 1488 va_list args; 1489 { 1490 int c, i, n; 1491 int width, prec, fillch; 1492 int base, len, neg, quoted; 1493 unsigned long val = 0; 1494 char *str, *buf0; 1495 const char *f; 1496 unsigned char *p; 1497 char num[32]; 1498 static char hexchars[] = "0123456789abcdef"; 1499 1500 buf0 = buf; 1501 --buflen; 1502 while (buflen > 0) { 1503 for (f = fmt; *f != '%' && *f != 0; ++f) 1504 ; 1505 if (f > fmt) { 1506 len = f - fmt; 1507 if (len > buflen) 1508 len = buflen; 1509 memcpy(buf, fmt, len); 1510 buf += len; 1511 buflen -= len; 1512 fmt = f; 1513 } 1514 if (*fmt == 0) 1515 break; 1516 c = *++fmt; 1517 width = prec = 0; 1518 fillch = ' '; 1519 if (c == '0') { 1520 fillch = '0'; 1521 c = *++fmt; 1522 } 1523 if (c == '*') { 1524 width = va_arg(args, int); 1525 c = *++fmt; 1526 } else { 1527 while (isdigit(c)) { 1528 width = width * 10 + c - '0'; 1529 c = *++fmt; 1530 } 1531 } 1532 if (c == '.') { 1533 c = *++fmt; 1534 if (c == '*') { 1535 prec = va_arg(args, int); 1536 c = *++fmt; 1537 } else { 1538 while (isdigit(c)) { 1539 prec = prec * 10 + c - '0'; 1540 c = *++fmt; 1541 } 1542 } 1543 } 1544 str = 0; 1545 base = 0; 1546 neg = 0; 1547 ++fmt; 1548 switch (c) { 1549 case 'd': 1550 i = va_arg(args, int); 1551 if (i < 0) { 1552 neg = 1; 1553 val = -i; 1554 } else 1555 val = i; 1556 base = 10; 1557 break; 1558 case 'o': 1559 val = va_arg(args, unsigned int); 1560 base = 8; 1561 break; 1562 case 'x': 1563 val = va_arg(args, unsigned int); 1564 base = 16; 1565 break; 1566 case 'p': 1567 val = (unsigned long) va_arg(args, void *); 1568 base = 16; 1569 neg = 2; 1570 break; 1571 case 's': 1572 str = va_arg(args, char *); 1573 break; 1574 case 'c': 1575 num[0] = va_arg(args, int); 1576 num[1] = 0; 1577 str = num; 1578 break; 1579 case 'm': 1580 str = strerror(errno); 1581 break; 1582 case 'v': /* "visible" string */ 1583 case 'q': /* quoted string */ 1584 quoted = c == 'q'; 1585 p = va_arg(args, unsigned char *); 1586 if (fillch == '0' && prec > 0) { 1587 n = prec; 1588 } else { 1589 n = strlen((char *)p); 1590 if (prec > 0 && prec < n) 1591 n = prec; 1592 } 1593 while (n > 0 && buflen > 0) { 1594 c = *p++; 1595 --n; 1596 if (!quoted && c >= 0x80) { 1597 OUTCHAR('M'); 1598 OUTCHAR('-'); 1599 c -= 0x80; 1600 } 1601 if (quoted && (c == '"' || c == '\\')) 1602 OUTCHAR('\\'); 1603 if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 1604 if (quoted) { 1605 OUTCHAR('\\'); 1606 switch (c) { 1607 case '\t': OUTCHAR('t'); break; 1608 case '\n': OUTCHAR('n'); break; 1609 case '\b': OUTCHAR('b'); break; 1610 case '\f': OUTCHAR('f'); break; 1611 default: 1612 OUTCHAR('x'); 1613 OUTCHAR(hexchars[c >> 4]); 1614 OUTCHAR(hexchars[c & 0xf]); 1615 } 1616 } else { 1617 if (c == '\t') 1618 OUTCHAR(c); 1619 else { 1620 OUTCHAR('^'); 1621 OUTCHAR(c ^ 0x40); 1622 } 1623 } 1624 } else 1625 OUTCHAR(c); 1626 } 1627 continue; 1628 default: 1629 *buf++ = '%'; 1630 if (c != '%') 1631 --fmt; /* so %z outputs %z etc. */ 1632 --buflen; 1633 continue; 1634 } 1635 if (base != 0) { 1636 str = num + sizeof(num); 1637 *--str = 0; 1638 while (str > num + neg) { 1639 *--str = hexchars[val % base]; 1640 val = val / base; 1641 if (--prec <= 0 && val == 0) 1642 break; 1643 } 1644 switch (neg) { 1645 case 1: 1646 *--str = '-'; 1647 break; 1648 case 2: 1649 *--str = 'x'; 1650 *--str = '0'; 1651 break; 1652 } 1653 len = num + sizeof(num) - 1 - str; 1654 } else { 1655 len = strlen(str); 1656 if (prec > 0 && len > prec) 1657 len = prec; 1658 } 1659 if (width > 0) { 1660 if (width > buflen) 1661 width = buflen; 1662 if ((n = width - len) > 0) { 1663 buflen -= n; 1664 for (; n > 0; --n) 1665 *buf++ = fillch; 1666 } 1667 } 1668 if (len > buflen) 1669 len = buflen; 1670 memcpy(buf, str, len); 1671 buf += len; 1672 buflen -= len; 1673 } 1674 *buf = 0; 1675 return buf - buf0; 1676 } 1677