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