3 * David L Nugent <davidn@blaze.net.au>.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, is permitted provided that the following conditions
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
22 * Modem chat module - send/expect style functions for getty
23 * For semi-intelligent modem handling.
25 * $FreeBSD: src/libexec/getty/chat.c,v 1.6 1999/08/28 00:09:34 peter Exp $
26 * $DragonFly: src/libexec/getty/chat.c,v 1.3 2003/11/14 03:54:30 dillon Exp $
29 #include <sys/param.h>
31 #include <sys/ioctl.h>
32 #include <sys/resource.h>
33 #include <sys/ttydefaults.h>
34 #include <sys/utsname.h>
48 #include <sys/socket.h>
52 #define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
54 #define CHATDEBUG_RECEIVE 0x01
55 #define CHATDEBUG_SEND 0x02
56 #define CHATDEBUG_EXPECT 0x04
57 #define CHATDEBUG_MISC 0x08
59 #define CHATDEBUG_DEFAULT 0
60 #define CHAT_DEFAULT_TIMEOUT 10
63 static int chat_debug = CHATDEBUG_DEFAULT;
64 static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
66 static volatile int alarmed = 0;
69 static void chat_alrm (int);
70 static int chat_unalarm (void);
71 static int getdigit (unsigned char **, int, int);
72 static char **read_chat (char **);
73 static char *cleanchr (char **, unsigned char);
74 static char *cleanstr (const unsigned char *, int);
75 static const char *result (int);
76 static int chat_expect (const char *);
77 static int chat_send (char const *);
81 * alarm signal handler
82 * handle timeouts in read/write
83 * change stdin to non-blocking mode to prevent
84 * possible hang in read().
95 signal(SIGALRM, chat_alrm);
96 ioctl(STDIN_FILENO, FIONBIO, &on);
101 * Turn back on blocking mode reset by chat_alrm()
108 return ioctl(STDIN_FILENO, FIONBIO, &off);
113 * convert a string of a given base (octal/hex) to binary
117 getdigit(ptr, base, max)
124 static const char xdigits[] = "0123456789abcdef";
126 for (i = 0, q = *ptr; i++ < max; ++q) {
128 const char * s = strchr(xdigits, tolower(*q));
130 if (s == NULL || (sval = s - xdigits) >= base)
132 val = (val * base) + sval;
141 * Convert a whitespace delimtied string into an array
142 * of strings, being expect/send pairs
149 char *str = *chatstr;
156 if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
157 (res=malloc((l / 2 + 1) * sizeof(char *))) != NULL) {
158 static char ws[] = " \t";
161 for (l = 0, p = strtok(strcpy(tmp, str), ws);
163 p = strtok(NULL, ws))
165 unsigned char *q, *r;
168 for (q = r = (unsigned char *)p; *r; ++q)
172 /* handle special escapes */
196 case 'p': /* pause */
200 case 'S': /* space */
203 case 'x': /* hexdigit */
205 *r++ = getdigit(&q, 16, 2);
208 case '0': /* octal */
210 *r++ = getdigit(&q, 8, 3);
213 default: /* literal */
216 case 0: /* not past eos */
221 /* copy standard character */
226 /* Remove surrounding quotes, if any
228 if (*p == '"' || *p == '\'') {
229 q = strrchr(p+1, *p);
230 if (q != NULL && *q == *p && q[1] == '\0') {
249 * clean a character for display (ctrl/meta character)
258 static char tmpbuf[5];
259 char * tmp = buf ? *buf : tmpbuf;
271 } else if (ch == 127) {
285 * clean a string for display (ctrl/meta characters)
290 const unsigned char *s;
293 static unsigned char * tmp = NULL;
294 static int tmplen = 0;
296 if (tmplen < l * 4 + 1)
297 tmp = realloc(tmp, tmplen = l * 4 + 1);
301 return (char *)"(mem alloc error)";
307 cleanchr(&p, s[i++]);
316 * return result as an pseudo-english word
323 static const char * results[] = {
324 "OK", "MEMERROR", "IOERROR", "TIMEOUT"
326 return results[r & 3];
332 * scan input for an expected string
341 if (chat_debug & CHATDEBUG_EXPECT)
342 syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
344 if ((len = strlen(str)) > 0) {
348 if ((got = malloc(len + 1)) == NULL)
352 memset(got, 0, len+1);
356 while (r == 0 && i < len) {
362 if (read(STDIN_FILENO, &ch, 1) == 1) {
364 if (chat_debug & CHATDEBUG_RECEIVE)
365 syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
366 cleanchr(NULL, ch), i);
373 /* See if we can resync on a
374 * partial match in our buffer
376 while (j < i && memcmp(got + j, str, i - j) != NULL)
379 memcpy(got, got + j, i - j);
393 if (chat_debug & CHATDEBUG_EXPECT)
394 syslog(LOG_DEBUG, "chat_expect %s", result(r));
411 if (chat_debug && CHATDEBUG_SEND)
412 syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
417 while (r == 0 && *str)
419 unsigned char ch = (unsigned char)*str++;
423 else if (ch == PAUSE_CH)
424 usleep(500000); /* 1/2 second */
426 usleep(10000); /* be kind to modem */
427 if (write(STDOUT_FILENO, &ch, 1) != 1)
436 if (chat_debug & CHATDEBUG_SEND)
437 syslog(LOG_DEBUG, "chat_send %s", result(r));
447 * -1 - no script supplied
448 * 0 - script terminated correctly
449 * 1 - invalid argument, expect string too large, etc.
450 * 2 - error on an I/O operation or fatal error condition
451 * 3 - timeout waiting for a simple string
454 * char *scrstr - unparsed chat script
455 * timeout - seconds timeout
456 * debug - debug value (bitmask)
460 getty_chat(scrstr, timeout, debug)
466 chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
469 if (scrstr != NULL) {
472 if (chat_debug & CHATDEBUG_MISC)
473 syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
475 if ((script = read_chat(&scrstr)) != NULL) {
481 * We need to be in raw mode for all this
485 old_alarm = signal(SIGALRM, chat_alrm);
486 chat_unalarm(); /* Force blocking mode at start */
489 * This is the send/expect loop
491 while (r == 0 && script[i] != NULL)
492 if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
493 r = chat_send(script[i++]);
495 signal(SIGALRM, old_alarm);
500 * Ensure stdin is in blocking mode
502 ioctl(STDIN_FILENO, FIONBIO, &off);
505 if (chat_debug & CHATDEBUG_MISC)
506 syslog(LOG_DEBUG, "getty_chat %s", result(r));