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