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