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