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