Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/libexec/getty/chat.c
34822 views
1
/*-
2
* Copyright (c) 1997
3
* David L Nugent <[email protected]>.
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
}
486
487