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