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