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