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.4 2004/03/26 00:30:12 cpressey 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().
94 signal(SIGALRM
, chat_alrm
);
95 ioctl(STDIN_FILENO
, FIONBIO
, &on
);
100 * 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(unsigned char **ptr
, int base
, int max
)
122 static const char xdigits
[] = "0123456789abcdef";
124 for (i
= 0, q
= *ptr
; i
++ < max
; ++q
) {
126 const char * s
= strchr(xdigits
, tolower(*q
));
128 if (s
== NULL
|| (sval
= s
- xdigits
) >= base
)
130 val
= (val
* base
) + sval
;
139 * Convert a whitespace delimtied string into an array
140 * of strings, being expect/send pairs
144 read_chat(char **chatstr
)
146 char *str
= *chatstr
;
153 if ((l
=strlen(str
)) > 0 && (tmp
=malloc(l
+ 1)) != NULL
&&
154 (res
=malloc((l
/ 2 + 1) * sizeof(char *))) != NULL
) {
155 static char ws
[] = " \t";
158 for (l
= 0, p
= strtok(strcpy(tmp
, str
), ws
);
160 p
= strtok(NULL
, ws
))
162 unsigned char *q
, *r
;
165 for (q
= r
= (unsigned char *)p
; *r
; ++q
)
169 /* handle special escapes */
193 case 'p': /* pause */
197 case 'S': /* space */
200 case 'x': /* hexdigit */
202 *r
++ = getdigit(&q
, 16, 2);
205 case '0': /* octal */
207 *r
++ = getdigit(&q
, 8, 3);
210 default: /* literal */
213 case 0: /* not past eos */
218 /* copy standard character */
223 /* Remove surrounding quotes, if any
225 if (*p
== '"' || *p
== '\'') {
226 q
= strrchr(p
+1, *p
);
227 if (q
!= NULL
&& *q
== *p
&& q
[1] == '\0') {
246 * clean a character for display (ctrl/meta character)
250 cleanchr(char **buf
, unsigned char ch
)
253 static char tmpbuf
[5];
254 char * tmp
= buf
? *buf
: tmpbuf
;
266 } else if (ch
== 127) {
280 * clean a string for display (ctrl/meta characters)
284 cleanstr(const unsigned char *s
, int l
)
286 static unsigned char * tmp
= NULL
;
287 static int tmplen
= 0;
289 if (tmplen
< l
* 4 + 1)
290 tmp
= realloc(tmp
, tmplen
= l
* 4 + 1);
294 return (char *)"(mem alloc error)";
300 cleanchr(&p
, s
[i
++]);
309 * return result as an pseudo-english word
315 static const char * results
[] = {
316 "OK", "MEMERROR", "IOERROR", "TIMEOUT"
318 return results
[r
& 3];
324 * scan input for an expected string
328 chat_expect(const char *str
)
332 if (chat_debug
& CHATDEBUG_EXPECT
)
333 syslog(LOG_DEBUG
, "chat_expect '%s'", cleanstr(str
, strlen(str
)));
335 if ((len
= strlen(str
)) > 0) {
339 if ((got
= malloc(len
+ 1)) == NULL
)
343 memset(got
, 0, len
+1);
347 while (r
== 0 && i
< len
) {
353 if (read(STDIN_FILENO
, &ch
, 1) == 1) {
355 if (chat_debug
& CHATDEBUG_RECEIVE
)
356 syslog(LOG_DEBUG
, "chat_recv '%s' m=%d",
357 cleanchr(NULL
, ch
), i
);
364 /* See if we can resync on a
365 * partial match in our buffer
367 while (j
< i
&& memcmp(got
+ j
, str
, i
- j
) != NULL
)
370 memcpy(got
, got
+ j
, i
- j
);
384 if (chat_debug
& CHATDEBUG_EXPECT
)
385 syslog(LOG_DEBUG
, "chat_expect %s", result(r
));
397 chat_send(char const *str
)
401 if (chat_debug
&& CHATDEBUG_SEND
)
402 syslog(LOG_DEBUG
, "chat_send '%s'", cleanstr(str
, strlen(str
)));
407 while (r
== 0 && *str
)
409 unsigned char ch
= (unsigned char)*str
++;
413 else if (ch
== PAUSE_CH
)
414 usleep(500000); /* 1/2 second */
416 usleep(10000); /* be kind to modem */
417 if (write(STDOUT_FILENO
, &ch
, 1) != 1)
426 if (chat_debug
& CHATDEBUG_SEND
)
427 syslog(LOG_DEBUG
, "chat_send %s", result(r
));
437 * -1 - no script supplied
438 * 0 - script terminated correctly
439 * 1 - invalid argument, expect string too large, etc.
440 * 2 - error on an I/O operation or fatal error condition
441 * 3 - timeout waiting for a simple string
444 * char *scrstr - unparsed chat script
445 * timeout - seconds timeout
446 * debug - debug value (bitmask)
450 getty_chat(char *scrstr
, int timeout
, int debug
)
454 chat_alarm
= timeout
? timeout
: CHAT_DEFAULT_TIMEOUT
;
457 if (scrstr
!= NULL
) {
460 if (chat_debug
& CHATDEBUG_MISC
)
461 syslog(LOG_DEBUG
, "getty_chat script='%s'", scrstr
);
463 if ((script
= read_chat(&scrstr
)) != NULL
) {
469 * We need to be in raw mode for all this
473 old_alarm
= signal(SIGALRM
, chat_alrm
);
474 chat_unalarm(); /* Force blocking mode at start */
477 * This is the send/expect loop
479 while (r
== 0 && script
[i
] != NULL
)
480 if ((r
= chat_expect(script
[i
++])) == 0 && script
[i
] != NULL
)
481 r
= chat_send(script
[i
++]);
483 signal(SIGALRM
, old_alarm
);
488 * Ensure stdin is in blocking mode
490 ioctl(STDIN_FILENO
, FIONBIO
, &off
);
493 if (chat_debug
& CHATDEBUG_MISC
)
494 syslog(LOG_DEBUG
, "getty_chat %s", result(r
));