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