chat.c revision 7001
1/* 2 * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 3 * 4 * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 5 * 6 * Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com). 7 * 8 * Chat -- a program for automatic session establishment (i.e. dial 9 * the phone and log in). 10 * 11 * This software is in the public domain. 12 * 13 * Please send all bug reports, requests for information, etc. to: 14 * 15 * Karl Fox <karl@MorningStar.Com> 16 * Morning Star Technologies, Inc. 17 * 1760 Zollinger Road 18 * Columbus, OH 43221 19 * (614)451-1883 20 * 21 * $Id: chat.c,v 1.2 1995/02/26 12:17:20 amurai Exp $ 22 * 23 * TODO: 24 * o Support more UUCP compatible control sequences. 25 * o Dialing shoud not block monitor process. 26 * o Reading modem by select should be unified into main.c 27 */ 28#include "defs.h" 29#include <ctype.h> 30#include <sys/uio.h> 31#ifndef isblank 32#define isblank(c) ((c) == '\t' || (c) == ' ') 33#endif 34#include <sys/time.h> 35#include <fcntl.h> 36#include <errno.h> 37#include <signal.h> 38#include <sys/wait.h> 39#include "timeout.h" 40#include "vars.h" 41 42#define IBSIZE 200 43 44static int TimeoutSec; 45static int abort_next, timeout_next; 46static int numaborts; 47char *AbortStrings[50]; 48char inbuff[IBSIZE]; 49 50extern int ChangeParity(char *); 51 52#define MATCH 1 53#define NOMATCH 0 54#define ABORT -1 55 56static char * 57findblank(p, instring) 58char *p; 59int instring; 60{ 61 if (instring) { 62 while (*p) { 63 if (*p == '\\') { 64 strcpy(p, p + 1); 65 if (!*p) 66 break; 67 } else if (*p == '"') 68 return(p); 69 p++; 70 } 71 } else { 72 while (*p) { 73 if (isblank(*p)) 74 return(p); 75 p++; 76 } 77 } 78 return p; 79} 80 81int 82MakeArgs(script, pvect) 83char *script; 84char **pvect; 85{ 86 int nargs, nb; 87 int instring; 88 89 nargs = 0; 90 while (*script) { 91 nb = strspn(script, " \t"); 92 script += nb; 93 if (*script) { 94 if (*script == '"') { 95 instring = 1; 96 script++; 97 if (*script == '\0') 98 return(nargs); 99 } else 100 instring = 0; 101 *pvect++ = script; 102 nargs++; 103 script = findblank(script, instring); 104 if (*script) 105 *script++ = '\0'; 106 } 107 } 108 *pvect = NULL; 109 return nargs; 110} 111 112/* 113 * \r Carrige return character 114 * \s Space character 115 * \n Line feed character 116 * \T Telephone number (defined via `set phone' 117 * \t Tab character 118 */ 119char * 120ExpandString(str, result, sendmode) 121char *str; 122char *result; 123int sendmode; 124{ 125 int addcr = 0; 126 127 if (sendmode) 128 addcr = 1; 129 while (*str) { 130 switch (*str) { 131 case '\\': 132 str++; 133 switch (*str) { 134 case 'c': 135 if (sendmode) 136 addcr = 0; 137 break; 138 case 'd': /* Delay 2 seconds */ 139 sleep(2); break; 140 case 'p': 141 usleep(250000); break; /* Pause 0.25 sec */ 142 case 'n': 143 *result++ = '\n'; break; 144 case 'r': 145 *result++ = '\r'; break; 146 case 's': 147 *result++ = ' '; break; 148 case 't': 149 *result++ = '\t'; break; 150 case 'P': 151 bcopy(VarAuthKey, result, strlen(VarAuthKey)); 152 result += strlen(VarAuthKey); 153 break; 154 case 'T': 155 bcopy(VarPhone, result, strlen(VarPhone)); 156 result += strlen(VarPhone); 157 break; 158 case 'U': 159 bcopy(VarAuthName, result, strlen(VarAuthName)); 160 result += strlen(VarAuthName); 161 break; 162 default: 163 *result++ = *str; break; 164 } 165 if (*str) str++; 166 break; 167 case '^': 168 str++; 169 if (*str) 170 *result++ = *str++ & 0x1f; 171 break; 172 default: 173 *result++ = *str++; 174 break; 175 } 176 } 177 if (addcr) 178 *result++ = '\r'; 179 *result++ = '\0'; 180 return(result); 181} 182 183int 184WaitforString(estr) 185char *estr; 186{ 187 struct timeval timeout; 188 char *s, *str, ch; 189 char *inp; 190 fd_set rfds; 191 int i, nfds, nb; 192 char buff[200]; 193 194#ifdef SIGALRM 195 int omask; 196 omask = sigblock(sigmask(SIGALRM)); 197#endif 198 (void) ExpandString(estr, buff, 0); 199 LogPrintf(LOG_CHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff); 200 str = buff; 201 inp = inbuff; 202 203 nfds = modem + 1; 204 s = str; 205 for (;;) { 206 FD_ZERO(&rfds); 207 FD_SET(modem, &rfds); 208 /* 209 * Because it is not clear whether select() modifies timeout value, 210 * it is better to initialize timeout values everytime. 211 */ 212 timeout.tv_sec = TimeoutSec; 213 timeout.tv_usec = 0; 214 i = select(nfds, &rfds, NULL, NULL, &timeout); 215#ifdef notdef 216 TimerService(); 217#endif 218 if (i < 0) { 219#ifdef SIGALRM 220 if (errno == EINTR) 221 continue; 222 sigsetmask(omask); 223#endif 224 perror("select"); 225 *inp = 0; 226 return(NOMATCH); 227 } else if (i == 0) { /* Timeout reached! */ 228 *inp = 0; 229 if (inp != inbuff) 230 LogPrintf(LOG_CHAT, "got: %s\n", inbuff); 231 LogPrintf(LOG_CHAT, "can't get (%d).\n", timeout.tv_sec); 232#ifdef SIGALRM 233 sigsetmask(omask); 234#endif 235 return(NOMATCH); 236 } 237 if (FD_ISSET(modem, &rfds)) { /* got something */ 238 if (DEV_IS_SYNC) { 239 nb = read(modem, inbuff, IBSIZE-1); 240 inbuff[nb] = 0; 241 if (strstr(inbuff, str)) { 242#ifdef SIGALRM 243 sigsetmask(omask); 244#endif 245 return(MATCH); 246 } 247 for (i = 0; i < numaborts; i++) { 248 if (strstr(inbuff, AbortStrings[i])) { 249 LogPrintf(LOG_CHAT, "Abort: %s\n", AbortStrings[i]); 250#ifdef SIGALRM 251 sigsetmask(omask); 252#endif 253 return(ABORT); 254 } 255 } 256 } else { 257 read(modem, &ch, 1); 258 *inp++ = ch; 259 if (ch == *s) { 260 s++; 261 if (*s == '\0') { 262#ifdef SIGALRM 263 sigsetmask(omask); 264#endif 265 *inp = 0; 266 return(MATCH); 267 } 268 } else { 269 s = str; 270 if (inp == inbuff+ IBSIZE) { 271 bcopy(inp - 100, inbuff, 100); 272 inp = inbuff + 100; 273 } 274 for (i = 0; i < numaborts; i++) { /* Look for Abort strings */ 275 int len; 276 char *s1; 277 278 s1 = AbortStrings[i]; 279 len = strlen(s1); 280 if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) { 281 LogPrintf(LOG_CHAT, "Abort: %s\n", s1); 282 *inp = 0; 283#ifdef SIGALRM 284 sigsetmask(omask); 285#endif 286 return(ABORT); 287 } 288 } 289 } 290 } 291 } 292 } 293#ifdef SIGALRM 294 sigsetmask(omask); 295#endif 296} 297 298void 299ExecStr(command, out) 300char *command, *out; 301{ 302 int pid; 303 int fids[2]; 304 char *vector[20]; 305 int stat, nb; 306 char *cp; 307 char tmp[300]; 308 extern int errno; 309 310 cp = inbuff + strlen(inbuff) - 1; 311 while (cp > inbuff) { 312 if (*cp < ' ' && *cp != '\t') { 313 cp++; 314 break; 315 } 316 cp--; 317 } 318 sprintf(tmp, "%s %s", command, cp); 319 (void) MakeArgs(tmp, &vector); 320 321 pipe(fids); 322 pid = fork(); 323 if (pid == 0) { 324 signal(SIGINT, SIG_DFL); 325 signal(SIGQUIT, SIG_DFL); 326 signal(SIGTERM, SIG_DFL); 327 signal(SIGHUP, SIG_DFL); 328 close(fids[0]); 329 dup2(fids[1], 1); 330 close(fids[1]); 331 nb = open("/dev/tty", O_RDWR); 332 dup2(nb, 0); 333LogPrintf(LOG_CHAT, "exec: %s\n", command); 334 pid = execvp(command, vector); 335 LogPrintf(LOG_CHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command); 336 exit(127); 337 } else { 338 close(fids[1]); 339 for (;;) { 340 nb = read(fids[0], out, 1); 341 if (nb <= 0) 342 break; 343 out++; 344 } 345 *out = '\0'; 346 close(fids[0]); 347 close(fids[1]); 348 waitpid(pid, &stat, WNOHANG); 349 } 350} 351 352void 353SendString(str) 354char *str; 355{ 356 char *cp; 357 int nb, on; 358 char buff[200]; 359 360 if (abort_next) { 361 abort_next = 0; 362 ExpandString(str, buff, 0); 363 AbortStrings[numaborts++] = strdup(buff); 364 } else if (timeout_next) { 365 timeout_next = 0; 366 TimeoutSec = atoi(str); 367 if (TimeoutSec <= 0) 368 TimeoutSec = 30; 369 } else { 370 if (*str == '!') { 371 (void) ExpandString(str+1, buff+2, 0); 372 ExecStr(buff + 2, buff + 2); 373 } else { 374 (void) ExpandString(str, buff+2, 1); 375 } 376 LogPrintf(LOG_CHAT, "sending: %s\n", buff+2); 377 cp = buff; 378 if (DEV_IS_SYNC) 379 bcopy("\377\003", buff, 2); /* Prepend HDLC header */ 380 else 381 cp += 2; 382 on = strlen(cp); 383 nb = write(modem, cp, on); 384 } 385} 386 387int 388ExpectString(str) 389char *str; 390{ 391 char *minus; 392 int state; 393 394 if (strcmp(str, "ABORT") == 0) { 395 ++abort_next; 396 return(MATCH); 397 } 398 if (strcmp(str, "TIMEOUT") == 0) { 399 ++timeout_next; 400 return(MATCH); 401 } 402 LogPrintf(LOG_CHAT, "Expecting %s\n", str); 403 while (*str) { 404 /* 405 * Check whether if string contains sub-send-expect. 406 */ 407 for (minus = str; *minus; minus++) { 408 if (*minus == '-') { 409 if (minus == str || minus[-1] != '\\') 410 break; 411 } 412 } 413 if (*minus == '-') { /* We have sub-send-expect. */ 414 *minus++ = '\0'; 415 state = WaitforString(str); 416 if (state != NOMATCH) 417 return(state); 418 /* 419 * Can't get expect string. Sendout send part. 420 */ 421 str = minus; 422 for (minus = str; *minus; minus++) { 423 if (*minus == '-') { 424 if (minus == str || minus[-1] != '\\') 425 break; 426 } 427 } 428 if (*minus == '-') { 429 *minus++ = '\0'; 430 SendString(str); 431 str = minus; 432 } else { 433 SendString(str); 434 return(MATCH); 435 } 436 } else { 437 /* 438 * Simple case. Wait for string. 439 */ 440 return(WaitforString(str)); 441 } 442 } 443 return(MATCH); 444} 445 446int 447DoChat(script) 448char *script; 449{ 450 char *vector[20]; 451 char **argv; 452 int argc, n, state; 453#ifdef DEBUG 454 int i; 455#endif 456 457 timeout_next = abort_next = 0; 458 for (n = 0; AbortStrings[n]; n++) { 459 free(AbortStrings[n]); 460 AbortStrings[n] = NULL; 461 } 462 numaborts = 0; 463 464 bzero(vector, sizeof(vector)); 465 n = MakeArgs(script, &vector); 466#ifdef DEBUG 467 logprintf("n = %d\n", n); 468 for (i = 0; i < n; i++) 469 logprintf(" %s\n", vector[i]); 470#endif 471 argc = n; 472 argv = vector; 473 TimeoutSec = 30; 474 while (*argv) { 475 if (strcmp(*argv, "P_ZERO") == 0 || 476 strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) { 477 ChangeParity(*argv++); 478 continue; 479 } 480 state = ExpectString(*argv++); 481 switch (state) { 482 case MATCH: 483 if (*argv) 484 SendString(*argv++); 485 break; 486 case ABORT: 487#ifdef notdef 488 HangupModem(); 489#endif 490 case NOMATCH: 491 return(NOMATCH); 492 } 493 } 494 return(MATCH); 495} 496