chat.c revision 53686
133965Sjdp/* 278828Sobrien * Chat -- a program for automatic session establishment (i.e. dial 389857Sobrien * the phone and log in). 438889Sjdp * 533965Sjdp * Standard termination codes: 633965Sjdp * 0 - successful completion of the script 733965Sjdp * 1 - invalid argument, expect string too large, etc. 833965Sjdp * 2 - error on an I/O operation or fatal error condition. 933965Sjdp * 3 - timeout waiting for a simple string. 1060484Sobrien * 4 - the first string declared as "ABORT" 1133965Sjdp * 5 - the second string declared as "ABORT" 1233965Sjdp * 6 - ... and so on for successive ABORT strings. 1333965Sjdp * 1433965Sjdp * This software is in the public domain. 1533965Sjdp * 1633965Sjdp * ----------------- 1733965Sjdp * added -T and -U option and \T and \U substitution to pass a phone 1833965Sjdp * number into chat script. Two are needed for some ISDN TA applications. 1933965Sjdp * Keith Dart <kdart@cisco.com> 2033965Sjdp * 2133965Sjdp * 2233965Sjdp * Added SAY keyword to send output to stderr. 2333965Sjdp * This allows to turn ECHO OFF and to output specific, user selected, 2433965Sjdp * text to give progress messages. This best works when stderr 2533965Sjdp * exists (i.e.: pppd in nodetach mode). 2689857Sobrien * 2733965Sjdp * Added HANGUP directives to allow for us to be called 2833965Sjdp * back. When HANGUP is set to NO, chat will not hangup at HUP signal. 2933965Sjdp * We rely on timeouts in that case. 3033965Sjdp * 3133965Sjdp * Added CLR_ABORT to clear previously set ABORT string. This has been 3233965Sjdp * dictated by the HANGUP above as "NO CARRIER" (for example) must be 3333965Sjdp * an ABORT condition until we know the other host is going to close 3433965Sjdp * the connection for call back. As soon as we have completed the 3533965Sjdp * first stage of the call back sequence, "NO CARRIER" is a valid, non 3633965Sjdp * fatal string. As soon as we got called back (probably get "CONNECT"), 3733965Sjdp * we should re-arm the ABORT "NO CARRIER". Hence the CLR_ABORT command. 3833965Sjdp * Note that CLR_ABORT packs the abort_strings[] array so that we do not 3977298Sobrien * have unused entries not being reclaimed. 4033965Sjdp * 4160484Sobrien * In the same vein as above, added CLR_REPORT keyword. 4233965Sjdp * 4333965Sjdp * Allow for comments. Line starting with '#' are comments and are 4478828Sobrien * ignored. If a '#' is to be expected as the first character, the 4578828Sobrien * expect string must be quoted. 4633965Sjdp * 4733965Sjdp * 4833965Sjdp * Francis Demierre <Francis@SwissMail.Com> 4933965Sjdp * Thu May 15 17:15:40 MET DST 1997 5033965Sjdp * 5133965Sjdp * 5277298Sobrien * Added -r "report file" switch & REPORT keyword. 5333965Sjdp * Robert Geer <bgeer@xmission.com> 5433965Sjdp * 5533965Sjdp * Added -s "use stderr" and -S "don't use syslog" switches. 5633965Sjdp * June 18, 1997 5733965Sjdp * Karl O. Pinc <kop@meme.com> 5833965Sjdp * 5977298Sobrien * 6077298Sobrien * Added -e "echo" switch & ECHO keyword 6133965Sjdp * Dick Streefland <dicks@tasking.nl> 6233965Sjdp * 6333965Sjdp * 6433965Sjdp * Considerable updates and modifications by 6577298Sobrien * Al Longyear <longyear@pobox.com> 6677298Sobrien * Paul Mackerras <paulus@cs.anu.edu.au> 6733965Sjdp * 6877298Sobrien * 6977298Sobrien * The original author is: 7077298Sobrien * 7177298Sobrien * Karl Fox <karl@MorningStar.Com> 7233965Sjdp * Morning Star Technologies, Inc. 7333965Sjdp * 1760 Zollinger Road 7460484Sobrien * Columbus, OH 43221 7577298Sobrien * (614)451-1883 7660484Sobrien * 7760484Sobrien * 7833965Sjdp */ 7960484Sobrien 8089857Sobrien#ifndef lint 8189857Sobrienstatic const char rcsid[] = 8277298Sobrien "$FreeBSD: head/usr.bin/chat/chat.c 53686 1999-11-25 07:28:54Z kris $"; 8389857Sobrien#endif 8477298Sobrien 8577298Sobrien#include <stdio.h> 8689857Sobrien#include <ctype.h> 8777298Sobrien#include <time.h> 8877298Sobrien#include <fcntl.h> 8989857Sobrien#include <signal.h> 9077298Sobrien#include <errno.h> 9177298Sobrien#include <string.h> 9277298Sobrien#include <stdlib.h> 9377298Sobrien#include <unistd.h> 9433965Sjdp#include <sys/types.h> 9594536Sobrien#include <sys/stat.h> 9633965Sjdp#include <syslog.h> 9733965Sjdp 9877298Sobrien#ifndef TERMIO 9977298Sobrien#undef TERMIOS 10033965Sjdp#define TERMIOS 10177298Sobrien#endif 10233965Sjdp 10377298Sobrien#ifdef TERMIO 10477298Sobrien#include <termio.h> 10577298Sobrien#endif 10633965Sjdp#ifdef TERMIOS 10777298Sobrien#include <termios.h> 10877298Sobrien#endif 10977298Sobrien 11033965Sjdp#define STR_LEN 1024 11177298Sobrien 11277298Sobrien#ifndef SIGTYPE 11333965Sjdp#define SIGTYPE void 11477298Sobrien#endif 11533965Sjdp 11633965Sjdp#undef __P 11777298Sobrien#undef __V 11877298Sobrien 11977298Sobrien#ifdef __STDC__ 12077298Sobrien#include <stdarg.h> 12133965Sjdp#define __V(x) x 12289857Sobrien#define __P(x) x 12377298Sobrien#else 12477298Sobrien#include <varargs.h> 12533965Sjdp#define __V(x) (va_alist) va_dcl 12677298Sobrien#define __P(x) () 12789857Sobrien#define const 12833965Sjdp#endif 12938889Sjdp 13033965Sjdp#ifndef O_NONBLOCK 13133965Sjdp#define O_NONBLOCK O_NDELAY 13233965Sjdp#endif 13333965Sjdp 13433965Sjdp#ifdef SUNOS 13533965Sjdpextern int sys_nerr; 13677298Sobrienextern char *sys_errlist[]; 13789857Sobrien#define memmove(to, from, n) bcopy(from, to, n) 13877298Sobrien#define strerror(n) ((unsigned)(n) < sys_nerr? sys_errlist[(n)] :\ 13933965Sjdp "unknown error") 14089857Sobrien#endif 14160484Sobrien 14260484Sobrien/*************** Micro getopt() *********************************************/ 14377298Sobrien#define OPTION(c,v) (_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \ 14477298Sobrien (--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\ 14577298Sobrien &&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0)) 14677298Sobrien#define OPTARG(c,v) (_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \ 14777298Sobrien (_O=4,(char*)0):(char*)0) 14877298Sobrien#define OPTONLYARG(c,v) (_O&2&&**v?(_O=1,--c,*v++):(char*)0) 14960484Sobrien#define ARG(c,v) (c?(--c,*v++):(char*)0) 15060484Sobrien 15177298Sobrienstatic int _O = 0; /* Internal state */ 15277298Sobrien/*************** Micro getopt() *********************************************/ 15377298Sobrien 15433965Sjdp#define MAX_ABORTS 50 15560484Sobrien#define MAX_REPORTS 50 15689857Sobrien#define DEFAULT_CHAT_TIMEOUT 45 15789857Sobrien 15889857Sobrienint echo = 0; 15977298Sobrienint verbose = 0; 16077298Sobrienint to_log = 1; 16189857Sobrienint to_stderr = 0; 16260484Sobrienint Verbose = 0; 16389857Sobrienint quiet = 0; 16489857Sobrienint report = 0; 16560484Sobrienint exit_code = 0; 16689857SobrienFILE* report_fp = (FILE *) 0; 16789857Sobrienchar *report_file = (char *) 0; 16860484Sobrienchar *chat_file = (char *) 0; 16977298Sobrienchar *phone_num = (char *) 0; 17060484Sobrienchar *phone_num2 = (char *) 0; 17160484Sobrienint timeout = DEFAULT_CHAT_TIMEOUT; 17260484Sobrien 17377298Sobrienint have_tty_parameters = 0; 17460484Sobrien 17577298Sobrien#ifdef TERMIO 17633965Sjdp#define term_parms struct termio 17733965Sjdp#define get_term_param(param) ioctl(0, TCGETA, param) 17868765Sobrien#define set_term_param(param) ioctl(0, TCSETA, param) 17933965Sjdpstruct termio saved_tty_parameters; 18060484Sobrien#endif 18133965Sjdp 18291041Sobrien#ifdef TERMIOS 18333965Sjdp#define term_parms struct termios 18433965Sjdp#define get_term_param(param) tcgetattr(0, param) 18533965Sjdp#define set_term_param(param) tcsetattr(0, TCSANOW, param) 18633965Sjdpstruct termios saved_tty_parameters; 18733965Sjdp#endif 18833965Sjdp 18977298Sobrienchar *abort_string[MAX_ABORTS], *fail_reason = (char *)0, 19033965Sjdp fail_buffer[50]; 19133965Sjdpint n_aborts = 0, abort_next = 0, timeout_next = 0, echo_next = 0; 19233965Sjdpint clear_abort_next = 0; 19378828Sobrien 19433965Sjdpchar *report_string[MAX_REPORTS] ; 19533965Sjdpchar report_buffer[50] ; 19633965Sjdpint n_reports = 0, report_next = 0, report_gathering = 0 ; 19733965Sjdpint clear_report_next = 0; 19833965Sjdp 19978828Sobrienint say_next = 0, hup_next = 0; 20078828Sobrien 20178828Sobrienvoid *dup_mem __P((void *b, size_t c)); 20278828Sobrienvoid *copy_of __P((char *s)); 20333965Sjdpstatic void usage __P((void)); 20478828Sobrienvoid logf __P((const char *fmt, ...)); 20578828Sobrienvoid fatal __P((int code, const char *fmt, ...)); 20633965SjdpSIGTYPE sigalrm __P((int signo)); 20778828SobrienSIGTYPE sigint __P((int signo)); 20878828SobrienSIGTYPE sigterm __P((int signo)); 20933965SjdpSIGTYPE sighup __P((int signo)); 21033965Sjdpvoid unalarm __P((void)); 21133965Sjdpvoid init __P((void)); 21233965Sjdpvoid set_tty_parameters __P((void)); 21333965Sjdpvoid echo_stderr __P((int)); 21433965Sjdpvoid break_sequence __P((void)); 21533965Sjdpvoid terminate __P((int status)); 21633965Sjdpvoid do_file __P((char *chat_file)); 21733965Sjdpint get_string __P((register char *string)); 21833965Sjdpint put_string __P((register char *s)); 21977298Sobrienint write_char __P((int c)); 22077298Sobrienint put_char __P((int c)); 22177298Sobrienint get_char __P((void)); 22277298Sobrienvoid chat_send __P((register char *s)); 22377298Sobrienchar *character __P((int c)); 22460484Sobrienvoid chat_expect __P((register char *s)); 22577298Sobrienchar *clean __P((register char *s, int sending)); 22677298Sobrienvoid break_sequence __P((void)); 22777298Sobrienvoid terminate __P((int status)); 22877298Sobrienvoid pack_array __P((char **array, int end)); 22977298Sobrienchar *expect_strtok __P((char *, char *)); 23077298Sobrienint vfmtmsg __P((char *, int, const char *, va_list)); /* vsprintf++ */ 23177298Sobrien 23277298Sobrienint main __P((int, char *[])); 23377298Sobrien 23477298Sobrienvoid *dup_mem(b, c) 23577298Sobrienvoid *b; 23677298Sobriensize_t c; 23777298Sobrien{ 23860484Sobrien void *ans = malloc (c); 23989857Sobrien if (!ans) 24060484Sobrien fatal(2, "memory error!"); 24160484Sobrien 24260484Sobrien memcpy (ans, b, c); 24377298Sobrien return ans; 24460484Sobrien} 24589857Sobrien 24689857Sobrienvoid *copy_of (s) 24789857Sobrienchar *s; 24889857Sobrien{ 24989857Sobrien return dup_mem (s, strlen (s) + 1); 25089857Sobrien} 25160484Sobrien 25289857Sobrien/* 25389857Sobrien * chat [ -v ] [-T number] [-U number] [ -t timeout ] [ -f chat-file ] \ 25489857Sobrien * [ -r report-file ] \ 25589857Sobrien * [...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]] 25689857Sobrien * 25789857Sobrien * Perform a UUCP-dialer-like chat script on stdin and stdout. 25889857Sobrien */ 25977298Sobrienint 26089857Sobrienmain(argc, argv) 26189857Sobrien int argc; 26260484Sobrien char **argv; 26389857Sobrien{ 26489857Sobrien int option; 26589857Sobrien char *arg; 26689857Sobrien 26789857Sobrien tzset(); 26889857Sobrien 26989857Sobrien while ((option = OPTION(argc, argv)) != 0) { 27089857Sobrien switch (option) { 27189857Sobrien case 'e': 27289857Sobrien ++echo; 27360484Sobrien break; 27489857Sobrien 27589857Sobrien case 'v': 27689857Sobrien ++verbose; 27789857Sobrien break; 27889857Sobrien 27989857Sobrien case 'V': 28089857Sobrien ++Verbose; 28189857Sobrien break; 28289857Sobrien 28389857Sobrien case 's': 28489857Sobrien ++to_stderr; 28589857Sobrien break; 28689857Sobrien 28789857Sobrien case 'S': 28860484Sobrien to_log = 0; 28989857Sobrien break; 29089857Sobrien 29189857Sobrien case 'f': 29260484Sobrien if ((arg = OPTARG(argc, argv)) != NULL) 29389857Sobrien chat_file = copy_of(arg); 29489857Sobrien else 29589857Sobrien usage(); 29660484Sobrien break; 29789857Sobrien 29889857Sobrien case 't': 29989857Sobrien if ((arg = OPTARG(argc, argv)) != NULL) 30089857Sobrien timeout = atoi(arg); 30189857Sobrien else 30260484Sobrien usage(); 30389857Sobrien break; 30489857Sobrien 30560484Sobrien case 'r': 30689857Sobrien arg = OPTARG (argc, argv); 30760484Sobrien if (arg) { 30860484Sobrien if (report_fp != NULL) 30960484Sobrien fclose (report_fp); 31060484Sobrien report_file = copy_of (arg); 31160484Sobrien report_fp = fopen (report_file, "a"); 31260484Sobrien if (report_fp != NULL) { 31360484Sobrien if (verbose) 31489857Sobrien fprintf (report_fp, "Opening \"%s\"...\n", 31560484Sobrien report_file); 31660484Sobrien report = 1; 31760484Sobrien } 31877298Sobrien } 31960484Sobrien break; 32060484Sobrien 32160484Sobrien case 'T': 32289857Sobrien if ((arg = OPTARG(argc, argv)) != NULL) 32360484Sobrien phone_num = copy_of(arg); 32460484Sobrien else 32560484Sobrien usage(); 32660484Sobrien break; 32760484Sobrien 32860484Sobrien case 'U': 32960484Sobrien if ((arg = OPTARG(argc, argv)) != NULL) 33060484Sobrien phone_num2 = copy_of(arg); 33160484Sobrien else 33260484Sobrien usage(); 33360484Sobrien break; 33460484Sobrien 33560484Sobrien default: 33660484Sobrien usage(); 33760484Sobrien break; 33860484Sobrien } 33989857Sobrien } 34060484Sobrien/* 34160484Sobrien * Default the report file to the stderr location 34260484Sobrien */ 34360484Sobrien if (report_fp == NULL) 34460484Sobrien report_fp = stderr; 34560484Sobrien 34660484Sobrien if (to_log) { 34760484Sobrien#ifdef ultrix 34860484Sobrien openlog("chat", LOG_PID); 34960484Sobrien#else 35089857Sobrien openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2); 35160484Sobrien 35260484Sobrien if (verbose) 35377298Sobrien setlogmask(LOG_UPTO(LOG_INFO)); 35460484Sobrien else 35589857Sobrien setlogmask(LOG_UPTO(LOG_WARNING)); 35689857Sobrien#endif 35789857Sobrien } 35860484Sobrien 35960484Sobrien init(); 36060484Sobrien 36160484Sobrien if (chat_file != NULL) { 36289857Sobrien arg = ARG(argc, argv); 36360484Sobrien if (arg != NULL) 36460484Sobrien usage(); 36589857Sobrien else 36660484Sobrien do_file (chat_file); 36760484Sobrien } else { 36860484Sobrien while ((arg = ARG(argc, argv)) != NULL) { 36989857Sobrien chat_expect(arg); 37089857Sobrien 37160484Sobrien if ((arg = ARG(argc, argv)) != NULL) 37260484Sobrien chat_send(arg); 37360484Sobrien } 37460484Sobrien } 37560484Sobrien 37660484Sobrien terminate(0); 37760484Sobrien return 0; 37889857Sobrien} 37989857Sobrien 38089857Sobrien/* 38160484Sobrien * Process a chat script when read from a file. 38277298Sobrien */ 38333965Sjdp 38477298Sobrienvoid do_file (chat_file) 38577298Sobrienchar *chat_file; 38677298Sobrien{ 38733965Sjdp int linect, sendflg; 38833965Sjdp char *sp, *arg, quote; 38933965Sjdp char buf [STR_LEN]; 39033965Sjdp FILE *cfp; 39133965Sjdp 39289857Sobrien cfp = fopen (chat_file, "r"); 39333965Sjdp if (cfp == NULL) 39433965Sjdp fatal(1, "%s -- open failed: %m", chat_file); 39533965Sjdp 39633965Sjdp linect = 0; 39733965Sjdp sendflg = 0; 39833965Sjdp 39933965Sjdp while (fgets(buf, STR_LEN, cfp) != NULL) { 40033965Sjdp sp = strchr (buf, '\n'); 40133965Sjdp if (sp) 40233965Sjdp *sp = '\0'; 40333965Sjdp 40433965Sjdp linect++; 40533965Sjdp sp = buf; 40633965Sjdp 40733965Sjdp /* lines starting with '#' are comments. If a real '#' 40833965Sjdp is to be expected, it should be quoted .... */ 40933965Sjdp if ( *sp == '#' ) 41033965Sjdp continue; 41133965Sjdp 41233965Sjdp while (*sp != '\0') { 41333965Sjdp if (*sp == ' ' || *sp == '\t') { 41433965Sjdp ++sp; 41533965Sjdp continue; 41633965Sjdp } 41733965Sjdp 41833965Sjdp if (*sp == '"' || *sp == '\'') { 41933965Sjdp quote = *sp++; 42033965Sjdp arg = sp; 42133965Sjdp while (*sp != quote) { 42233965Sjdp if (*sp == '\0') 42333965Sjdp fatal(1, "unterminated quote (line %d)", linect); 42433965Sjdp 42533965Sjdp if (*sp++ == '\\') { 42633965Sjdp if (*sp != '\0') 42733965Sjdp ++sp; 42833965Sjdp } 42933965Sjdp } 43033965Sjdp } 43133965Sjdp else { 43233965Sjdp arg = sp; 43333965Sjdp while (*sp != '\0' && *sp != ' ' && *sp != '\t') 43433965Sjdp ++sp; 43533965Sjdp } 43633965Sjdp 43733965Sjdp if (*sp != '\0') 43877298Sobrien *sp++ = '\0'; 43933965Sjdp 44033965Sjdp if (sendflg) 44133965Sjdp chat_send (arg); 44277298Sobrien else 44333965Sjdp chat_expect (arg); 44433965Sjdp sendflg = !sendflg; 44533965Sjdp } 44633965Sjdp } 44733965Sjdp fclose (cfp); 44833965Sjdp} 44933965Sjdp 45033965Sjdp/* 45177298Sobrien * We got an error parsing the command line. 45233965Sjdp */ 45377298Sobrienstatic void 45433965Sjdpusage() 45533965Sjdp{ 45633965Sjdp fprintf(stderr, "\ 45777298SobrienUsage: chat [-e] [-v] [-V] [-t timeout] [-r report-file] [-T phone-number]\n\ 45833965Sjdp [-U phone-number2] {-f chat-file | chat-script}\n"); 45933965Sjdp exit(1); 46033965Sjdp} 46133965Sjdp 46233965Sjdpchar line[1024]; 46333965Sjdp 46433965Sjdp/* 46533965Sjdp * Send a message to syslog and/or stderr. 46633965Sjdp */ 46733965Sjdpvoid logf __V((const char *fmt, ...)) 46877298Sobrien{ 46977298Sobrien va_list args; 47077298Sobrien 47133965Sjdp#ifdef __STDC__ 47277298Sobrien va_start(args, fmt); 47377298Sobrien#else 47478828Sobrien char *fmt; 47577298Sobrien va_start(args); 47633965Sjdp fmt = va_arg(args, char *); 47733965Sjdp#endif 47833965Sjdp 47977298Sobrien vfmtmsg(line, sizeof(line), fmt, args); 48033965Sjdp if (to_log) 48177298Sobrien syslog(LOG_INFO, "%s", line); 48233965Sjdp if (to_stderr) 48333965Sjdp fprintf(stderr, "%s\n", line); 48433965Sjdp} 48533965Sjdp 48633965Sjdp/* 48733965Sjdp * Print an error message and terminate. 48833965Sjdp */ 48933965Sjdp 49033965Sjdpvoid fatal __V((int code, const char *fmt, ...)) 49133965Sjdp{ 49233965Sjdp va_list args; 49333965Sjdp 49433965Sjdp#ifdef __STDC__ 49533965Sjdp va_start(args, fmt); 49633965Sjdp#else 49733965Sjdp int code; 49833965Sjdp char *fmt; 49933965Sjdp va_start(args); 50033965Sjdp code = va_arg(args, int); 50133965Sjdp fmt = va_arg(args, char *); 50233965Sjdp#endif 50333965Sjdp 50433965Sjdp vfmtmsg(line, sizeof(line), fmt, args); 50533965Sjdp if (to_log) 50633965Sjdp syslog(LOG_ERR, "%s", line); 50733965Sjdp if (to_stderr) 50833965Sjdp fprintf(stderr, "%s\n", line); 50933965Sjdp terminate(code); 51033965Sjdp} 51133965Sjdp 51233965Sjdpint alarmed = 0; 51333965Sjdp 51433965SjdpSIGTYPE sigalrm(signo) 51533965Sjdpint signo; 51633965Sjdp{ 51733965Sjdp int flags; 51833965Sjdp 51933965Sjdp alarm(1); 52033965Sjdp alarmed = 1; /* Reset alarm to avoid race window */ 52133965Sjdp signal(SIGALRM, sigalrm); /* that can cause hanging in read() */ 52233965Sjdp 52333965Sjdp if ((flags = fcntl(0, F_GETFL, 0)) == -1) 52433965Sjdp fatal(2, "Can't get file mode flags on stdin: %m"); 52533965Sjdp 52633965Sjdp if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1) 52733965Sjdp fatal(2, "Can't set file mode flags on stdin: %m"); 52833965Sjdp 52933965Sjdp if (verbose) 53033965Sjdp logf("alarm"); 53133965Sjdp} 53233965Sjdp 53333965Sjdpvoid unalarm() 53433965Sjdp{ 53533965Sjdp int flags; 53633965Sjdp 53733965Sjdp if ((flags = fcntl(0, F_GETFL, 0)) == -1) 53833965Sjdp fatal(2, "Can't get file mode flags on stdin: %m"); 53933965Sjdp 54033965Sjdp if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1) 54133965Sjdp fatal(2, "Can't set file mode flags on stdin: %m"); 54233965Sjdp} 54333965Sjdp 54433965SjdpSIGTYPE sigint(signo) 54533965Sjdpint signo; 54633965Sjdp{ 54733965Sjdp fatal(2, "SIGINT"); 54833965Sjdp} 54933965Sjdp 55033965SjdpSIGTYPE sigterm(signo) 55133965Sjdpint signo; 55233965Sjdp{ 55333965Sjdp fatal(2, "SIGTERM"); 55433965Sjdp} 55533965Sjdp 55633965SjdpSIGTYPE sighup(signo) 55733965Sjdpint signo; 55833965Sjdp{ 55933965Sjdp fatal(2, "SIGHUP"); 56033965Sjdp} 56133965Sjdp 56233965Sjdpvoid init() 56333965Sjdp{ 56433965Sjdp signal(SIGINT, sigint); 56533965Sjdp signal(SIGTERM, sigterm); 56677298Sobrien signal(SIGHUP, sighup); 56733965Sjdp 56877298Sobrien set_tty_parameters(); 56933965Sjdp signal(SIGALRM, sigalrm); 57033965Sjdp alarm(0); 57133965Sjdp alarmed = 0; 57233965Sjdp} 57333965Sjdp 57477298Sobrienvoid set_tty_parameters() 57577298Sobrien{ 57633965Sjdp#if defined(get_term_param) 57733965Sjdp term_parms t; 57833965Sjdp 57933965Sjdp if (get_term_param (&t) < 0) 58033965Sjdp fatal(2, "Can't get terminal parameters: %m"); 58133965Sjdp 58233965Sjdp saved_tty_parameters = t; 58333965Sjdp have_tty_parameters = 1; 58433965Sjdp 58533965Sjdp t.c_iflag |= IGNBRK | ISTRIP | IGNPAR; 58633965Sjdp t.c_oflag = 0; 58733965Sjdp t.c_lflag = 0; 58833965Sjdp t.c_cc[VERASE] = 58933965Sjdp t.c_cc[VKILL] = 0; 59033965Sjdp t.c_cc[VMIN] = 1; 59177298Sobrien t.c_cc[VTIME] = 0; 59277298Sobrien 59333965Sjdp if (set_term_param (&t) < 0) 59433965Sjdp fatal(2, "Can't set terminal parameters: %m"); 59533965Sjdp#endif 59633965Sjdp} 59733965Sjdp 59833965Sjdpvoid break_sequence() 59977298Sobrien{ 60077298Sobrien#ifdef TERMIOS 60177298Sobrien tcsendbreak (0, 0); 60233965Sjdp#endif 60377298Sobrien} 60433965Sjdp 60577298Sobrienvoid terminate(status) 60677298Sobrienint status; 60733965Sjdp{ 60833965Sjdp echo_stderr(-1); 60933965Sjdp if (report_file != (char *) 0 && report_fp != (FILE *) NULL) { 61033965Sjdp/* 61133965Sjdp * Allow the last of the report string to be gathered before we terminate. 61233965Sjdp */ 61377298Sobrien if (report_gathering) { 61433965Sjdp int c, rep_len; 61538889Sjdp 61633965Sjdp rep_len = strlen(report_buffer); 61733965Sjdp while (rep_len + 1 <= sizeof(report_buffer)) { 61833965Sjdp alarm(1); 61933965Sjdp c = get_char(); 62033965Sjdp alarm(0); 62133965Sjdp if (c < 0 || iscntrl(c)) 62233965Sjdp break; 62333965Sjdp report_buffer[rep_len] = c; 62433965Sjdp ++rep_len; 62533965Sjdp } 62633965Sjdp report_buffer[rep_len] = 0; 62733965Sjdp fprintf (report_fp, "chat: %s\n", report_buffer); 62833965Sjdp } 62933965Sjdp if (verbose) 63033965Sjdp fprintf (report_fp, "Closing \"%s\".\n", report_file); 63133965Sjdp fclose (report_fp); 63233965Sjdp report_fp = (FILE *) NULL; 63333965Sjdp } 63433965Sjdp 63533965Sjdp#if defined(get_term_param) 63633965Sjdp if (have_tty_parameters) { 63733965Sjdp if (set_term_param (&saved_tty_parameters) < 0) 63833965Sjdp fatal(2, "Can't restore terminal parameters: %m"); 63933965Sjdp } 64033965Sjdp#endif 64133965Sjdp 64233965Sjdp exit(status); 64333965Sjdp} 64433965Sjdp 64533965Sjdp/* 64633965Sjdp * 'Clean up' this string. 64778828Sobrien */ 64833965Sjdpchar *clean(s, sending) 64933965Sjdpregister char *s; 65033965Sjdpint sending; /* set to 1 when sending (putting) this string. */ 65133965Sjdp{ 65233965Sjdp char temp[STR_LEN], cur_chr; 65338889Sjdp register char *s1, *phchar; 65438889Sjdp int add_return = sending; 65577298Sobrien#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7')) 65633965Sjdp 65733965Sjdp s1 = temp; 65833965Sjdp /* Don't overflow buffer, leave room for chars we append later */ 65933965Sjdp while (*s && s1 - temp < sizeof(temp) - 2 - add_return) { 66033965Sjdp cur_chr = *s++; 66133965Sjdp if (cur_chr == '^') { 66233965Sjdp cur_chr = *s++; 66377298Sobrien if (cur_chr == '\0') { 66438889Sjdp *s1++ = '^'; 66538889Sjdp break; 66638889Sjdp } 66738889Sjdp cur_chr &= 0x1F; 66838889Sjdp if (cur_chr != 0) { 66938889Sjdp *s1++ = cur_chr; 67038889Sjdp } 67138889Sjdp continue; 67238889Sjdp } 67338889Sjdp 67438889Sjdp if (cur_chr != '\\') { 67538889Sjdp *s1++ = cur_chr; 67638889Sjdp continue; 67738889Sjdp } 67838889Sjdp 67938889Sjdp cur_chr = *s++; 68038889Sjdp if (cur_chr == '\0') { 68138889Sjdp if (sending) { 68238889Sjdp *s1++ = '\\'; 68338889Sjdp *s1++ = '\\'; 68438889Sjdp } 68538889Sjdp break; 68638889Sjdp } 68738889Sjdp 68833965Sjdp switch (cur_chr) { 68933965Sjdp case 'b': 69077298Sobrien *s1++ = '\b'; 69133965Sjdp break; 69233965Sjdp 69333965Sjdp case 'c': 69433965Sjdp if (sending && *s == '\0') 69533965Sjdp add_return = 0; 69633965Sjdp else 69733965Sjdp *s1++ = cur_chr; 69833965Sjdp break; 69933965Sjdp 70033965Sjdp case '\\': 70133965Sjdp case 'K': 70233965Sjdp case 'p': 70333965Sjdp case 'd': 70433965Sjdp if (sending) 70533965Sjdp *s1++ = '\\'; 70633965Sjdp 70733965Sjdp *s1++ = cur_chr; 70833965Sjdp break; 70933965Sjdp 71077298Sobrien case 'T': 71133965Sjdp if (sending && phone_num) { 71233965Sjdp for ( phchar = phone_num; *phchar != '\0'; phchar++) 71333965Sjdp *s1++ = *phchar; 71433965Sjdp } 71533965Sjdp else { 71633965Sjdp *s1++ = '\\'; 71733965Sjdp *s1++ = 'T'; 71833965Sjdp } 71933965Sjdp break; 72033965Sjdp 72160484Sobrien case 'U': 72233965Sjdp if (sending && phone_num2) { 72333965Sjdp for ( phchar = phone_num2; *phchar != '\0'; phchar++) 72433965Sjdp *s1++ = *phchar; 72533965Sjdp } 72633965Sjdp else { 72733965Sjdp *s1++ = '\\'; 72833965Sjdp *s1++ = 'U'; 72933965Sjdp } 73033965Sjdp break; 73133965Sjdp 73233965Sjdp case 'q': 73377298Sobrien quiet = 1; 73433965Sjdp break; 73533965Sjdp 73633965Sjdp case 'r': 73733965Sjdp *s1++ = '\r'; 73833965Sjdp break; 73933965Sjdp 74033965Sjdp case 'n': 74133965Sjdp *s1++ = '\n'; 74233965Sjdp break; 74333965Sjdp 74433965Sjdp case 's': 74533965Sjdp *s1++ = ' '; 74633965Sjdp break; 74738889Sjdp 74838889Sjdp case 't': 74938889Sjdp *s1++ = '\t'; 75038889Sjdp break; 75138889Sjdp 75238889Sjdp case 'N': 75338889Sjdp if (sending) { 75438889Sjdp *s1++ = '\\'; 75538889Sjdp *s1++ = '\0'; 75638889Sjdp } 75738889Sjdp else 75838889Sjdp *s1++ = 'N'; 75938889Sjdp break; 76038889Sjdp 76138889Sjdp default: 76238889Sjdp if (isoctal (cur_chr)) { 76338889Sjdp cur_chr &= 0x07; 76438889Sjdp if (isoctal (*s)) { 76538889Sjdp cur_chr <<= 3; 76638889Sjdp cur_chr |= *s++ - '0'; 76733965Sjdp if (isoctal (*s)) { 76833965Sjdp cur_chr <<= 3; 76933965Sjdp cur_chr |= *s++ - '0'; 77033965Sjdp } 77133965Sjdp } 77260484Sobrien 77338889Sjdp if (cur_chr != 0 || sending) { 77460484Sobrien if (sending && (cur_chr == '\\' || cur_chr == 0)) 77533965Sjdp *s1++ = '\\'; 77633965Sjdp *s1++ = cur_chr; 77733965Sjdp } 77833965Sjdp break; 77933965Sjdp } 78033965Sjdp 78133965Sjdp if (sending) 78233965Sjdp *s1++ = '\\'; 78333965Sjdp *s1++ = cur_chr; 78433965Sjdp break; 78533965Sjdp } 78633965Sjdp } 78733965Sjdp 78833965Sjdp if (add_return) 78933965Sjdp *s1++ = '\r'; 79033965Sjdp 79133965Sjdp *s1++ = '\0'; /* guarantee closure */ 79233965Sjdp *s1++ = '\0'; /* terminate the string */ 79333965Sjdp return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */ 79438889Sjdp} 79538889Sjdp 79638889Sjdp/* 79738889Sjdp * A modified version of 'strtok'. This version skips \ sequences. 79838889Sjdp */ 79938889Sjdp 80038889Sjdpchar *expect_strtok (s, term) 80138889Sjdp char *s, *term; 80238889Sjdp{ 80338889Sjdp static char *str = ""; 80438889Sjdp int escape_flag = 0; 80538889Sjdp char *result; 80638889Sjdp 80738889Sjdp/* 80838889Sjdp * If a string was specified then do initial processing. 80938889Sjdp */ 81038889Sjdp if (s) 81138889Sjdp str = s; 81238889Sjdp 81338889Sjdp/* 81433965Sjdp * If this is the escape flag then reset it and ignore the character. 81533965Sjdp */ 81660484Sobrien if (*str) 81733965Sjdp result = str; 81833965Sjdp else 81933965Sjdp result = (char *) 0; 82033965Sjdp 82133965Sjdp while (*str) { 82233965Sjdp if (escape_flag) { 82333965Sjdp escape_flag = 0; 82433965Sjdp ++str; 82533965Sjdp continue; 82633965Sjdp } 82733965Sjdp 82833965Sjdp if (*str == '\\') { 82933965Sjdp ++str; 83033965Sjdp escape_flag = 1; 83133965Sjdp continue; 83233965Sjdp } 83389857Sobrien 83433965Sjdp/* 83533965Sjdp * If this is not in the termination string, continue. 83633965Sjdp */ 83733965Sjdp if (strchr (term, *str) == (char *) 0) { 83833965Sjdp ++str; 83933965Sjdp continue; 84033965Sjdp } 84133965Sjdp 84233965Sjdp/* 84360484Sobrien * This is the terminator. Mark the end of the string and stop. 84433965Sjdp */ 84533965Sjdp *str++ = '\0'; 84633965Sjdp break; 84733965Sjdp } 84877298Sobrien return (result); 84977298Sobrien} 85033965Sjdp 85133965Sjdp/* 85233965Sjdp * Process the expect string 85333965Sjdp */ 85433965Sjdp 85533965Sjdpvoid chat_expect (s) 85633965Sjdpchar *s; 85733965Sjdp{ 85833965Sjdp char *expect; 85933965Sjdp char *reply; 86033965Sjdp 86133965Sjdp if (strcmp(s, "HANGUP") == 0) { 86233965Sjdp ++hup_next; 86333965Sjdp return; 86433965Sjdp } 86533965Sjdp 86633965Sjdp if (strcmp(s, "ABORT") == 0) { 86733965Sjdp ++abort_next; 86833965Sjdp return; 86933965Sjdp } 87033965Sjdp 87133965Sjdp if (strcmp(s, "CLR_ABORT") == 0) { 87233965Sjdp ++clear_abort_next; 87333965Sjdp return; 87433965Sjdp } 87533965Sjdp 87633965Sjdp if (strcmp(s, "REPORT") == 0) { 87733965Sjdp ++report_next; 87833965Sjdp return; 87933965Sjdp } 88033965Sjdp 88133965Sjdp if (strcmp(s, "CLR_REPORT") == 0) { 88233965Sjdp ++clear_report_next; 88333965Sjdp return; 88433965Sjdp } 88533965Sjdp 88633965Sjdp if (strcmp(s, "TIMEOUT") == 0) { 88733965Sjdp ++timeout_next; 88833965Sjdp return; 88933965Sjdp } 89033965Sjdp 89133965Sjdp if (strcmp(s, "ECHO") == 0) { 89233965Sjdp ++echo_next; 89333965Sjdp return; 89433965Sjdp } 89533965Sjdp 89633965Sjdp if (strcmp(s, "SAY") == 0) { 89733965Sjdp ++say_next; 89833965Sjdp return; 89933965Sjdp } 90033965Sjdp 90133965Sjdp/* 90233965Sjdp * Fetch the expect and reply string. 90333965Sjdp */ 90433965Sjdp for (;;) { 90533965Sjdp expect = expect_strtok (s, "-"); 90633965Sjdp s = (char *) 0; 90760484Sobrien 90833965Sjdp if (expect == (char *) 0) 90960484Sobrien return; 91060484Sobrien 91160484Sobrien reply = expect_strtok (s, "-"); 91260484Sobrien 91360484Sobrien/* 91460484Sobrien * Handle the expect string. If successful then exit. 91560484Sobrien */ 91633965Sjdp if (get_string (expect)) 91760484Sobrien return; 91860484Sobrien 91960484Sobrien/* 92060484Sobrien * If there is a sub-reply string then send it. Otherwise any condition 92160484Sobrien * is terminal. 92277298Sobrien */ 92360484Sobrien if (reply == (char *) 0 || exit_code != 3) 92460484Sobrien break; 92560484Sobrien 92660484Sobrien chat_send (reply); 92760484Sobrien } 92877298Sobrien 92960484Sobrien/* 93060484Sobrien * The expectation did not occur. This is terminal. 93160484Sobrien */ 93260484Sobrien if (fail_reason) 93360484Sobrien logf("Failed (%s)", fail_reason); 93460484Sobrien else 93560484Sobrien logf("Failed"); 93660484Sobrien terminate(exit_code); 93760484Sobrien} 93833965Sjdp 93933965Sjdp/* 94033965Sjdp * Translate the input character to the appropriate string for printing 94133965Sjdp * the data. 94233965Sjdp */ 94333965Sjdp 94433965Sjdpchar *character(c) 94533965Sjdpint c; 94633965Sjdp{ 94760484Sobrien static char string[10]; 94860484Sobrien char *meta; 94933965Sjdp 95033965Sjdp meta = (c & 0x80) ? "M-" : ""; 95133965Sjdp c &= 0x7F; 95233965Sjdp 95333965Sjdp if (c < 32) 95433965Sjdp sprintf(string, "%s^%c", meta, (int)c + '@'); 95533965Sjdp else if (c == 127) 95633965Sjdp sprintf(string, "%s^?", meta); 95733965Sjdp else 95833965Sjdp sprintf(string, "%s%c", meta, c); 95933965Sjdp 96033965Sjdp return (string); 96133965Sjdp} 96233965Sjdp 96333965Sjdp/* 96477298Sobrien * process the reply string 96560484Sobrien */ 96660484Sobrienvoid chat_send (s) 96760484Sobrienregister char *s; 96860484Sobrien{ 96960484Sobrien if (say_next) { 97060484Sobrien say_next = 0; 97177298Sobrien s = clean(s,0); 97260484Sobrien write(2, s, strlen(s)); 97360484Sobrien free(s); 97460484Sobrien return; 97560484Sobrien } 97660484Sobrien 97777298Sobrien if (hup_next) { 97860484Sobrien hup_next = 0; 97960484Sobrien if (strcmp(s, "OFF") == 0) 98060484Sobrien signal(SIGHUP, SIG_IGN); 98133965Sjdp else 98233965Sjdp signal(SIGHUP, sighup); 98377298Sobrien return; 98460484Sobrien } 98560484Sobrien 98660484Sobrien if (echo_next) { 98777298Sobrien echo_next = 0; 98833965Sjdp echo = (strcmp(s, "ON") == 0); 98960484Sobrien return; 99060484Sobrien } 99160484Sobrien 99233965Sjdp if (abort_next) { 99333965Sjdp char *s1; 99433965Sjdp 99533965Sjdp abort_next = 0; 99633965Sjdp 99733965Sjdp if (n_aborts >= MAX_ABORTS) 99833965Sjdp fatal(2, "Too many ABORT strings"); 99933965Sjdp 100033965Sjdp s1 = clean(s, 0); 100133965Sjdp 100233965Sjdp if (strlen(s1) > strlen(s) 100333965Sjdp || strlen(s1) + 1 > sizeof(fail_buffer)) 100460484Sobrien fatal(1, "Illegal or too-long ABORT string ('%v')", s); 100560484Sobrien 100660484Sobrien abort_string[n_aborts++] = s1; 100760484Sobrien 100860484Sobrien if (verbose) 100960484Sobrien logf("abort on (%v)", s); 101033965Sjdp return; 101133965Sjdp } 101233965Sjdp 101333965Sjdp if (clear_abort_next) { 101433965Sjdp char *s1; 101533965Sjdp int i; 101633965Sjdp int old_max; 101733965Sjdp int pack = 0; 101833965Sjdp 101933965Sjdp clear_abort_next = 0; 102033965Sjdp 102133965Sjdp s1 = clean(s, 0); 102260484Sobrien 102333965Sjdp if (strlen(s1) > strlen(s) 102433965Sjdp || strlen(s1) + 1 > sizeof(fail_buffer)) 102533965Sjdp fatal(1, "Illegal or too-long CLR_ABORT string ('%v')", s); 102633965Sjdp 102789857Sobrien old_max = n_aborts; 102889857Sobrien for (i=0; i < n_aborts; i++) { 102989857Sobrien if ( strcmp(s1,abort_string[i]) == 0 ) { 103033965Sjdp free(abort_string[i]); 103133965Sjdp abort_string[i] = NULL; 103233965Sjdp pack++; 103333965Sjdp n_aborts--; 103433965Sjdp if (verbose) 103533965Sjdp logf("clear abort on (%v)", s); 103660484Sobrien } 103760484Sobrien } 103833965Sjdp free(s1); 103977298Sobrien if (pack) 104060484Sobrien pack_array(abort_string,old_max); 104160484Sobrien return; 104233965Sjdp } 104360484Sobrien 104460484Sobrien if (report_next) { 104533965Sjdp char *s1; 104660484Sobrien 104760484Sobrien report_next = 0; 104860484Sobrien if (n_reports >= MAX_REPORTS) 104960484Sobrien fatal(2, "Too many REPORT strings"); 105060484Sobrien 105160484Sobrien s1 = clean(s, 0); 105260484Sobrien 105360484Sobrien if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 105460484Sobrien fatal(1, "Illegal or too-long REPORT string ('%v')", s); 105560484Sobrien 105677298Sobrien report_string[n_reports++] = s1; 105760484Sobrien 105860484Sobrien if (verbose) 105960484Sobrien logf("report (%v)", s); 106060484Sobrien return; 106160484Sobrien } 106260484Sobrien 106360484Sobrien if (clear_report_next) { 106460484Sobrien char *s1; 106560484Sobrien int i; 106660484Sobrien int old_max; 106760484Sobrien int pack = 0; 106860484Sobrien 106960484Sobrien clear_report_next = 0; 107060484Sobrien 107160484Sobrien s1 = clean(s, 0); 107260484Sobrien 107360484Sobrien if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1) 107460484Sobrien fatal(1, "Illegal or too-long REPORT string ('%v')", s); 107560484Sobrien 107660484Sobrien old_max = n_reports; 107760484Sobrien for (i=0; i < n_reports; i++) { 107833965Sjdp if ( strcmp(s1,report_string[i]) == 0 ) { 107933965Sjdp free(report_string[i]); 108033965Sjdp report_string[i] = NULL; 108133965Sjdp pack++; 108233965Sjdp n_reports--; 108333965Sjdp if (verbose) 108433965Sjdp logf("clear report (%v)", s); 108538889Sjdp } 108638889Sjdp } 108738889Sjdp free(s1); 108889857Sobrien if (pack) 108938889Sjdp pack_array(report_string,old_max); 109033965Sjdp 109133965Sjdp return; 109233965Sjdp } 109333965Sjdp 109433965Sjdp if (timeout_next) { 109533965Sjdp timeout_next = 0; 109633965Sjdp timeout = atoi(s); 109733965Sjdp 109833965Sjdp if (timeout <= 0) 109933965Sjdp timeout = DEFAULT_CHAT_TIMEOUT; 110033965Sjdp 110133965Sjdp if (verbose) 110233965Sjdp logf("timeout set to %d seconds", timeout); 110333965Sjdp 110433965Sjdp return; 110533965Sjdp } 110633965Sjdp 110733965Sjdp if (strcmp(s, "EOT") == 0) 110833965Sjdp s = "^D\\c"; 110933965Sjdp else if (strcmp(s, "BREAK") == 0) 111089857Sobrien s = "\\K\\c"; 111133965Sjdp 111233965Sjdp if (!put_string(s)) 111333965Sjdp fatal(1, "Failed"); 111433965Sjdp} 111533965Sjdp 111633965Sjdpint get_char() 111733965Sjdp{ 111833965Sjdp int status; 111933965Sjdp char c; 112033965Sjdp 112133965Sjdp status = read(0, &c, 1); 112233965Sjdp 112333965Sjdp switch (status) { 112433965Sjdp case 1: 112533965Sjdp return ((int)c & 0x7F); 112633965Sjdp 112733965Sjdp default: 112833965Sjdp logf("warning: read() on stdin returned %d", status); 112933965Sjdp 113033965Sjdp case -1: 113133965Sjdp if ((status = fcntl(0, F_GETFL, 0)) == -1) 113233965Sjdp fatal(2, "Can't get file mode flags on stdin: %m"); 113333965Sjdp 113433965Sjdp if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 113533965Sjdp fatal(2, "Can't set file mode flags on stdin: %m"); 113633965Sjdp 113733965Sjdp return (-1); 113833965Sjdp } 113933965Sjdp} 114033965Sjdp 114133965Sjdpint put_char(c) 114233965Sjdpint c; 114333965Sjdp{ 114433965Sjdp int status; 114533965Sjdp char ch = c; 114633965Sjdp 114733965Sjdp usleep(10000); /* inter-character typing delay (?) */ 114833965Sjdp 114933965Sjdp status = write(1, &ch, 1); 115033965Sjdp 115133965Sjdp switch (status) { 115238889Sjdp case 1: 115333965Sjdp return (0); 115438889Sjdp 115533965Sjdp default: 115633965Sjdp logf("warning: write() on stdout returned %d", status); 115778828Sobrien 115833965Sjdp case -1: 115978828Sobrien if ((status = fcntl(0, F_GETFL, 0)) == -1) 116078828Sobrien fatal(2, "Can't get file mode flags on stdin, %m"); 116178828Sobrien 116277298Sobrien if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1) 116333965Sjdp fatal(2, "Can't set file mode flags on stdin: %m"); 116433965Sjdp 116533965Sjdp return (-1); 116633965Sjdp } 116733965Sjdp} 116833965Sjdp 116938889Sjdpint write_char (c) 117038889Sjdpint c; 117133965Sjdp{ 117233965Sjdp if (alarmed || put_char(c) < 0) { 117333965Sjdp alarm(0); 117433965Sjdp alarmed = 0; 117533965Sjdp 117638889Sjdp if (verbose) { 117733965Sjdp if (errno == EINTR || errno == EWOULDBLOCK) 117838889Sjdp logf(" -- write timed out"); 117933965Sjdp else 118038889Sjdp logf(" -- write failed: %m"); 118138889Sjdp } 118238889Sjdp return (0); 118338889Sjdp } 118433965Sjdp return (1); 118538889Sjdp} 118638889Sjdp 118738889Sjdpint put_string (s) 118838889Sjdpregister char *s; 118938889Sjdp{ 119038889Sjdp quiet = 0; 119138889Sjdp s = clean(s, 1); 119238889Sjdp 119338889Sjdp if (verbose) { 119438889Sjdp if (quiet) 119589857Sobrien logf("send (??????)"); 119689857Sobrien else 119789857Sobrien logf("send (%v)", s); 119889857Sobrien } 119989857Sobrien 120089857Sobrien alarm(timeout); alarmed = 0; 120189857Sobrien 120289857Sobrien while (*s) { 120389857Sobrien register char c = *s++; 120489857Sobrien 120589857Sobrien if (c != '\\') { 120638889Sjdp if (!write_char (c)) 120738889Sjdp return 0; 120889857Sobrien continue; 120989857Sobrien } 121089857Sobrien 121138889Sjdp c = *s++; 121238889Sjdp switch (c) { 121338889Sjdp case 'd': 121438889Sjdp sleep(1); 121538889Sjdp break; 121633965Sjdp 121733965Sjdp case 'K': 121833965Sjdp break_sequence(); 121933965Sjdp break; 122033965Sjdp 122133965Sjdp case 'p': 122233965Sjdp usleep(10000); /* 1/100th of a second (arg is microseconds) */ 122333965Sjdp break; 122433965Sjdp 122533965Sjdp default: 122633965Sjdp if (!write_char (c)) 122733965Sjdp return 0; 122833965Sjdp break; 122933965Sjdp } 123033965Sjdp } 123133965Sjdp 123260484Sobrien alarm(0); 123360484Sobrien alarmed = 0; 123460484Sobrien return (1); 123560484Sobrien} 123633965Sjdp 123733965Sjdp/* 123833965Sjdp * Echo a character to stderr. 123933965Sjdp * When called with -1, a '\n' character is generated when 124033965Sjdp * the cursor is not at the beginning of a line. 124133965Sjdp */ 124277298Sobrienvoid echo_stderr(n) 124377298Sobrienint n; 124477298Sobrien{ 124577298Sobrien static int need_lf; 124677298Sobrien char *s; 124777298Sobrien 124877298Sobrien switch (n) { 124933965Sjdp case '\r': /* ignore '\r' */ 125033965Sjdp break; 125133965Sjdp case -1: 125260484Sobrien if (need_lf == 0) 125360484Sobrien break; 125460484Sobrien /* fall through */ 125560484Sobrien case '\n': 125633965Sjdp write(2, "\n", 1); 125760484Sobrien need_lf = 0; 125889857Sobrien break; 125960484Sobrien default: 126089857Sobrien s = character(n); 126133965Sjdp write(2, s, strlen(s)); 126260484Sobrien need_lf = 1; 126333965Sjdp break; 126460484Sobrien } 126560484Sobrien} 126660484Sobrien 126789857Sobrien/* 126860484Sobrien * 'Wait for' this string to appear on this file descriptor. 126960484Sobrien */ 127060484Sobrienint get_string(string) 127189857Sobrienregister char *string; 127233965Sjdp{ 127360484Sobrien char temp[STR_LEN]; 127433965Sjdp int c, printed = 0, len, minlen; 127560484Sobrien register char *s = temp, *end = s + STR_LEN; 127660484Sobrien char *logged = temp; 127760484Sobrien 127833965Sjdp fail_reason = (char *)0; 127960484Sobrien 128060484Sobrien if (strlen(string) > STR_LEN) { 128160484Sobrien logf("expect string is too long"); 128260484Sobrien exit_code = 1; 128333965Sjdp return 0; 128460484Sobrien } 128560484Sobrien 128660484Sobrien string = clean(string, 0); 128733965Sjdp len = strlen(string); 128860484Sobrien minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1; 128960484Sobrien 129060484Sobrien if (verbose) 129160484Sobrien logf("expect (%v)", string); 129233965Sjdp 129360484Sobrien if (len == 0) { 129460484Sobrien if (verbose) 129560484Sobrien logf("got it"); 129660484Sobrien return (1); 129760484Sobrien } 129860484Sobrien 129960484Sobrien alarm(timeout); 130060484Sobrien alarmed = 0; 130160484Sobrien 130260484Sobrien while ( ! alarmed && (c = get_char()) >= 0) { 130360484Sobrien int n, abort_len, report_len; 130433965Sjdp 130560484Sobrien if (echo) 130660484Sobrien echo_stderr(c); 130760484Sobrien if (verbose && c == '\n') { 130860484Sobrien if (s == logged) 130960484Sobrien logf(""); /* blank line */ 131060484Sobrien else 131133965Sjdp logf("%0.*v", s - logged, logged); 131233965Sjdp logged = s + 1; 131360484Sobrien } 131460484Sobrien 131560484Sobrien *s++ = c; 131633965Sjdp 131760484Sobrien if (verbose && s >= logged + 80) { 131860484Sobrien logf("%0.*v", s - logged, logged); 131960484Sobrien logged = s; 132060484Sobrien } 132160484Sobrien 132260484Sobrien if (Verbose) { 132360484Sobrien if (c == '\n') 132460484Sobrien fputc( '\n', stderr ); 132560484Sobrien else if (c != '\r') 132660484Sobrien fprintf( stderr, "%s", character(c) ); 132760484Sobrien } 132860484Sobrien 132960484Sobrien if (!report_gathering) { 133060484Sobrien for (n = 0; n < n_reports; ++n) { 133160484Sobrien if ((report_string[n] != (char*) NULL) && 133260484Sobrien s - temp >= (report_len = strlen(report_string[n])) && 133360484Sobrien strncmp(s - report_len, report_string[n], report_len) == 0) { 133460484Sobrien time_t time_now = time ((time_t*) NULL); 133533965Sjdp struct tm* tm_now = localtime (&time_now); 133633965Sjdp 133760484Sobrien strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now); 133860484Sobrien strcat (report_buffer, report_string[n]); 133960484Sobrien 134060484Sobrien report_string[n] = (char *) NULL; 134189857Sobrien report_gathering = 1; 134260484Sobrien break; 134360484Sobrien } 134460484Sobrien } 134560484Sobrien } 134660484Sobrien else { 134760484Sobrien if (!iscntrl (c)) { 134860484Sobrien int rep_len = strlen (report_buffer); 134933965Sjdp report_buffer[rep_len] = c; 135060484Sobrien report_buffer[rep_len + 1] = '\0'; 135160484Sobrien } 135233965Sjdp else { 135333965Sjdp report_gathering = 0; 135460484Sobrien fprintf (report_fp, "chat: %s\n", report_buffer); 135560484Sobrien } 135660484Sobrien } 135760484Sobrien 135889857Sobrien if (s - temp >= len && 135960484Sobrien c == string[len - 1] && 136089857Sobrien strncmp(s - len, string, len) == 0) { 136160484Sobrien if (verbose) { 136260484Sobrien if (s > logged) 136377298Sobrien logf("%0.*v", s - logged, logged); 136460484Sobrien logf(" -- got it\n"); 136560484Sobrien } 136677298Sobrien 136789857Sobrien alarm(0); 136889857Sobrien alarmed = 0; 136989857Sobrien return (1); 137089857Sobrien } 137160484Sobrien 137260484Sobrien for (n = 0; n < n_aborts; ++n) { 137360484Sobrien if (s - temp >= (abort_len = strlen(abort_string[n])) && 137460484Sobrien strncmp(s - abort_len, abort_string[n], abort_len) == 0) { 137577298Sobrien if (verbose) { 137689857Sobrien if (s > logged) 137777298Sobrien logf("%0.*v", s - logged, logged); 137860484Sobrien logf(" -- failed"); 137960484Sobrien } 138060484Sobrien 138160484Sobrien alarm(0); 138277298Sobrien alarmed = 0; 138360484Sobrien exit_code = n + 4; 138489857Sobrien strcpy(fail_reason = fail_buffer, abort_string[n]); 138589857Sobrien return (0); 138689857Sobrien } 138760484Sobrien } 138860484Sobrien 138960484Sobrien if (s >= end) { 139060484Sobrien if (logged < s - minlen) { 139177298Sobrien logf("%0.*v", s - logged, logged); 139260484Sobrien logged = s; 139389857Sobrien } 139489857Sobrien s -= minlen; 139589857Sobrien memmove(temp, s, minlen); 139677298Sobrien logged = temp + (logged - s); 139760484Sobrien s = temp + minlen; 139860484Sobrien } 139960484Sobrien 140060484Sobrien if (alarmed && verbose) 140189857Sobrien logf("warning: alarm synchronization problem"); 140277298Sobrien } 140360484Sobrien 140460484Sobrien alarm(0); 140589857Sobrien 140660484Sobrien if (verbose && printed) { 140777298Sobrien if (alarmed) 140889857Sobrien logf(" -- read timed out"); 140960484Sobrien else 141060484Sobrien logf(" -- read failed: %m"); 141160484Sobrien } 141260484Sobrien 141360484Sobrien exit_code = 3; 141433965Sjdp alarmed = 0; 141533965Sjdp return (0); 141633965Sjdp} 141733965Sjdp 141833965Sjdp/* 141933965Sjdp * Gross kludge to handle Solaris versions >= 2.6 having usleep. 142033965Sjdp */ 142133965Sjdp#ifdef SOL2 142233965Sjdp#include <sys/param.h> 142333965Sjdp#if MAXUID > 65536 /* then this is Solaris 2.6 or later */ 142433965Sjdp#undef NO_USLEEP 142533965Sjdp#endif 142633965Sjdp#endif /* SOL2 */ 142733965Sjdp 142833965Sjdp#ifdef NO_USLEEP 142933965Sjdp#include <sys/types.h> 143033965Sjdp#include <sys/time.h> 143133965Sjdp 143233965Sjdp/* 143333965Sjdp usleep -- support routine for 4.2BSD system call emulations 143433965Sjdp last edit: 29-Oct-1984 D A Gwyn 143533965Sjdp */ 143633965Sjdp 143733965Sjdpextern int select(); 143833965Sjdp 143933965Sjdpint 144033965Sjdpusleep( usec ) /* returns 0 if ok, else -1 */ 144133965Sjdp long usec; /* delay in microseconds */ 144233965Sjdp{ 144333965Sjdp static struct { /* `timeval' */ 144433965Sjdp long tv_sec; /* seconds */ 144533965Sjdp long tv_usec; /* microsecs */ 144633965Sjdp } delay; /* _select() timeout */ 144733965Sjdp 144889857Sobrien delay.tv_sec = usec / 1000000L; 144989857Sobrien delay.tv_usec = usec % 1000000L; 145033965Sjdp 145133965Sjdp return select(0, (long *)0, (long *)0, (long *)0, &delay); 145233965Sjdp} 145333965Sjdp#endif 145433965Sjdp 145533965Sjdpvoid 145689857Sobrienpack_array (array, end) 145733965Sjdp char **array; /* The address of the array of string pointers */ 145833965Sjdp int end; /* The index of the next free entry before CLR_ */ 145933965Sjdp{ 146033965Sjdp int i, j; 146133965Sjdp 146233965Sjdp for (i = 0; i < end; i++) { 146333965Sjdp if (array[i] == NULL) { 146489857Sobrien for (j = i+1; j < end; ++j) 146533965Sjdp if (array[j] != NULL) 146633965Sjdp array[i++] = array[j]; 146733965Sjdp for (; i < end; ++i) 146833965Sjdp array[i] = NULL; 146933965Sjdp break; 147033965Sjdp } 147133965Sjdp } 147233965Sjdp} 147389857Sobrien 147489857Sobrien/* 147533965Sjdp * vfmtmsg - format a message into a buffer. Like vsprintf except we 147678828Sobrien * also specify the length of the output buffer, and we handle the 147778828Sobrien * %m (error message) format. 147878828Sobrien * Doesn't do floating-point formats. 147989857Sobrien * Returns the number of chars put into buf. 148078828Sobrien */ 148133965Sjdp#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0) 148233965Sjdp 148333965Sjdpint 148433965Sjdpvfmtmsg(buf, buflen, fmt, args) 148560484Sobrien char *buf; 148660484Sobrien int buflen; 148733965Sjdp const char *fmt; 148833965Sjdp va_list args; 148933965Sjdp{ 149033965Sjdp int c, i, n; 149133965Sjdp int width, prec, fillch; 149233965Sjdp int base, len, neg, quoted; 149389857Sobrien unsigned long val = 0; 149489857Sobrien char *str, *buf0; 149589857Sobrien const char *f; 149689857Sobrien unsigned char *p; 149733965Sjdp char num[32]; 149833965Sjdp static char hexchars[] = "0123456789abcdef"; 149933965Sjdp 150033965Sjdp buf0 = buf; 150133965Sjdp --buflen; 150233965Sjdp while (buflen > 0) { 150333965Sjdp for (f = fmt; *f != '%' && *f != 0; ++f) 150433965Sjdp ; 150533965Sjdp if (f > fmt) { 150633965Sjdp len = f - fmt; 150733965Sjdp if (len > buflen) 150833965Sjdp len = buflen; 150933965Sjdp memcpy(buf, fmt, len); 151033965Sjdp buf += len; 151133965Sjdp buflen -= len; 151233965Sjdp fmt = f; 151389857Sobrien } 151433965Sjdp if (*fmt == 0) 151533965Sjdp break; 151660484Sobrien c = *++fmt; 151789857Sobrien width = prec = 0; 151860484Sobrien fillch = ' '; 151933965Sjdp if (c == '0') { 152033965Sjdp fillch = '0'; 152133965Sjdp c = *++fmt; 152233965Sjdp } 152333965Sjdp if (c == '*') { 152433965Sjdp width = va_arg(args, int); 152533965Sjdp c = *++fmt; 152633965Sjdp } else { 152733965Sjdp while (isdigit(c)) { 152833965Sjdp width = width * 10 + c - '0'; 152933965Sjdp c = *++fmt; 153033965Sjdp } 153133965Sjdp } 153233965Sjdp if (c == '.') { 153333965Sjdp c = *++fmt; 153433965Sjdp if (c == '*') { 153533965Sjdp prec = va_arg(args, int); 153633965Sjdp c = *++fmt; 153789857Sobrien } else { 153889857Sobrien while (isdigit(c)) { 153989857Sobrien prec = prec * 10 + c - '0'; 154089857Sobrien c = *++fmt; 154133965Sjdp } 154289857Sobrien } 154389857Sobrien } 154489857Sobrien str = 0; 154589857Sobrien base = 0; 154689857Sobrien neg = 0; 154733965Sjdp ++fmt; 154889857Sobrien switch (c) { 154989857Sobrien case 'd': 155089857Sobrien i = va_arg(args, int); 155189857Sobrien if (i < 0) { 155289857Sobrien neg = 1; 155389857Sobrien val = -i; 155433965Sjdp } else 155533965Sjdp val = i; 155633965Sjdp base = 10; 155789857Sobrien break; 155833965Sjdp case 'o': 155989857Sobrien val = va_arg(args, unsigned int); 156089857Sobrien base = 8; 156189857Sobrien break; 156289857Sobrien case 'x': 156333965Sjdp val = va_arg(args, unsigned int); 156433965Sjdp base = 16; 156589857Sobrien break; 156689857Sobrien case 'p': 156733965Sjdp val = (unsigned long) va_arg(args, void *); 156889857Sobrien base = 16; 156933965Sjdp neg = 2; 157033965Sjdp break; 157189857Sobrien case 's': 157289857Sobrien str = va_arg(args, char *); 157389857Sobrien break; 157460484Sobrien case 'c': 157533965Sjdp num[0] = va_arg(args, int); 157689857Sobrien num[1] = 0; 157733965Sjdp str = num; 157833965Sjdp break; 157989857Sobrien case 'm': 158089857Sobrien str = strerror(errno); 158189857Sobrien break; 158289857Sobrien case 'v': /* "visible" string */ 158389857Sobrien case 'q': /* quoted string */ 158433965Sjdp quoted = c == 'q'; 158533965Sjdp p = va_arg(args, unsigned char *); 158689857Sobrien if (fillch == '0' && prec > 0) { 158733965Sjdp n = prec; 158860484Sobrien } else { 158933965Sjdp n = strlen((char *)p); 159033965Sjdp if (prec > 0 && prec < n) 159189857Sobrien n = prec; 159233965Sjdp } 159389857Sobrien while (n > 0 && buflen > 0) { 159489857Sobrien c = *p++; 159589857Sobrien --n; 159633965Sjdp if (!quoted && c >= 0x80) { 159789857Sobrien OUTCHAR('M'); 159889857Sobrien OUTCHAR('-'); 159989857Sobrien c -= 0x80; 160089857Sobrien } 160189857Sobrien if (quoted && (c == '"' || c == '\\')) 160289857Sobrien OUTCHAR('\\'); 160389857Sobrien if (c < 0x20 || (0x7f <= c && c < 0xa0)) { 160489857Sobrien if (quoted) { 160533965Sjdp OUTCHAR('\\'); 160660484Sobrien switch (c) { 160760484Sobrien case '\t': OUTCHAR('t'); break; 160860484Sobrien case '\n': OUTCHAR('n'); break; 160977298Sobrien case '\b': OUTCHAR('b'); break; 161060484Sobrien case '\f': OUTCHAR('f'); break; 161160484Sobrien default: 161277298Sobrien OUTCHAR('x'); 161377298Sobrien OUTCHAR(hexchars[c >> 4]); 161460484Sobrien OUTCHAR(hexchars[c & 0xf]); 161577298Sobrien } 161677298Sobrien } else { 161760484Sobrien if (c == '\t') 161860484Sobrien OUTCHAR(c); 161960484Sobrien else { 162060484Sobrien OUTCHAR('^'); 162177298Sobrien OUTCHAR(c ^ 0x40); 162260484Sobrien } 162360484Sobrien } 162477298Sobrien } else 162577298Sobrien OUTCHAR(c); 162660484Sobrien } 162760484Sobrien continue; 162877298Sobrien default: 162977298Sobrien *buf++ = '%'; 163089857Sobrien if (c != '%') 163160484Sobrien --fmt; /* so %z outputs %z etc. */ 163277298Sobrien --buflen; 163360484Sobrien continue; 163460484Sobrien } 163560484Sobrien if (base != 0) { 163660484Sobrien str = num + sizeof(num); 163777298Sobrien *--str = 0; 163860484Sobrien while (str > num + neg) { 163960484Sobrien *--str = hexchars[val % base]; 164077298Sobrien val = val / base; 164177298Sobrien if (--prec <= 0 && val == 0) 164260484Sobrien break; 164360484Sobrien } 164477298Sobrien switch (neg) { 164560484Sobrien case 1: 164633965Sjdp *--str = '-'; 164777298Sobrien break; 164860484Sobrien case 2: 164977298Sobrien *--str = 'x'; 165077298Sobrien *--str = '0'; 165177298Sobrien break; 165277298Sobrien } 165333965Sjdp len = num + sizeof(num) - 1 - str; 165460484Sobrien } else { 165533965Sjdp len = strlen(str); 165660484Sobrien if (prec > 0 && len > prec) 165760484Sobrien len = prec; 165877298Sobrien } 165960484Sobrien if (width > 0) { 166060484Sobrien if (width > buflen) 166177298Sobrien width = buflen; 166277298Sobrien if ((n = width - len) > 0) { 166360484Sobrien buflen -= n; 166477298Sobrien for (; n > 0; --n) 166577298Sobrien *buf++ = fillch; 166677298Sobrien } 166777298Sobrien } 166860484Sobrien if (len > buflen) 166960484Sobrien len = buflen; 167060484Sobrien memcpy(buf, str, len); 167160484Sobrien buf += len; 167260484Sobrien buflen -= len; 167360484Sobrien } 167460484Sobrien *buf = 0; 167560484Sobrien return buf - buf0; 167660484Sobrien} 167760484Sobrien