chat.c revision 24541
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 condtion.
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 *	Please send all bug reports, requests for information, etc. to:
17 *
18 *		Al Longyear (longyear@netcom.com)
19 *		(I was the last person to change this code.)
20 *
21 *      Added -r "report file" switch & REPORT keyword.
22 *              Robert Geer <bgeer@xmission.com>
23 *
24 *	The original author is:
25 *
26 *		Karl Fox <karl@MorningStar.Com>
27 *		Morning Star Technologies, Inc.
28 *		1760 Zollinger Road
29 *		Columbus, OH  43221
30 *		(614)451-1883
31 *
32 */
33
34static char rcsid[] = "$Id: chat.c,v 1.6 1997/02/22 19:54:23 peter Exp $";
35
36#include <stdio.h>
37#include <time.h>
38#include <fcntl.h>
39#include <signal.h>
40#include <errno.h>
41#include <string.h>
42#include <stdlib.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <syslog.h>
46
47#ifndef TERMIO
48#undef	TERMIOS
49#define TERMIOS
50#endif
51
52#ifdef TERMIO
53#include <termio.h>
54#endif
55#ifdef TERMIOS
56#include <termios.h>
57#endif
58
59#define	STR_LEN	1024
60
61#ifndef SIGTYPE
62#define SIGTYPE void
63#endif
64
65#ifdef __STDC__
66#undef __P
67#define __P(x)	x
68#else
69#define __P(x)	()
70#define const
71#endif
72
73#ifndef O_NONBLOCK
74#define O_NONBLOCK	O_NDELAY
75#endif
76
77/*************** Micro getopt() *********************************************/
78#define	OPTION(c,v)	(_O&2&&**v?*(*v)++:!c||_O&4?0:(!(_O&1)&& \
79				(--c,++v),_O=4,c&&**v=='-'&&v[0][1]?*++*v=='-'\
80				&&!v[0][1]?(--c,++v,0):(_O=2,*(*v)++):0))
81#define	OPTARG(c,v)	(_O&2?**v||(++v,--c)?(_O=1,--c,*v++): \
82				(_O=4,(char*)0):(char*)0)
83#define	OPTONLYARG(c,v)	(_O&2&&**v?(_O=1,--c,*v++):(char*)0)
84#define	ARG(c,v)	(c?(--c,*v++):(char*)0)
85
86static int _O = 0;		/* Internal state */
87/*************** Micro getopt() *********************************************/
88
89char *program_name;
90
91#define	MAX_ABORTS		50
92#define	MAX_REPORTS		50
93#define	DEFAULT_CHAT_TIMEOUT	45
94
95int verbose       = 0;
96int quiet         = 0;
97int report        = 0;
98int exit_code     = 0;
99FILE* report_fp   = (FILE *) 0;
100char *report_file = (char *) 0;
101char *chat_file   = (char *) 0;
102int timeout       = DEFAULT_CHAT_TIMEOUT;
103
104int have_tty_parameters = 0;
105
106#ifdef TERMIO
107#define term_parms struct termio
108#define get_term_param(param) ioctl(0, TCGETA, param)
109#define set_term_param(param) ioctl(0, TCSETA, param)
110struct termio saved_tty_parameters;
111#endif
112
113#ifdef TERMIOS
114#define term_parms struct termios
115#define get_term_param(param) tcgetattr(0, param)
116#define set_term_param(param) tcsetattr(0, TCSANOW, param)
117struct termios saved_tty_parameters;
118#endif
119
120char *abort_string[MAX_ABORTS], *fail_reason = (char *)0,
121	fail_buffer[50];
122int n_aborts = 0, abort_next = 0, timeout_next = 0;
123
124char *report_string[MAX_REPORTS] ;
125char  report_buffer[50] ;
126int n_reports = 0, report_next = 0, report_gathering = 0 ;
127
128void *dup_mem __P((void *b, size_t c));
129void *copy_of __P((char *s));
130void usage __P((void));
131void logf __P((const char *str));
132void logflush __P((void));
133void fatal __P((const char *msg));
134void sysfatal __P((const char *msg));
135SIGTYPE sigalrm __P((int signo));
136SIGTYPE sigint __P((int signo));
137SIGTYPE sigterm __P((int signo));
138SIGTYPE sighup __P((int signo));
139void unalarm __P((void));
140void init __P((void));
141void set_tty_parameters __P((void));
142void break_sequence __P((void));
143void terminate __P((int status));
144void do_file __P((char *chat_file));
145int  get_string __P((register char *string));
146int  put_string __P((register char *s));
147int  write_char __P((int c));
148int  put_char __P((int c));
149int  get_char __P((void));
150void chat_send __P((register char *s));
151char *character __P((int c));
152void chat_expect __P((register char *s));
153char *clean __P((register char *s, int sending));
154void break_sequence __P((void));
155void terminate __P((int status));
156void die __P((void));
157
158void *dup_mem(b, c)
159void *b;
160size_t c;
161    {
162    void *ans = malloc (c);
163    if (!ans)
164        {
165	fatal ("memory error!\n");
166        }
167    memcpy (ans, b, c);
168    return ans;
169    }
170
171void *copy_of (s)
172char *s;
173    {
174    return dup_mem (s, strlen (s) + 1);
175    }
176
177/*
178 *	chat [ -v ] [ -t timeout ] [ -f chat-file ] [ -r report-file ] \
179 *		[...[[expect[-say[-expect...]] say expect[-say[-expect]] ...]]]
180 *
181 *	Perform a UUCP-dialer-like chat script on stdin and stdout.
182 */
183int
184main(argc, argv)
185int argc;
186char **argv;
187    {
188    int option;
189    char *arg;
190
191    program_name = *argv;
192    tzset();
193
194    while (option = OPTION(argc, argv))
195        {
196	switch (option)
197	    {
198	    case 'v':
199		++verbose;
200		break;
201
202	    case 'f':
203		if (arg = OPTARG(argc, argv))
204		    {
205		    chat_file = copy_of(arg);
206		    }
207		else
208		    {
209		    usage();
210		    }
211		break;
212
213	    case 't':
214		if (arg = OPTARG(argc, argv))
215		    {
216		    timeout = atoi(arg);
217		    }
218		else
219		    {
220		    usage();
221		    }
222		break;
223
224	    case 'r':
225		arg = OPTARG (argc, argv);
226		if (arg)
227		    {
228		    if (report_fp != NULL)
229		        {
230			fclose (report_fp);
231		        }
232		    report_file = copy_of (arg);
233		    report_fp   = fopen (report_file, "a");
234		    if (report_fp != NULL)
235		        {
236			if (verbose)
237			    {
238			    fprintf (report_fp, "Opening \"%s\"...\n",
239				     report_file);
240			    }
241			report = 1;
242		        }
243		    }
244		break;
245
246	    default:
247		usage();
248		break;
249	    }
250      }
251/*
252 * Default the report file to the stderr location
253 */
254    if (report_fp == NULL)
255        {
256	report_fp = stderr;
257        }
258
259#ifdef ultrix
260    openlog("chat", LOG_PID);
261#else
262    openlog("chat", LOG_PID | LOG_NDELAY, LOG_LOCAL2);
263
264    if (verbose)
265        {
266	setlogmask(LOG_UPTO(LOG_INFO));
267        }
268    else
269        {
270	setlogmask(LOG_UPTO(LOG_WARNING));
271        }
272#endif
273
274    init();
275
276    if (chat_file != NULL)
277	{
278	arg = ARG(argc, argv);
279	if (arg != NULL)
280	    {
281	    usage();
282	    }
283	else
284	    {
285	    do_file (chat_file);
286	    }
287	}
288    else
289	{
290	while (arg = ARG(argc, argv))
291	    {
292	    chat_expect(arg);
293
294	    if (arg = ARG(argc, argv))
295	        {
296		chat_send(arg);
297	        }
298	    }
299	}
300
301    terminate(0);
302    }
303
304/*
305 *  Process a chat script when read from a file.
306 */
307
308void do_file (chat_file)
309char *chat_file;
310    {
311    int linect, len, sendflg;
312    char *sp, *arg, quote;
313    char buf [STR_LEN];
314    FILE *cfp;
315
316    cfp = fopen (chat_file, "r");
317    if (cfp == NULL)
318	{
319	syslog (LOG_ERR, "%s -- open failed: %m", chat_file);
320	terminate (1);
321	}
322
323    linect = 0;
324    sendflg = 0;
325
326    while (fgets(buf, STR_LEN, cfp) != NULL)
327	{
328	sp = strchr (buf, '\n');
329	if (sp)
330	    {
331	    *sp = '\0';
332	    }
333
334	linect++;
335	sp = buf;
336	while (*sp != '\0')
337	    {
338	    if (*sp == ' ' || *sp == '\t')
339		{
340		++sp;
341		continue;
342		}
343
344	    if (*sp == '"' || *sp == '\'')
345		{
346		quote = *sp++;
347		arg = sp;
348		while (*sp != quote)
349		    {
350		    if (*sp == '\0')
351			{
352			syslog (LOG_ERR, "unterminated quote (line %d)",
353				linect);
354			terminate (1);
355			}
356
357		    if (*sp++ == '\\')
358		        {
359			if (*sp != '\0')
360			    {
361			    ++sp;
362			    }
363		        }
364		    }
365		}
366	    else
367		{
368		arg = sp;
369		while (*sp != '\0' && *sp != ' ' && *sp != '\t')
370		    {
371		    ++sp;
372		    }
373		}
374
375	    if (*sp != '\0')
376	        {
377		*sp++ = '\0';
378	        }
379
380	    if (sendflg)
381		{
382		chat_send (arg);
383		}
384	    else
385		{
386		chat_expect (arg);
387		}
388	    sendflg = !sendflg;
389	    }
390	}
391    fclose (cfp);
392    }
393
394/*
395 *	We got an error parsing the command line.
396 */
397void usage()
398    {
399    fprintf(stderr, "\
400Usage: %s [-v] [-t timeout] [-r report-file] {-f chat-file | chat-script}\n",
401	    program_name);
402    exit(1);
403    }
404
405char line[256];
406char *p;
407
408void logf (str)
409const char *str;
410    {
411    p = line + strlen(line);
412    strcat (p, str);
413
414    if (str[strlen(str)-1] == '\n')
415	{
416	syslog (LOG_INFO, "%s", line);
417	line[0] = 0;
418	}
419    }
420
421void logflush()
422    {
423    if (line[0] != 0)
424	{
425	syslog(LOG_INFO, "%s", line);
426	line[0] = 0;
427        }
428    }
429
430/*
431 *	Terminate with an error.
432 */
433void die()
434    {
435    terminate(1);
436    }
437
438/*
439 *	Print an error message and terminate.
440 */
441
442void fatal (msg)
443const char *msg;
444    {
445    syslog(LOG_ERR, "%s", msg);
446    terminate(2);
447    }
448
449/*
450 *	Print an error message along with the system error message and
451 *	terminate.
452 */
453
454void sysfatal (msg)
455const char *msg;
456    {
457    syslog(LOG_ERR, "%s: %m", msg);
458    terminate(2);
459    }
460
461int alarmed = 0;
462
463SIGTYPE sigalrm(signo)
464int signo;
465    {
466    int flags;
467
468    alarm(1);
469    alarmed = 1;		/* Reset alarm to avoid race window */
470    signal(SIGALRM, sigalrm);	/* that can cause hanging in read() */
471
472    logflush();
473    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
474        {
475	sysfatal("Can't get file mode flags on stdin");
476        }
477    else
478        {
479	if (fcntl(0, F_SETFL, flags | O_NONBLOCK) == -1)
480	    {
481	    sysfatal("Can't set file mode flags on stdin");
482	    }
483        }
484
485    if (verbose)
486	{
487	syslog(LOG_INFO, "alarm");
488	}
489    }
490
491void unalarm()
492    {
493    int flags;
494
495    if ((flags = fcntl(0, F_GETFL, 0)) == -1)
496        {
497	sysfatal("Can't get file mode flags on stdin");
498        }
499    else
500        {
501	if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) == -1)
502	    {
503	    sysfatal("Can't set file mode flags on stdin");
504	    }
505        }
506    }
507
508SIGTYPE sigint(signo)
509int signo;
510    {
511    fatal("SIGINT");
512    }
513
514SIGTYPE sigterm(signo)
515int signo;
516    {
517    fatal("SIGTERM");
518    }
519
520SIGTYPE sighup(signo)
521int signo;
522    {
523    fatal("SIGHUP");
524    }
525
526void init()
527    {
528    signal(SIGINT, sigint);
529    signal(SIGTERM, sigterm);
530    signal(SIGHUP, sighup);
531
532    set_tty_parameters();
533    signal(SIGALRM, sigalrm);
534    alarm(0);
535    alarmed = 0;
536    }
537
538void set_tty_parameters()
539    {
540#if defined(get_term_param)
541    term_parms t;
542
543    if (get_term_param (&t) < 0)
544        {
545	have_tty_parameters = 0;
546	return;
547        }
548
549    saved_tty_parameters = t;
550    have_tty_parameters  = 1;
551
552    t.c_iflag     |= IGNBRK | ISTRIP | IGNPAR;
553    t.c_oflag      = 0;
554    t.c_lflag      = 0;
555    t.c_cc[VERASE] =
556    t.c_cc[VKILL]  = 0;
557    t.c_cc[VMIN]   = 1;
558    t.c_cc[VTIME]  = 0;
559
560    if (set_term_param (&t) < 0)
561        {
562	sysfatal("Can't set terminal parameters");
563        }
564#endif
565    }
566
567void break_sequence()
568    {
569#ifdef TERMIOS
570    tcsendbreak (0, 0);
571#endif
572    }
573
574void terminate(status)
575int status;
576    {
577    if (report_file != (char *) 0 && report_fp != (FILE *) NULL)
578        {
579	if (verbose)
580	    {
581	    fprintf (report_fp, "Closing \"%s\".\n", report_file);
582	    }
583	fclose (report_fp);
584	report_fp = (FILE*) NULL;
585        }
586
587#if defined(get_term_param)
588    if (have_tty_parameters)
589        {
590	if (set_term_param (&saved_tty_parameters) < 0)
591	    {
592	    syslog(LOG_ERR, "Can't restore terminal parameters: %m");
593	    exit(1);
594	    }
595        }
596#endif
597
598    exit(status);
599    }
600
601/*
602 *	'Clean up' this string.
603 */
604char *clean(s, sending)
605register char *s;
606int sending;
607    {
608    char temp[STR_LEN], cur_chr;
609    register char *s1;
610    int add_return = sending;
611#define isoctal(chr) (((chr) >= '0') && ((chr) <= '7'))
612
613    s1 = temp;
614    while (*s)
615	{
616	cur_chr = *s++;
617	if (cur_chr == '^')
618	    {
619	    cur_chr = *s++;
620	    if (cur_chr == '\0')
621		{
622		*s1++ = '^';
623		break;
624		}
625	    cur_chr &= 0x1F;
626	    if (cur_chr != 0)
627	        {
628		*s1++ = cur_chr;
629	        }
630	    continue;
631	    }
632
633	if (cur_chr != '\\')
634	    {
635	    *s1++ = cur_chr;
636	    continue;
637	    }
638
639	cur_chr = *s++;
640	if (cur_chr == '\0')
641	    {
642	    if (sending)
643		{
644		*s1++ = '\\';
645		*s1++ = '\\';
646		}
647	    break;
648	    }
649
650	switch (cur_chr)
651	    {
652	case 'b':
653	    *s1++ = '\b';
654	    break;
655
656	case 'c':
657	    if (sending && *s == '\0')
658	        {
659		add_return = 0;
660	        }
661	    else
662	        {
663		*s1++ = cur_chr;
664	        }
665	    break;
666
667	case '\\':
668	case 'K':
669	case 'p':
670	case 'd':
671	    if (sending)
672	        {
673		*s1++ = '\\';
674	        }
675
676	    *s1++ = cur_chr;
677	    break;
678
679	case 'q':
680	    quiet = ! quiet;
681	    break;
682
683	case 'r':
684	    *s1++ = '\r';
685	    break;
686
687	case 'n':
688	    *s1++ = '\n';
689	    break;
690
691	case 's':
692	    *s1++ = ' ';
693	    break;
694
695	case 't':
696	    *s1++ = '\t';
697	    break;
698
699	case 'N':
700	    if (sending)
701		{
702		*s1++ = '\\';
703		*s1++ = '\0';
704		}
705	    else
706	        {
707		*s1++ = 'N';
708	        }
709	    break;
710
711	default:
712	    if (isoctal (cur_chr))
713		{
714		cur_chr &= 0x07;
715		if (isoctal (*s))
716		    {
717		    cur_chr <<= 3;
718		    cur_chr |= *s++ - '0';
719		    if (isoctal (*s))
720			{
721			cur_chr <<= 3;
722			cur_chr |= *s++ - '0';
723			}
724		    }
725
726		if (cur_chr != 0 || sending)
727		    {
728		    if (sending && (cur_chr == '\\' || cur_chr == 0))
729		        {
730			*s1++ = '\\';
731		        }
732		    *s1++ = cur_chr;
733		    }
734		break;
735		}
736
737	    if (sending)
738	        {
739		*s1++ = '\\';
740	        }
741	    *s1++ = cur_chr;
742	    break;
743	    }
744	}
745
746    if (add_return)
747        {
748	*s1++ = '\r';
749        }
750
751    *s1++ = '\0'; /* guarantee closure */
752    *s1++ = '\0'; /* terminate the string */
753    return dup_mem (temp, (size_t) (s1 - temp)); /* may have embedded nuls */
754    }
755
756/*
757 * Process the expect string
758 */
759void chat_expect(s)
760register char *s;
761    {
762    if (strcmp(s, "ABORT") == 0)
763	{
764	++abort_next;
765	return;
766	}
767
768    if (strcmp(s, "REPORT") == 0)
769	{
770	++report_next;
771	return;
772	}
773
774    if (strcmp(s, "TIMEOUT") == 0)
775	{
776	++timeout_next;
777	return;
778	}
779
780    while (*s)
781	{
782	register char *hyphen;
783
784	for (hyphen = s; *hyphen; ++hyphen)
785	    {
786	    if (*hyphen == '-')
787	        {
788		if (hyphen == s || hyphen[-1] != '\\')
789		    {
790		    break;
791		    }
792	        }
793	    }
794
795	if (*hyphen == '-')
796	    {
797	    *hyphen = '\0';
798
799	    if (get_string(s))
800	        {
801		return;
802	        }
803	    else
804		{
805		s = hyphen + 1;
806
807		for (hyphen = s; *hyphen; ++hyphen)
808		    {
809		    if (*hyphen == '-')
810		        {
811			if (hyphen == s || hyphen[-1] != '\\')
812			    {
813			    break;
814			    }
815		        }
816		    }
817
818		if (*hyphen == '-')
819		    {
820		    *hyphen = '\0';
821
822		    chat_send(s);
823		    s = hyphen + 1;
824		    }
825		else
826		    {
827		    chat_send(s);
828		    return;
829		    }
830		}
831	    }
832	else
833	    {
834	    if (get_string(s))
835	        {
836		return;
837	        }
838	    else
839		{
840		if (fail_reason)
841		    {
842		    syslog(LOG_INFO, "Failed (%s)", fail_reason);
843		    }
844		else
845		    {
846		    syslog(LOG_INFO, "Failed");
847		    }
848
849		terminate(exit_code);
850		}
851	    }
852	}
853    }
854
855char *character(c)
856int c;
857    {
858    static char string[10];
859    char *meta;
860
861    meta = (c & 0x80) ? "M-" : "";
862    c &= 0x7F;
863
864    if (c < 32)
865        {
866	sprintf(string, "%s^%c", meta, (int)c + '@');
867        }
868    else
869        {
870	if (c == 127)
871	    {
872	    sprintf(string, "%s^?", meta);
873	    }
874	else
875	    {
876	    sprintf(string, "%s%c", meta, c);
877	    }
878        }
879
880    return (string);
881    }
882
883/*
884 *  process the reply string
885 */
886void chat_send (s)
887register char *s;
888    {
889    if (abort_next)
890        {
891	char *s1;
892
893	abort_next = 0;
894
895	if (n_aborts >= MAX_ABORTS)
896	    {
897	    fatal("Too many ABORT strings");
898	    }
899
900	s1 = clean(s, 0);
901
902	if (strlen(s1) > strlen(s)
903	    || strlen(s1) + 1 > sizeof(fail_buffer))
904	    {
905	    syslog(LOG_WARNING, "Illegal or too-long ABORT string ('%s')", s);
906	    die();
907	    }
908
909	abort_string[n_aborts++] = s1;
910
911	if (verbose)
912	    {
913	    logf("abort on (");
914
915	    for (s1 = s; *s1; ++s1)
916	        {
917		logf(character(*s1));
918	        }
919
920	    logf(")\n");
921	    }
922	return;
923	}
924
925    if (report_next)
926        {
927	char *s1;
928
929	report_next = 0;
930	if (n_reports >= MAX_REPORTS)
931	    {
932	    fatal("Too many REPORT strings");
933	    }
934
935	s1 = clean(s, 0);
936
937	if (strlen(s1) > strlen(s) || strlen(s1) > sizeof fail_buffer - 1)
938	    {
939	    syslog(LOG_WARNING, "Illegal or too-long REPORT string ('%s')", s);
940	    die();
941	    }
942
943	report_string[n_reports++] = s1;
944
945	if (verbose)
946	    {
947	    logf("report (");
948	    s1 = s;
949	    while (*s1)
950	        {
951		logf(character(*s1));
952		++s1;
953	        }
954	    logf(")\n");
955	    }
956	return;
957        }
958
959    if (timeout_next)
960        {
961	timeout_next = 0;
962	timeout = atoi(s);
963
964	if (timeout <= 0)
965	    {
966	    timeout = DEFAULT_CHAT_TIMEOUT;
967	    }
968
969	if (verbose)
970	    {
971	    syslog(LOG_INFO, "timeout set to %d seconds", timeout);
972	    }
973	return;
974        }
975
976    if (strcmp(s, "EOT") == 0)
977        {
978	s = "^D\\c";
979        }
980    else
981        {
982	if (strcmp(s, "BREAK") == 0)
983	    {
984	    s = "\\K\\c";
985	    }
986        }
987
988    if (!put_string(s))
989        {
990	syslog(LOG_INFO, "Failed");
991	terminate(1);
992        }
993    }
994
995int get_char()
996    {
997    int status;
998    char c;
999
1000    status = read(0, &c, 1);
1001
1002    switch (status)
1003        {
1004    case 1:
1005	return ((int)c & 0x7F);
1006
1007    default:
1008	syslog(LOG_WARNING, "warning: read() on stdin returned %d",
1009	       status);
1010
1011    case -1:
1012	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1013	    {
1014	    sysfatal("Can't get file mode flags on stdin");
1015	    }
1016	else
1017	    {
1018	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1019	        {
1020		sysfatal("Can't set file mode flags on stdin");
1021	        }
1022	    }
1023
1024	return (-1);
1025        }
1026    }
1027
1028int put_char(c)
1029int c;
1030    {
1031    int status;
1032    char ch = c;
1033
1034    usleep(10000);		/* inter-character typing delay (?) */
1035
1036    status = write(1, &ch, 1);
1037
1038    switch (status)
1039        {
1040    case 1:
1041	return (0);
1042
1043    default:
1044	syslog(LOG_WARNING, "warning: write() on stdout returned %d",
1045	       status);
1046
1047    case -1:
1048	if ((status = fcntl(0, F_GETFL, 0)) == -1)
1049	    {
1050	    sysfatal("Can't get file mode flags on stdin");
1051	    }
1052	else
1053	    {
1054	    if (fcntl(0, F_SETFL, status & ~O_NONBLOCK) == -1)
1055	        {
1056		sysfatal("Can't set file mode flags on stdin");
1057	        }
1058	    }
1059
1060	return (-1);
1061        }
1062    }
1063
1064int write_char (c)
1065int c;
1066    {
1067    if (alarmed || put_char(c) < 0)
1068	{
1069	extern int errno;
1070
1071	alarm(0);
1072	alarmed = 0;
1073
1074	if (verbose)
1075	    {
1076	    if (errno == EINTR || errno == EWOULDBLOCK)
1077	        {
1078		syslog(LOG_INFO, " -- write timed out");
1079	        }
1080	    else
1081	        {
1082		syslog(LOG_INFO, " -- write failed: %m");
1083	        }
1084	    }
1085	return (0);
1086	}
1087    return (1);
1088    }
1089
1090int put_string (s)
1091register char *s;
1092    {
1093    s = clean(s, 1);
1094
1095    if (verbose)
1096	{
1097	logf("send (");
1098
1099	if (quiet)
1100	    {
1101	    logf("??????");
1102	    }
1103	else
1104	    {
1105	    register char *s1 = s;
1106
1107	    for (s1 = s; *s1; ++s1)
1108	        {
1109		logf(character(*s1));
1110	        }
1111	    }
1112
1113	logf(")\n");
1114	}
1115
1116    alarm(timeout); alarmed = 0;
1117
1118    while (*s)
1119	{
1120	register char c = *s++;
1121
1122	if (c != '\\')
1123	    {
1124	    if (!write_char (c))
1125	        {
1126		return 0;
1127	        }
1128	    continue;
1129	    }
1130
1131	c = *s++;
1132	switch (c)
1133	    {
1134	case 'd':
1135	    sleep(1);
1136	    break;
1137
1138	case 'K':
1139	    break_sequence();
1140	    break;
1141
1142	case 'p':
1143	    usleep(10000); 	/* 1/100th of a second (arg is microseconds) */
1144	    break;
1145
1146	default:
1147	    if (!write_char (c))
1148		return 0;
1149	    break;
1150	    }
1151	}
1152
1153    alarm(0);
1154    alarmed = 0;
1155    return (1);
1156    }
1157
1158/*
1159 *	'Wait for' this string to appear on this file descriptor.
1160 */
1161int get_string(string)
1162register char *string;
1163    {
1164    char temp[STR_LEN];
1165    int c, printed = 0, len, minlen;
1166    register char *s = temp, *end = s + STR_LEN;
1167
1168    fail_reason = (char *)0;
1169    string = clean(string, 0);
1170    len = strlen(string);
1171    minlen = (len > sizeof(fail_buffer)? len: sizeof(fail_buffer)) - 1;
1172
1173    if (verbose)
1174	{
1175	register char *s1;
1176
1177	logf("expect (");
1178
1179	for (s1 = string; *s1; ++s1)
1180	    {
1181	    logf(character(*s1));
1182	    }
1183
1184	logf(")\n");
1185	}
1186
1187    if (len > STR_LEN)
1188	{
1189	syslog(LOG_INFO, "expect string is too long");
1190	exit_code = 1;
1191	return 0;
1192	}
1193
1194    if (len == 0)
1195	{
1196	if (verbose)
1197	    {
1198	    syslog(LOG_INFO, "got it");
1199	    }
1200
1201	return (1);
1202	}
1203
1204    alarm(timeout);
1205    alarmed = 0;
1206
1207    while ( ! alarmed && (c = get_char()) >= 0)
1208	{
1209	int n, abort_len, report_len;
1210
1211	if (verbose)
1212	    {
1213	    if (c == '\n')
1214	        {
1215		logf("\n");
1216	        }
1217	    else
1218	        {
1219		logf(character(c));
1220	        }
1221	    }
1222
1223	*s++ = c;
1224
1225	if (s - temp >= len &&
1226	    c == string[len - 1] &&
1227	    strncmp(s - len, string, len) == 0)
1228	    {
1229	    if (verbose)
1230		{
1231		logf(" -- got it\n");
1232		}
1233
1234	    alarm(0);
1235	    alarmed = 0;
1236	    return (1);
1237	    }
1238
1239	for (n = 0; n < n_aborts; ++n)
1240	    {
1241	    if (s - temp >= (abort_len = strlen(abort_string[n])) &&
1242		strncmp(s - abort_len, abort_string[n], abort_len) == 0)
1243	        {
1244		if (verbose)
1245		    {
1246		    logf(" -- failed\n");
1247		    }
1248
1249		alarm(0);
1250		alarmed = 0;
1251		exit_code = n + 4;
1252		strcpy(fail_reason = fail_buffer, abort_string[n]);
1253		return (0);
1254	        }
1255	    }
1256
1257	if (!report_gathering)
1258	    {
1259	    for (n = 0; n < n_reports; ++n)
1260	        {
1261		if ((report_string[n] != (char*) NULL) &&
1262		    s - temp >= (report_len = strlen(report_string[n])) &&
1263		    strncmp(s - report_len, report_string[n], report_len) == 0)
1264		    {
1265		    time_t time_now   = time ((time_t*) NULL);
1266		    struct tm* tm_now = localtime (&time_now);
1267
1268		    strftime (report_buffer, 20, "%b %d %H:%M:%S ", tm_now);
1269		    strcat (report_buffer, report_string[n]);
1270
1271		    report_string[n] = (char *) NULL;
1272		    report_gathering = 1;
1273		    break;
1274		    }
1275	        }
1276	    }
1277	else
1278	    {
1279	    if (!iscntrl (c))
1280	        {
1281		int rep_len = strlen (report_buffer);
1282		report_buffer[rep_len]     = c;
1283		report_buffer[rep_len + 1] = '\0';
1284	        }
1285	    else
1286	        {
1287		report_gathering = 0;
1288		fprintf (report_fp, "chat:  %s\n", report_buffer);
1289	        }
1290	    }
1291
1292	if (s >= end)
1293	    {
1294	    strncpy (temp, s - minlen, minlen);
1295	    s = temp + minlen;
1296	    }
1297
1298	if (alarmed && verbose)
1299	    {
1300	    syslog(LOG_WARNING, "warning: alarm synchronization problem");
1301	    }
1302	}
1303
1304    alarm(0);
1305
1306    if (verbose && printed)
1307	{
1308	if (alarmed)
1309	    {
1310	    logf(" -- read timed out\n");
1311	    }
1312	else
1313	    {
1314	    logflush();
1315	    syslog(LOG_INFO, " -- read failed: %m");
1316	    }
1317	}
1318
1319    exit_code = 3;
1320    alarmed   = 0;
1321    return (0);
1322    }
1323
1324#ifdef NO_USLEEP
1325#include <sys/types.h>
1326#include <sys/time.h>
1327
1328/*
1329  usleep -- support routine for 4.2BSD system call emulations
1330  last edit:  29-Oct-1984     D A Gwyn
1331  */
1332
1333extern int	  select();
1334
1335int
1336usleep( usec )				  /* returns 0 if ok, else -1 */
1337    long		usec;		/* delay in microseconds */
1338{
1339    static struct			/* `timeval' */
1340        {
1341	long	tv_sec;		/* seconds */
1342	long	tv_usec;	/* microsecs */
1343        } delay;	    /* _select() timeout */
1344
1345    delay.tv_sec  = usec / 1000000L;
1346    delay.tv_usec = usec % 1000000L;
1347
1348    return select( 0, (long *)0, (long *)0, (long *)0, &delay );
1349}
1350#endif
1351