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