Move old locale sources into the attic.
[dragonfly/netmp.git] / libexec / getty / chat.c
blob5d2dcc9c40403bf14cf2c5e4bf719354d7fa6bb5
1 /*-
2 * Copyright (c) 1997
3 * David L Nugent <davidn@blaze.net.au>.
4 * All rights reserved.
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.
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>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <sys/resource.h>
33 #include <sys/ttydefaults.h>
34 #include <sys/utsname.h>
35 #include <ctype.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <libutil.h>
39 #include <locale.h>
40 #include <setjmp.h>
41 #include <signal.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <syslog.h>
45 #include <time.h>
46 #include <termios.h>
47 #include <unistd.h>
48 #include <sys/socket.h>
50 #include "extern.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().
87 static void
88 chat_alrm(int signo)
90 int on = 1;
92 alarm(1);
93 alarmed = 1;
94 signal(SIGALRM, chat_alrm);
95 ioctl(STDIN_FILENO, FIONBIO, &on);
100 * Turn back on blocking mode reset by chat_alrm()
103 static int
104 chat_unalarm(void)
106 int off = 0;
108 return ioctl(STDIN_FILENO, FIONBIO, &off);
113 * convert a string of a given base (octal/hex) to binary
116 static int
117 getdigit(unsigned char **ptr, int base, int max)
119 int i, val = 0;
120 char * q;
122 static const char xdigits[] = "0123456789abcdef";
124 for (i = 0, q = *ptr; i++ < max; ++q) {
125 int sval;
126 const char * s = strchr(xdigits, tolower(*q));
128 if (s == NULL || (sval = s - xdigits) >= base)
129 break;
130 val = (val * base) + sval;
132 *ptr = q;
133 return val;
138 * read_chat()
139 * Convert a whitespace delimtied string into an array
140 * of strings, being expect/send pairs
143 static char **
144 read_chat(char **chatstr)
146 char *str = *chatstr;
147 char **res = NULL;
149 if (str != NULL) {
150 char *tmp = NULL;
151 int l;
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";
156 char * p;
158 for (l = 0, p = strtok(strcpy(tmp, str), ws);
159 p != NULL;
160 p = strtok(NULL, ws))
162 unsigned char *q, *r;
164 /* Read escapes */
165 for (q = r = (unsigned char *)p; *r; ++q)
167 if (*q == '\\')
169 /* handle special escapes */
170 switch (*++q)
172 case 'a': /* bell */
173 *r++ = '\a';
174 break;
175 case 'r': /* cr */
176 *r++ = '\r';
177 break;
178 case 'n': /* nl */
179 *r++ = '\n';
180 break;
181 case 'f': /* ff */
182 *r++ = '\f';
183 break;
184 case 'b': /* bs */
185 *r++ = '\b';
186 break;
187 case 'e': /* esc */
188 *r++ = 27;
189 break;
190 case 't': /* tab */
191 *r++ = '\t';
192 break;
193 case 'p': /* pause */
194 *r++ = PAUSE_CH;
195 break;
196 case 's':
197 case 'S': /* space */
198 *r++ = ' ';
199 break;
200 case 'x': /* hexdigit */
201 ++q;
202 *r++ = getdigit(&q, 16, 2);
203 --q;
204 break;
205 case '0': /* octal */
206 ++q;
207 *r++ = getdigit(&q, 8, 3);
208 --q;
209 break;
210 default: /* literal */
211 *r++ = *q;
212 break;
213 case 0: /* not past eos */
214 --q;
215 break;
217 } else {
218 /* copy standard character */
219 *r++ = *q;
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') {
228 *q = '\0';
229 strcpy(p, p+1);
233 res[l++] = p;
235 res[l] = NULL;
236 *chatstr = tmp;
237 return res;
239 free(tmp);
241 return res;
246 * clean a character for display (ctrl/meta character)
249 static char *
250 cleanchr(char **buf, unsigned char ch)
252 int l;
253 static char tmpbuf[5];
254 char * tmp = buf ? *buf : tmpbuf;
256 if (ch & 0x80) {
257 strcpy(tmp, "M-");
258 l = 2;
259 ch &= 0x7f;
260 } else
261 l = 0;
263 if (ch < 32) {
264 tmp[l++] = '^';
265 tmp[l++] = ch + '@';
266 } else if (ch == 127) {
267 tmp[l++] = '^';
268 tmp[l++] = '?';
269 } else
270 tmp[l++] = ch;
271 tmp[l] = '\0';
273 if (buf)
274 *buf = tmp + l;
275 return tmp;
280 * clean a string for display (ctrl/meta characters)
283 static char *
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);
292 if (tmp == NULL) {
293 tmplen = 0;
294 return (char *)"(mem alloc error)";
295 } else {
296 int i = 0;
297 char * p = tmp;
299 while (i < l)
300 cleanchr(&p, s[i++]);
301 *p = '\0';
304 return tmp;
309 * return result as an pseudo-english word
312 static const char *
313 result(int r)
315 static const char * results[] = {
316 "OK", "MEMERROR", "IOERROR", "TIMEOUT"
318 return results[r & 3];
323 * chat_expect()
324 * scan input for an expected string
327 static int
328 chat_expect(const char *str)
330 int len, r = 0;
332 if (chat_debug & CHATDEBUG_EXPECT)
333 syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
335 if ((len = strlen(str)) > 0) {
336 int i = 0;
337 char * got;
339 if ((got = malloc(len + 1)) == NULL)
340 r = 1;
341 else {
343 memset(got, 0, len+1);
344 alarm(chat_alarm);
345 alarmed = 0;
347 while (r == 0 && i < len) {
348 if (alarmed)
349 r = 3;
350 else {
351 unsigned char ch;
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);
359 if (ch == str[i])
360 got[i++] = ch;
361 else if (i > 0) {
362 int j = 1;
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)
368 j++;
369 if (j < i)
370 memcpy(got, got + j, i - j);
371 i -= j;
373 } else
374 r = alarmed ? 3 : 2;
377 alarm(0);
378 chat_unalarm();
379 alarmed = 0;
380 free(got);
384 if (chat_debug & CHATDEBUG_EXPECT)
385 syslog(LOG_DEBUG, "chat_expect %s", result(r));
387 return r;
392 * chat_send()
393 * send a chat string
396 static int
397 chat_send(char const *str)
399 int r = 0;
401 if (chat_debug && CHATDEBUG_SEND)
402 syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
404 if (*str) {
405 alarm(chat_alarm);
406 alarmed = 0;
407 while (r == 0 && *str)
409 unsigned char ch = (unsigned char)*str++;
411 if (alarmed)
412 r = 3;
413 else if (ch == PAUSE_CH)
414 usleep(500000); /* 1/2 second */
415 else {
416 usleep(10000); /* be kind to modem */
417 if (write(STDOUT_FILENO, &ch, 1) != 1)
418 r = alarmed ? 3 : 2;
421 alarm(0);
422 chat_unalarm();
423 alarmed = 0;
426 if (chat_debug & CHATDEBUG_SEND)
427 syslog(LOG_DEBUG, "chat_send %s", result(r));
429 return r;
434 * getty_chat()
436 * Termination codes:
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
443 * Parameters:
444 * char *scrstr - unparsed chat script
445 * timeout - seconds timeout
446 * debug - debug value (bitmask)
450 getty_chat(char *scrstr, int timeout, int debug)
452 int r = -1;
454 chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
455 chat_debug = debug;
457 if (scrstr != NULL) {
458 char **script;
460 if (chat_debug & CHATDEBUG_MISC)
461 syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
463 if ((script = read_chat(&scrstr)) != NULL) {
464 int i = r = 0;
465 int off = 0;
466 sig_t old_alarm;
469 * We need to be in raw mode for all this
470 * Rely on caller...
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);
484 free(script);
485 free(scrstr);
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));
497 return r;