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