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