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