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