]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/getty.tproj/chat.c
Merge branch 'apple'
[apple_cmds.git] / system_cmds / getty.tproj / chat.c
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
26 #include <sys/cdefs.h>
27
28 #ifndef lint
29 __unused static const char rcsid[] =
30 "$FreeBSD: src/libexec/getty/chat.c,v 1.11 2005/04/06 17:42:24 stefanf Exp $";
31 #endif /* not lint */
32
33 #include <sys/types.h>
34 #include <sys/ioctl.h>
35 #include <sys/utsname.h>
36
37 #include <ctype.h>
38 #include <signal.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <unistd.h>
43
44 #include "gettytab.h"
45 #include "extern.h"
46
47 #define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
48
49 #define CHATDEBUG_RECEIVE 0x01
50 #define CHATDEBUG_SEND 0x02
51 #define CHATDEBUG_EXPECT 0x04
52 #define CHATDEBUG_MISC 0x08
53
54 #define CHATDEBUG_DEFAULT 0
55 #define CHAT_DEFAULT_TIMEOUT 10
56
57
58 static int chat_debug = CHATDEBUG_DEFAULT;
59 static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
60
61 static volatile int alarmed = 0;
62
63
64 static void chat_alrm(int);
65 static int chat_unalarm(void);
66 static int getdigit(unsigned char **, int, int);
67 static char **read_chat(char **);
68 static char *cleanchr(char **, unsigned char);
69 static char *cleanstr(const char *, int);
70 static const char *result(int);
71 static int chat_expect(const char *);
72 static int chat_send(char const *);
73
74
75 /*
76 * alarm signal handler
77 * handle timeouts in read/write
78 * change stdin to non-blocking mode to prevent
79 * possible hang in read().
80 */
81
82 static void
83 chat_alrm(int signo)
84 {
85 int on = 1;
86
87 alarm(1);
88 alarmed = 1;
89 signal(SIGALRM, chat_alrm);
90 ioctl(STDIN_FILENO, FIONBIO, &on);
91 }
92
93
94 /*
95 * Turn back on blocking mode reset by chat_alrm()
96 */
97
98 static int
99 chat_unalarm(void)
100 {
101 int off = 0;
102 return ioctl(STDIN_FILENO, FIONBIO, &off);
103 }
104
105
106 /*
107 * convert a string of a given base (octal/hex) to binary
108 */
109
110 static int
111 getdigit(unsigned char **ptr, int base, int max)
112 {
113 int i, val = 0;
114 unsigned char * q;
115
116 static const char xdigits[] = "0123456789abcdef";
117
118 for (i = 0, q = *ptr; i++ < max; ++q) {
119 int sval;
120 const char * s = strchr(xdigits, tolower(*q));
121
122 if (s == NULL || (sval = s - xdigits) >= base)
123 break;
124 val = (val * base) + sval;
125 }
126 *ptr = q;
127 return val;
128 }
129
130
131 /*
132 * read_chat()
133 * Convert a whitespace delimtied string into an array
134 * of strings, being expect/send pairs
135 */
136
137 static char **
138 read_chat(char **chatstr)
139 {
140 char *str = *chatstr;
141 char **res = NULL;
142
143 if (str != NULL) {
144 char *tmp = NULL;
145 int l;
146
147 if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
148 (res=malloc((l / 2 + 1) * sizeof(char *))) != NULL) {
149 static char ws[] = " \t";
150 char * p;
151
152 for (l = 0, p = strtok(strcpy(tmp, str), ws);
153 p != NULL;
154 p = strtok(NULL, ws))
155 {
156 unsigned char *q, *r;
157
158 /* Read escapes */
159 for (q = r = (unsigned char *)p; *r; ++q)
160 {
161 if (*q == '\\')
162 {
163 /* handle special escapes */
164 switch (*++q)
165 {
166 case 'a': /* bell */
167 *r++ = '\a';
168 break;
169 case 'r': /* cr */
170 *r++ = '\r';
171 break;
172 case 'n': /* nl */
173 *r++ = '\n';
174 break;
175 case 'f': /* ff */
176 *r++ = '\f';
177 break;
178 case 'b': /* bs */
179 *r++ = '\b';
180 break;
181 case 'e': /* esc */
182 *r++ = 27;
183 break;
184 case 't': /* tab */
185 *r++ = '\t';
186 break;
187 case 'p': /* pause */
188 *r++ = PAUSE_CH;
189 break;
190 case 's':
191 case 'S': /* space */
192 *r++ = ' ';
193 break;
194 case 'x': /* hexdigit */
195 ++q;
196 *r++ = getdigit(&q, 16, 2);
197 --q;
198 break;
199 case '0': /* octal */
200 ++q;
201 *r++ = getdigit(&q, 8, 3);
202 --q;
203 break;
204 default: /* literal */
205 *r++ = *q;
206 break;
207 case 0: /* not past eos */
208 --q;
209 break;
210 }
211 } else {
212 /* copy standard character */
213 *r++ = *q;
214 }
215 }
216
217 /* Remove surrounding quotes, if any
218 */
219 if (*p == '"' || *p == '\'') {
220 q = (unsigned char*)strrchr(p+1, *p);
221 if (q != NULL && *q == *p && q[1] == '\0') {
222 *q = '\0';
223 strcpy(p, p+1);
224 }
225 }
226
227 res[l++] = p;
228 }
229 res[l] = NULL;
230 *chatstr = tmp;
231 return res;
232 }
233 free(tmp);
234 }
235 return res;
236 }
237
238
239 /*
240 * clean a character for display (ctrl/meta character)
241 */
242
243 static char *
244 cleanchr(char **buf, unsigned char ch)
245 {
246 int l;
247 static char tmpbuf[5];
248 char * tmp = buf ? *buf : tmpbuf;
249
250 if (ch & 0x80) {
251 strcpy(tmp, "M-");
252 l = 2;
253 ch &= 0x7f;
254 } else
255 l = 0;
256
257 if (ch < 32) {
258 tmp[l++] = '^';
259 tmp[l++] = ch + '@';
260 } else if (ch == 127) {
261 tmp[l++] = '^';
262 tmp[l++] = '?';
263 } else
264 tmp[l++] = ch;
265 tmp[l] = '\0';
266
267 if (buf)
268 *buf = tmp + l;
269 return tmp;
270 }
271
272
273 /*
274 * clean a string for display (ctrl/meta characters)
275 */
276
277 static char *
278 cleanstr(const char *s, int l)
279 {
280 static char * tmp = NULL;
281 static int tmplen = 0;
282
283 if (tmplen < l * 4 + 1)
284 tmp = realloc(tmp, tmplen = l * 4 + 1);
285
286 if (tmp == NULL) {
287 tmplen = 0;
288 return (char *)"(mem alloc error)";
289 } else {
290 int i = 0;
291 char * p = tmp;
292
293 while (i < l)
294 cleanchr(&p, s[i++]);
295 *p = '\0';
296 }
297
298 return tmp;
299 }
300
301
302 /*
303 * return result as a pseudo-english word
304 */
305
306 static const char *
307 result(int r)
308 {
309 static const char * results[] = {
310 "OK", "MEMERROR", "IOERROR", "TIMEOUT"
311 };
312 return results[r & 3];
313 }
314
315
316 /*
317 * chat_expect()
318 * scan input for an expected string
319 */
320
321 static int
322 chat_expect(const char *str)
323 {
324 int len, r = 0;
325
326 if (chat_debug & CHATDEBUG_EXPECT)
327 syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
328
329 if ((len = strlen(str)) > 0) {
330 int i = 0;
331 char * got;
332
333 if ((got = malloc(len + 1)) == NULL)
334 r = 1;
335 else {
336
337 memset(got, 0, len+1);
338 alarm(chat_alarm);
339 alarmed = 0;
340
341 while (r == 0 && i < len) {
342 if (alarmed)
343 r = 3;
344 else {
345 unsigned char ch;
346
347 if (read(STDIN_FILENO, &ch, 1) == 1) {
348
349 if (chat_debug & CHATDEBUG_RECEIVE)
350 syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
351 cleanchr(NULL, ch), i);
352
353 if (ch == str[i])
354 got[i++] = ch;
355 else if (i > 0) {
356 int j = 1;
357
358 /* See if we can resync on a
359 * partial match in our buffer
360 */
361 while (j < i && memcmp(got + j, str, i - j) != 0)
362 j++;
363 if (j < i)
364 memcpy(got, got + j, i - j);
365 i -= j;
366 }
367 } else
368 r = alarmed ? 3 : 2;
369 }
370 }
371 alarm(0);
372 chat_unalarm();
373 alarmed = 0;
374 free(got);
375 }
376 }
377
378 if (chat_debug & CHATDEBUG_EXPECT)
379 syslog(LOG_DEBUG, "chat_expect %s", result(r));
380
381 return r;
382 }
383
384
385 /*
386 * chat_send()
387 * send a chat string
388 */
389
390 static int
391 chat_send(char const *str)
392 {
393 int r = 0;
394
395 if (chat_debug & CHATDEBUG_SEND)
396 syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
397
398 if (*str) {
399 alarm(chat_alarm);
400 alarmed = 0;
401 while (r == 0 && *str)
402 {
403 unsigned char ch = (unsigned char)*str++;
404
405 if (alarmed)
406 r = 3;
407 else if (ch == PAUSE_CH)
408 usleep(500000); /* 1/2 second */
409 else {
410 usleep(10000); /* be kind to modem */
411 if (write(STDOUT_FILENO, &ch, 1) != 1)
412 r = alarmed ? 3 : 2;
413 }
414 }
415 alarm(0);
416 chat_unalarm();
417 alarmed = 0;
418 }
419
420 if (chat_debug & CHATDEBUG_SEND)
421 syslog(LOG_DEBUG, "chat_send %s", result(r));
422
423 return r;
424 }
425
426
427 /*
428 * getty_chat()
429 *
430 * Termination codes:
431 * -1 - no script supplied
432 * 0 - script terminated correctly
433 * 1 - invalid argument, expect string too large, etc.
434 * 2 - error on an I/O operation or fatal error condition
435 * 3 - timeout waiting for a simple string
436 *
437 * Parameters:
438 * char *scrstr - unparsed chat script
439 * timeout - seconds timeout
440 * debug - debug value (bitmask)
441 */
442
443 int
444 getty_chat(char *scrstr, int timeout, int debug)
445 {
446 int r = -1;
447
448 chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
449 chat_debug = debug;
450
451 if (scrstr != NULL) {
452 char **script;
453
454 if (chat_debug & CHATDEBUG_MISC)
455 syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
456
457 if ((script = read_chat(&scrstr)) != NULL) {
458 int i = r = 0;
459 int off = 0;
460 sig_t old_alarm;
461
462 /*
463 * We need to be in raw mode for all this
464 * Rely on caller...
465 */
466
467 old_alarm = signal(SIGALRM, chat_alrm);
468 chat_unalarm(); /* Force blocking mode at start */
469
470 /*
471 * This is the send/expect loop
472 */
473 while (r == 0 && script[i] != NULL)
474 if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
475 r = chat_send(script[i++]);
476
477 signal(SIGALRM, old_alarm);
478 free(script);
479 free(scrstr);
480
481 /*
482 * Ensure stdin is in blocking mode
483 */
484 ioctl(STDIN_FILENO, FIONBIO, &off);
485 }
486
487 if (chat_debug & CHATDEBUG_MISC)
488 syslog(LOG_DEBUG, "getty_chat %s", result(r));
489
490 }
491 return r;
492 }