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