chat.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. /*-
  2. * Copyright (c) 1997
  3. * David L Nugent <davidn@blaze.net.au>.
  4. * All rights reserved.
  5. *
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, is permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice immediately at the beginning of the file, without modification,
  12. * this list of conditions, and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. This work was done expressly for inclusion into FreeBSD. Other use
  17. * is permitted provided this notation is included.
  18. * 4. Absolutely no warranty of function or purpose is made by the authors.
  19. * 5. Modifications may be freely made to this file providing the above
  20. * conditions are met.
  21. *
  22. * Modem chat module - send/expect style functions for getty
  23. * For semi-intelligent modem handling.
  24. */
  25. #include <sys/types.h>
  26. #include <sys/ioctl.h>
  27. #include <sys/utsname.h>
  28. #include <ctype.h>
  29. #include <signal.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <syslog.h>
  33. #include <unistd.h>
  34. #include "gettytab.h"
  35. #include "extern.h"
  36. #define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
  37. #define CHATDEBUG_RECEIVE 0x01
  38. #define CHATDEBUG_SEND 0x02
  39. #define CHATDEBUG_EXPECT 0x04
  40. #define CHATDEBUG_MISC 0x08
  41. #define CHATDEBUG_DEFAULT 0
  42. #define CHAT_DEFAULT_TIMEOUT 10
  43. static int chat_debug = CHATDEBUG_DEFAULT;
  44. static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
  45. static volatile int alarmed = 0;
  46. static void chat_alrm(int);
  47. static int chat_unalarm(void);
  48. static int getdigit(char **, int, int);
  49. static char **read_chat(char **);
  50. static char *cleanchr(char **, unsigned char);
  51. static const char *cleanstr(const char *, int);
  52. static const char *result(int);
  53. static int chat_expect(const char *);
  54. static int chat_send(char const *);
  55. /*
  56. * alarm signal handler
  57. * handle timeouts in read/write
  58. * change stdin to non-blocking mode to prevent
  59. * possible hang in read().
  60. */
  61. static void
  62. chat_alrm(int signo __unused)
  63. {
  64. int on = 1;
  65. alarm(1);
  66. alarmed = 1;
  67. signal(SIGALRM, chat_alrm);
  68. ioctl(STDIN_FILENO, FIONBIO, &on);
  69. }
  70. /*
  71. * Turn back on blocking mode reset by chat_alrm()
  72. */
  73. static int
  74. chat_unalarm(void)
  75. {
  76. int off = 0;
  77. return ioctl(STDIN_FILENO, FIONBIO, &off);
  78. }
  79. /*
  80. * convert a string of a given base (octal/hex) to binary
  81. */
  82. static int
  83. getdigit(char **ptr, int base, int max)
  84. {
  85. int i, val = 0;
  86. char * q;
  87. static const char xdigits[] = "0123456789abcdef";
  88. for (i = 0, q = *ptr; i++ < max; ++q) {
  89. int sval;
  90. const char * s = strchr(xdigits, tolower(*q));
  91. if (s == NULL || (sval = s - xdigits) >= base)
  92. break;
  93. val = (val * base) + sval;
  94. }
  95. *ptr = q;
  96. return val;
  97. }
  98. /*
  99. * read_chat()
  100. * Convert a whitespace delimtied string into an array
  101. * of strings, being expect/send pairs
  102. */
  103. static char **
  104. read_chat(char **chatstr)
  105. {
  106. char *str = *chatstr;
  107. char **res = NULL;
  108. if (str != NULL) {
  109. char *tmp = NULL;
  110. int l;
  111. if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
  112. (res=malloc(((l + 1) / 2 + 1) * sizeof(char *))) != NULL) {
  113. static char ws[] = " \t";
  114. char * p;
  115. for (l = 0, p = strtok(strcpy(tmp, str), ws);
  116. p != NULL;
  117. p = strtok(NULL, ws))
  118. {
  119. char *q, *r;
  120. /* Read escapes */
  121. for (q = r = p; *r; ++q)
  122. {
  123. if (*q == '\\')
  124. {
  125. /* handle special escapes */
  126. switch (*++q)
  127. {
  128. case 'a': /* bell */
  129. *r++ = '\a';
  130. break;
  131. case 'r': /* cr */
  132. *r++ = '\r';
  133. break;
  134. case 'n': /* nl */
  135. *r++ = '\n';
  136. break;
  137. case 'f': /* ff */
  138. *r++ = '\f';
  139. break;
  140. case 'b': /* bs */
  141. *r++ = '\b';
  142. break;
  143. case 'e': /* esc */
  144. *r++ = 27;
  145. break;
  146. case 't': /* tab */
  147. *r++ = '\t';
  148. break;
  149. case 'p': /* pause */
  150. *r++ = PAUSE_CH;
  151. break;
  152. case 's':
  153. case 'S': /* space */
  154. *r++ = ' ';
  155. break;
  156. case 'x': /* hexdigit */
  157. ++q;
  158. *r++ = getdigit(&q, 16, 2);
  159. --q;
  160. break;
  161. case '0': /* octal */
  162. ++q;
  163. *r++ = getdigit(&q, 8, 3);
  164. --q;
  165. break;
  166. default: /* literal */
  167. *r++ = *q;
  168. break;
  169. case 0: /* not past eos */
  170. --q;
  171. break;
  172. }
  173. } else {
  174. /* copy standard character */
  175. *r++ = *q;
  176. }
  177. }
  178. /* Remove surrounding quotes, if any
  179. */
  180. if (*p == '"' || *p == '\'') {
  181. q = strrchr(p+1, *p);
  182. if (q != NULL && *q == *p && q[1] == '\0') {
  183. *q = '\0';
  184. p++;
  185. }
  186. }
  187. res[l++] = p;
  188. }
  189. res[l] = NULL;
  190. *chatstr = tmp;
  191. return res;
  192. }
  193. free(tmp);
  194. }
  195. return res;
  196. }
  197. /*
  198. * clean a character for display (ctrl/meta character)
  199. */
  200. static char *
  201. cleanchr(char **buf, unsigned char ch)
  202. {
  203. int l;
  204. static char tmpbuf[5];
  205. char * tmp = buf ? *buf : tmpbuf;
  206. if (ch & 0x80) {
  207. strcpy(tmp, "M-");
  208. l = 2;
  209. ch &= 0x7f;
  210. } else
  211. l = 0;
  212. if (ch < 32) {
  213. tmp[l++] = '^';
  214. tmp[l++] = ch + '@';
  215. } else if (ch == 127) {
  216. tmp[l++] = '^';
  217. tmp[l++] = '?';
  218. } else
  219. tmp[l++] = ch;
  220. tmp[l] = '\0';
  221. if (buf)
  222. *buf = tmp + l;
  223. return tmp;
  224. }
  225. /*
  226. * clean a string for display (ctrl/meta characters)
  227. */
  228. static const char *
  229. cleanstr(const char *s, int l)
  230. {
  231. static char * tmp = NULL;
  232. static int tmplen = 0;
  233. if (tmplen < l * 4 + 1)
  234. tmp = realloc(tmp, tmplen = l * 4 + 1);
  235. if (tmp == NULL) {
  236. tmplen = 0;
  237. return "(mem alloc error)";
  238. } else {
  239. int i = 0;
  240. char * p = tmp;
  241. while (i < l)
  242. cleanchr(&p, s[i++]);
  243. *p = '\0';
  244. }
  245. return tmp;
  246. }
  247. /*
  248. * return result as a pseudo-english word
  249. */
  250. static const char *
  251. result(int r)
  252. {
  253. static const char * results[] = {
  254. "OK", "MEMERROR", "IOERROR", "TIMEOUT"
  255. };
  256. return results[r & 3];
  257. }
  258. /*
  259. * chat_expect()
  260. * scan input for an expected string
  261. */
  262. static int
  263. chat_expect(const char *str)
  264. {
  265. int len, r = 0;
  266. if (chat_debug & CHATDEBUG_EXPECT)
  267. syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
  268. if ((len = strlen(str)) > 0) {
  269. int i = 0;
  270. char * got;
  271. if ((got = malloc(len + 1)) == NULL)
  272. r = 1;
  273. else {
  274. memset(got, 0, len+1);
  275. alarm(chat_alarm);
  276. alarmed = 0;
  277. while (r == 0 && i < len) {
  278. if (alarmed)
  279. r = 3;
  280. else {
  281. unsigned char ch;
  282. if (read(STDIN_FILENO, &ch, 1) == 1) {
  283. if (chat_debug & CHATDEBUG_RECEIVE)
  284. syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
  285. cleanchr(NULL, ch), i);
  286. if (ch == str[i])
  287. got[i++] = ch;
  288. else if (i > 0) {
  289. int j = 1;
  290. /* See if we can resync on a
  291. * partial match in our buffer
  292. */
  293. while (j < i && memcmp(got + j, str, i - j) != 0)
  294. j++;
  295. if (j < i)
  296. memcpy(got, got + j, i - j);
  297. i -= j;
  298. }
  299. } else
  300. r = alarmed ? 3 : 2;
  301. }
  302. }
  303. alarm(0);
  304. chat_unalarm();
  305. alarmed = 0;
  306. free(got);
  307. }
  308. }
  309. if (chat_debug & CHATDEBUG_EXPECT)
  310. syslog(LOG_DEBUG, "chat_expect %s", result(r));
  311. return r;
  312. }
  313. /*
  314. * chat_send()
  315. * send a chat string
  316. */
  317. static int
  318. chat_send(char const *str)
  319. {
  320. int r = 0;
  321. if (chat_debug & CHATDEBUG_SEND)
  322. syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
  323. if (*str) {
  324. alarm(chat_alarm);
  325. alarmed = 0;
  326. while (r == 0 && *str)
  327. {
  328. unsigned char ch = (unsigned char)*str++;
  329. if (alarmed)
  330. r = 3;
  331. else if (ch == PAUSE_CH)
  332. usleep(500000); /* 1/2 second */
  333. else {
  334. usleep(10000); /* be kind to modem */
  335. if (write(STDOUT_FILENO, &ch, 1) != 1)
  336. r = alarmed ? 3 : 2;
  337. }
  338. }
  339. alarm(0);
  340. chat_unalarm();
  341. alarmed = 0;
  342. }
  343. if (chat_debug & CHATDEBUG_SEND)
  344. syslog(LOG_DEBUG, "chat_send %s", result(r));
  345. return r;
  346. }
  347. /*
  348. * getty_chat()
  349. *
  350. * Termination codes:
  351. * -1 - no script supplied
  352. * 0 - script terminated correctly
  353. * 1 - invalid argument, expect string too large, etc.
  354. * 2 - error on an I/O operation or fatal error condition
  355. * 3 - timeout waiting for a simple string
  356. *
  357. * Parameters:
  358. * char *scrstr - unparsed chat script
  359. * timeout - seconds timeout
  360. * debug - debug value (bitmask)
  361. */
  362. int
  363. getty_chat(char *scrstr, int timeout, int debug)
  364. {
  365. int r = -1;
  366. chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
  367. chat_debug = debug;
  368. if (scrstr != NULL) {
  369. char **script;
  370. if (chat_debug & CHATDEBUG_MISC)
  371. syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
  372. if ((script = read_chat(&scrstr)) != NULL) {
  373. int i = r = 0;
  374. int off = 0;
  375. sig_t old_alarm;
  376. /*
  377. * We need to be in raw mode for all this
  378. * Rely on caller...
  379. */
  380. old_alarm = signal(SIGALRM, chat_alrm);
  381. chat_unalarm(); /* Force blocking mode at start */
  382. /*
  383. * This is the send/expect loop
  384. */
  385. while (r == 0 && script[i] != NULL)
  386. if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
  387. r = chat_send(script[i++]);
  388. signal(SIGALRM, old_alarm);
  389. free(script);
  390. free(scrstr);
  391. /*
  392. * Ensure stdin is in blocking mode
  393. */
  394. ioctl(STDIN_FILENO, FIONBIO, &off);
  395. }
  396. if (chat_debug & CHATDEBUG_MISC)
  397. syslog(LOG_DEBUG, "getty_chat %s", result(r));
  398. }
  399. return r;
  400. }