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