calloc - Work around gcc-8 bug (2).
[dragonfly.git] / libexec / getty / chat.c
blobb7435a34ab36190733e548a57bd25732b6e33224
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: head/libexec/getty/chat.c 329724 2018-02-21 15:57:24Z trasz $
28 #include <sys/types.h>
29 #include <sys/ioctl.h>
30 #include <sys/utsname.h>
32 #include <ctype.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <syslog.h>
37 #include <unistd.h>
39 #include "gettytab.h"
40 #include "extern.h"
42 #define PAUSE_CH (unsigned char)'\xff' /* pause kludge */
44 #define CHATDEBUG_RECEIVE 0x01
45 #define CHATDEBUG_SEND 0x02
46 #define CHATDEBUG_EXPECT 0x04
47 #define CHATDEBUG_MISC 0x08
49 #define CHATDEBUG_DEFAULT 0
50 #define CHAT_DEFAULT_TIMEOUT 10
53 static int chat_debug = CHATDEBUG_DEFAULT;
54 static int chat_alarm = CHAT_DEFAULT_TIMEOUT; /* Default */
56 static volatile int alarmed = 0;
59 static void chat_alrm(int);
60 static int chat_unalarm(void);
61 static int getdigit(unsigned char **, int, int);
62 static char **read_chat(char **);
63 static char *cleanchr(char **, unsigned char);
64 static const char *cleanstr(const unsigned char *, int);
65 static const char *result(int);
66 static int chat_expect(const char *);
67 static int chat_send(char const *);
71 * alarm signal handler
72 * handle timeouts in read/write
73 * change stdin to non-blocking mode to prevent
74 * possible hang in read().
77 static void
78 chat_alrm(int signo __unused)
80 int on = 1;
82 alarm(1);
83 alarmed = 1;
84 signal(SIGALRM, chat_alrm);
85 ioctl(STDIN_FILENO, FIONBIO, &on);
90 * Turn back on blocking mode reset by chat_alrm()
93 static int
94 chat_unalarm(void)
96 int off = 0;
98 return ioctl(STDIN_FILENO, FIONBIO, &off);
103 * convert a string of a given base (octal/hex) to binary
106 static int
107 getdigit(unsigned char **ptr, int base, int max)
109 int i, val = 0;
110 char * q;
112 static const char xdigits[] = "0123456789abcdef";
114 for (i = 0, q = *ptr; i++ < max; ++q) {
115 int sval;
116 const char * s = strchr(xdigits, tolower(*q));
118 if (s == NULL || (sval = s - xdigits) >= base)
119 break;
120 val = (val * base) + sval;
122 *ptr = q;
123 return val;
128 * read_chat()
129 * Convert a whitespace delimtied string into an array
130 * of strings, being expect/send pairs
133 static char **
134 read_chat(char **chatstr)
136 char *str = *chatstr;
137 char **res = NULL;
139 if (str != NULL) {
140 char *tmp = NULL;
141 int l;
143 if ((l=strlen(str)) > 0 && (tmp=malloc(l + 1)) != NULL &&
144 (res=malloc((l / 2 + 1) * sizeof(char *))) != NULL) {
145 static char ws[] = " \t";
146 char * p;
148 for (l = 0, p = strtok(strcpy(tmp, str), ws);
149 p != NULL;
150 p = strtok(NULL, ws))
152 unsigned char *q, *r;
154 /* Read escapes */
155 for (q = r = (unsigned char *)p; *r; ++q)
157 if (*q == '\\')
159 /* handle special escapes */
160 switch (*++q)
162 case 'a': /* bell */
163 *r++ = '\a';
164 break;
165 case 'r': /* cr */
166 *r++ = '\r';
167 break;
168 case 'n': /* nl */
169 *r++ = '\n';
170 break;
171 case 'f': /* ff */
172 *r++ = '\f';
173 break;
174 case 'b': /* bs */
175 *r++ = '\b';
176 break;
177 case 'e': /* esc */
178 *r++ = 27;
179 break;
180 case 't': /* tab */
181 *r++ = '\t';
182 break;
183 case 'p': /* pause */
184 *r++ = PAUSE_CH;
185 break;
186 case 's':
187 case 'S': /* space */
188 *r++ = ' ';
189 break;
190 case 'x': /* hexdigit */
191 ++q;
192 *r++ = getdigit(&q, 16, 2);
193 --q;
194 break;
195 case '0': /* octal */
196 ++q;
197 *r++ = getdigit(&q, 8, 3);
198 --q;
199 break;
200 default: /* literal */
201 *r++ = *q;
202 break;
203 case 0: /* not past eos */
204 --q;
205 break;
207 } else {
208 /* copy standard character */
209 *r++ = *q;
213 /* Remove surrounding quotes, if any
215 if (*p == '"' || *p == '\'') {
216 q = strrchr(p+1, *p);
217 if (q != NULL && *q == *p && q[1] == '\0') {
218 *q = '\0';
219 strcpy(p, p+1);
223 res[l++] = p;
225 res[l] = NULL;
226 *chatstr = tmp;
227 return res;
229 free(tmp);
231 return res;
236 * clean a character for display (ctrl/meta character)
239 static char *
240 cleanchr(char **buf, unsigned char ch)
242 int l;
243 static char tmpbuf[5];
244 char * tmp = buf ? *buf : tmpbuf;
246 if (ch & 0x80) {
247 strcpy(tmp, "M-");
248 l = 2;
249 ch &= 0x7f;
250 } else
251 l = 0;
253 if (ch < 32) {
254 tmp[l++] = '^';
255 tmp[l++] = ch + '@';
256 } else if (ch == 127) {
257 tmp[l++] = '^';
258 tmp[l++] = '?';
259 } else
260 tmp[l++] = ch;
261 tmp[l] = '\0';
263 if (buf)
264 *buf = tmp + l;
265 return tmp;
270 * clean a string for display (ctrl/meta characters)
273 static const char *
274 cleanstr(const unsigned char *s, int l)
276 static unsigned char * tmp = NULL;
277 static int tmplen = 0;
279 if (tmplen < l * 4 + 1)
280 tmp = realloc(tmp, tmplen = l * 4 + 1);
282 if (tmp == NULL) {
283 tmplen = 0;
284 return "(mem alloc error)";
285 } else {
286 int i = 0;
287 char * p = tmp;
289 while (i < l)
290 cleanchr(&p, s[i++]);
291 *p = '\0';
294 return tmp;
299 * return result as a pseudo-english word
302 static const char *
303 result(int r)
305 static const char * results[] = {
306 "OK", "MEMERROR", "IOERROR", "TIMEOUT"
308 return results[r & 3];
313 * chat_expect()
314 * scan input for an expected string
317 static int
318 chat_expect(const char *str)
320 int len, r = 0;
322 if (chat_debug & CHATDEBUG_EXPECT)
323 syslog(LOG_DEBUG, "chat_expect '%s'", cleanstr(str, strlen(str)));
325 if ((len = strlen(str)) > 0) {
326 int i = 0;
327 char * got;
329 if ((got = malloc(len + 1)) == NULL)
330 r = 1;
331 else {
333 memset(got, 0, len+1);
334 alarm(chat_alarm);
335 alarmed = 0;
337 while (r == 0 && i < len) {
338 if (alarmed)
339 r = 3;
340 else {
341 unsigned char ch;
343 if (read(STDIN_FILENO, &ch, 1) == 1) {
345 if (chat_debug & CHATDEBUG_RECEIVE)
346 syslog(LOG_DEBUG, "chat_recv '%s' m=%d",
347 cleanchr(NULL, ch), i);
349 if (ch == str[i])
350 got[i++] = ch;
351 else if (i > 0) {
352 int j = 1;
354 /* See if we can resync on a
355 * partial match in our buffer
357 while (j < i && memcmp(got + j, str, i - j) != 0)
358 j++;
359 if (j < i)
360 memcpy(got, got + j, i - j);
361 i -= j;
363 } else
364 r = alarmed ? 3 : 2;
367 alarm(0);
368 chat_unalarm();
369 alarmed = 0;
370 free(got);
374 if (chat_debug & CHATDEBUG_EXPECT)
375 syslog(LOG_DEBUG, "chat_expect %s", result(r));
377 return r;
382 * chat_send()
383 * send a chat string
386 static int
387 chat_send(char const *str)
389 int r = 0;
391 if (chat_debug & CHATDEBUG_SEND)
392 syslog(LOG_DEBUG, "chat_send '%s'", cleanstr(str, strlen(str)));
394 if (*str) {
395 alarm(chat_alarm);
396 alarmed = 0;
397 while (r == 0 && *str)
399 unsigned char ch = (unsigned char)*str++;
401 if (alarmed)
402 r = 3;
403 else if (ch == PAUSE_CH)
404 usleep(500000); /* 1/2 second */
405 else {
406 usleep(10000); /* be kind to modem */
407 if (write(STDOUT_FILENO, &ch, 1) != 1)
408 r = alarmed ? 3 : 2;
411 alarm(0);
412 chat_unalarm();
413 alarmed = 0;
416 if (chat_debug & CHATDEBUG_SEND)
417 syslog(LOG_DEBUG, "chat_send %s", result(r));
419 return r;
424 * getty_chat()
426 * Termination codes:
427 * -1 - no script supplied
428 * 0 - script terminated correctly
429 * 1 - invalid argument, expect string too large, etc.
430 * 2 - error on an I/O operation or fatal error condition
431 * 3 - timeout waiting for a simple string
433 * Parameters:
434 * char *scrstr - unparsed chat script
435 * timeout - seconds timeout
436 * debug - debug value (bitmask)
440 getty_chat(char *scrstr, int timeout, int debug)
442 int r = -1;
444 chat_alarm = timeout ? timeout : CHAT_DEFAULT_TIMEOUT;
445 chat_debug = debug;
447 if (scrstr != NULL) {
448 char **script;
450 if (chat_debug & CHATDEBUG_MISC)
451 syslog(LOG_DEBUG, "getty_chat script='%s'", scrstr);
453 if ((script = read_chat(&scrstr)) != NULL) {
454 int i = r = 0;
455 int off = 0;
456 sig_t old_alarm;
459 * We need to be in raw mode for all this
460 * Rely on caller...
463 old_alarm = signal(SIGALRM, chat_alrm);
464 chat_unalarm(); /* Force blocking mode at start */
467 * This is the send/expect loop
469 while (r == 0 && script[i] != NULL)
470 if ((r = chat_expect(script[i++])) == 0 && script[i] != NULL)
471 r = chat_send(script[i++]);
473 signal(SIGALRM, old_alarm);
474 free(script);
475 free(scrstr);
478 * Ensure stdin is in blocking mode
480 ioctl(STDIN_FILENO, FIONBIO, &off);
483 if (chat_debug & CHATDEBUG_MISC)
484 syslog(LOG_DEBUG, "getty_chat %s", result(r));
487 return r;