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