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