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