9 |
*/ |
*/ |
10 |
#include "libbb.h" |
#include "libbb.h" |
11 |
|
|
|
/* |
|
|
#define ENABLE_FEATURE_CHAT_NOFAIL 1 // +126 bytes |
|
|
#define ENABLE_FEATURE_CHAT_TTY_HIFI 0 // + 70 bytes |
|
|
#define ENABLE_FEATURE_CHAT_IMPLICIT_CR 1 // + 44 bytes |
|
|
#define ENABLE_FEATURE_CHAT_SEND_ESCAPES 0 // +103 bytes |
|
|
#define ENABLE_FEATURE_CHAT_VAR_ABORT_LEN 0 // + 70 bytes |
|
|
#define ENABLE_FEATURE_CHAT_CLR_ABORT 0 // +113 bytes |
|
|
#define ENABLE_FEATURE_CHAT_SWALLOW_OPTS 0 // + 23 bytes |
|
|
*/ |
|
|
|
|
12 |
// default timeout: 45 sec |
// default timeout: 45 sec |
13 |
#define DEFAULT_CHAT_TIMEOUT 45*1000 |
#define DEFAULT_CHAT_TIMEOUT 45*1000 |
14 |
// max length of "abort string", |
// max length of "abort string", |
27 |
}; |
}; |
28 |
|
|
29 |
// exit code |
// exit code |
30 |
// N.B> 10 bytes for volatile. Why all these signals?! |
#define exitcode bb_got_signal |
|
static /*volatile*/ smallint exitcode; |
|
31 |
|
|
32 |
// trap for critical signals |
// trap for critical signals |
33 |
static void signal_handler(UNUSED_PARAM int signo) |
static void signal_handler(UNUSED_PARAM int signo) |
90 |
return p - start; |
return p - start; |
91 |
} |
} |
92 |
|
|
|
|
|
93 |
int chat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int chat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
94 |
int chat_main(int argc UNUSED_PARAM, char **argv) |
int chat_main(int argc UNUSED_PARAM, char **argv) |
95 |
{ |
{ |
96 |
// should we dump device output? to what fd? by default no. |
int record_fd = -1; |
|
// this can be controlled later via ECHO {ON|OFF} chat directive |
|
|
// int echo_fd; |
|
97 |
bool echo = 0; |
bool echo = 0; |
98 |
// collection of device replies which cause unconditional termination |
// collection of device replies which cause unconditional termination |
99 |
llist_t *aborts = NULL; |
llist_t *aborts = NULL; |
118 |
DIR_TIMEOUT, |
DIR_TIMEOUT, |
119 |
DIR_ECHO, |
DIR_ECHO, |
120 |
DIR_SAY, |
DIR_SAY, |
121 |
|
DIR_RECORD, |
122 |
}; |
}; |
123 |
|
|
124 |
// make x* functions fail with correct exitcode |
// make x* functions fail with correct exitcode |
153 |
#if ENABLE_FEATURE_CHAT_CLR_ABORT |
#if ENABLE_FEATURE_CHAT_CLR_ABORT |
154 |
"CLR_ABORT\0" |
"CLR_ABORT\0" |
155 |
#endif |
#endif |
156 |
"TIMEOUT\0" "ECHO\0" "SAY\0" |
"TIMEOUT\0" "ECHO\0" "SAY\0" "RECORD\0" |
157 |
, *argv |
, *argv |
158 |
); |
); |
159 |
if (key >= 0) { |
if (key >= 0) { |
160 |
// cache directive value |
// cache directive value |
161 |
char *arg = *++argv; |
char *arg = *++argv; |
162 |
// ON -> 1, anything else -> 0 |
// OFF -> 0, anything else -> 1 |
163 |
bool onoff = !strcmp("ON", arg); |
bool onoff = (0 != strcmp("OFF", arg)); |
164 |
// process directive |
// process directive |
165 |
if (DIR_HANGUP == key) { |
if (DIR_HANGUP == key) { |
166 |
// turn SIGHUP on/off |
// turn SIGHUP on/off |
204 |
timeout = DEFAULT_CHAT_TIMEOUT; |
timeout = DEFAULT_CHAT_TIMEOUT; |
205 |
} else if (DIR_ECHO == key) { |
} else if (DIR_ECHO == key) { |
206 |
// turn echo on/off |
// turn echo on/off |
207 |
// N.B. echo means dumping output |
// N.B. echo means dumping device input/output to stderr |
|
// from stdin (device) to stderr |
|
208 |
echo = onoff; |
echo = onoff; |
209 |
//TODO? echo_fd = onoff * STDERR_FILENO; |
} else if (DIR_RECORD == key) { |
210 |
//TODO? echo_fd = xopen(arg, O_WRONLY|O_CREAT|O_TRUNC); |
// turn record on/off |
211 |
|
// N.B. record means dumping device input to a file |
212 |
|
// close previous record_fd |
213 |
|
if (record_fd > 0) |
214 |
|
close(record_fd); |
215 |
|
// N.B. do we have to die here on open error? |
216 |
|
record_fd = (onoff) ? xopen(arg, O_WRONLY|O_CREAT|O_TRUNC) : -1; |
217 |
} else if (DIR_SAY == key) { |
} else if (DIR_SAY == key) { |
218 |
// just print argument verbatim |
// just print argument verbatim |
219 |
fprintf(stderr, arg); |
// TODO: should we use full_write() to avoid unistd/stdio conflict? |
220 |
|
bb_error_msg("%s", arg); |
221 |
} |
} |
222 |
// next, please! |
// next, please! |
223 |
argv++; |
argv++; |
281 |
|
|
282 |
// read next char from device |
// read next char from device |
283 |
if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) { |
if (safe_read(STDIN_FILENO, buf+buf_len, 1) > 0) { |
284 |
// dump device output if ECHO ON or RECORD fname |
// dump device input if RECORD fname |
285 |
//TODO? if (echo_fd > 0) { |
if (record_fd > 0) { |
286 |
//TODO? full_write(echo_fd, buf+buf_len, 1); |
full_write(record_fd, buf+buf_len, 1); |
287 |
//TODO? } |
} |
288 |
if (echo > 0) |
// dump device input if ECHO ON |
289 |
|
if (echo > 0) { |
290 |
|
// if (buf[buf_len] < ' ') { |
291 |
|
// full_write(STDERR_FILENO, "^", 1); |
292 |
|
// buf[buf_len] += '@'; |
293 |
|
// } |
294 |
full_write(STDERR_FILENO, buf+buf_len, 1); |
full_write(STDERR_FILENO, buf+buf_len, 1); |
295 |
|
} |
296 |
buf_len++; |
buf_len++; |
297 |
// move input frame if we've reached higher bound |
// move input frame if we've reached higher bound |
298 |
if (buf_len > COMMON_BUFSIZE) { |
if (buf_len > COMMON_BUFSIZE) { |
319 |
if (delta >= 0 && !memcmp(buf+delta, expect, expect_len)) |
if (delta >= 0 && !memcmp(buf+delta, expect, expect_len)) |
320 |
goto expect_done; |
goto expect_done; |
321 |
#undef buf |
#undef buf |
322 |
} |
} /* while (have data) */ |
323 |
|
|
324 |
// device timed out or unexpected reply received |
// device timed out or unexpected reply received |
325 |
exitcode = ERR_TIMEOUT; |
exitcode = ERR_TIMEOUT; |
364 |
trim(++buf); |
trim(++buf); |
365 |
buf = loaded = xmalloc_xopen_read_close(buf, NULL); |
buf = loaded = xmalloc_xopen_read_close(buf, NULL); |
366 |
} |
} |
|
|
|
367 |
// expand escape sequences in command |
// expand escape sequences in command |
368 |
len = unescape(buf, &nocr); |
len = unescape(buf, &nocr); |
369 |
|
|
370 |
// send command |
// send command |
371 |
#if ENABLE_FEATURE_CHAT_SEND_ESCAPES |
alarm(timeout); |
372 |
pfd.fd = STDOUT_FILENO; |
pfd.fd = STDOUT_FILENO; |
373 |
pfd.events = POLLOUT; |
pfd.events = POLLOUT; |
374 |
while (len && !exitcode |
while (len && !exitcode |
375 |
&& poll(&pfd, 1, timeout) > 0 |
&& poll(&pfd, 1, -1) > 0 |
376 |
&& (pfd.revents & POLLOUT) |
&& (pfd.revents & POLLOUT) |
377 |
) { |
) { |
378 |
// ugly! ugly! ugly! |
#if ENABLE_FEATURE_CHAT_SEND_ESCAPES |
|
// gotta send char by char to achieve this! |
|
|
// Brrr... |
|
379 |
// "\\d" means 1 sec delay, "\\p" means 0.01 sec delay |
// "\\d" means 1 sec delay, "\\p" means 0.01 sec delay |
380 |
// "\\K" means send BREAK |
// "\\K" means send BREAK |
381 |
char c = *buf; |
char c = *buf; |
385 |
sleep(1); |
sleep(1); |
386 |
len--; |
len--; |
387 |
continue; |
continue; |
388 |
} else if ('p' == c) { |
} |
389 |
|
if ('p' == c) { |
390 |
usleep(10000); |
usleep(10000); |
391 |
len--; |
len--; |
392 |
continue; |
continue; |
393 |
} else if ('K' == c) { |
} |
394 |
|
if ('K' == c) { |
395 |
tcsendbreak(STDOUT_FILENO, 0); |
tcsendbreak(STDOUT_FILENO, 0); |
396 |
len--; |
len--; |
397 |
continue; |
continue; |
|
} else { |
|
|
buf--; |
|
398 |
} |
} |
399 |
|
buf--; |
400 |
} |
} |
401 |
if (safe_write(STDOUT_FILENO, buf, 1) > 0) { |
if (safe_write(STDOUT_FILENO, buf, 1) != 1) |
|
len--; |
|
|
buf++; |
|
|
} else |
|
402 |
break; |
break; |
403 |
} |
len--; |
404 |
|
buf++; |
405 |
#else |
#else |
|
// if (len) { |
|
|
alarm(timeout); |
|
406 |
len -= full_write(STDOUT_FILENO, buf, len); |
len -= full_write(STDOUT_FILENO, buf, len); |
|
alarm(0); |
|
|
// } |
|
407 |
#endif |
#endif |
408 |
|
} /* while (can write) */ |
409 |
|
alarm(0); |
410 |
|
|
411 |
// report I/O error if there still exists at least one non-sent char |
// report I/O error if there still exists at least one non-sent char |
412 |
if (len) |
if (len) |
420 |
else if (!nocr) |
else if (!nocr) |
421 |
xwrite(STDOUT_FILENO, "\r", 1); |
xwrite(STDOUT_FILENO, "\r", 1); |
422 |
#endif |
#endif |
|
|
|
423 |
// bail out unless we sent command successfully |
// bail out unless we sent command successfully |
424 |
if (exitcode) |
if (exitcode) |
425 |
break; |
break; |
426 |
|
} /* if (*argv) */ |
|
} |
|
427 |
} |
} |
428 |
} |
} /* while (*argv) */ |
429 |
|
|
430 |
#if ENABLE_FEATURE_CHAT_TTY_HIFI |
#if ENABLE_FEATURE_CHAT_TTY_HIFI |
431 |
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); |
tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio0); |