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