chat.c revision 8857
1270866Simp/* 2270866Simp * Written by Toshiharu OHNO (tony-o@iij.ad.jp) 3270866Simp * 4270866Simp * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd. 5270866Simp * 6270866Simp * Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com). 7270866Simp * 8270866Simp * Chat -- a program for automatic session establishment (i.e. dial 9270866Simp * the phone and log in). 10270866Simp * 11270866Simp * This software is in the public domain. 12270866Simp * 13270866Simp * Please send all bug reports, requests for information, etc. to: 14270866Simp * 15270866Simp * Karl Fox <karl@MorningStar.Com> 16270866Simp * Morning Star Technologies, Inc. 17270866Simp * 1760 Zollinger Road 18270866Simp * Columbus, OH 43221 19270866Simp * (614)451-1883 20270866Simp * 21270866Simp * $Id: chat.c,v 1.3 1995/03/11 15:18:37 amurai Exp $ 22270866Simp * 23270866Simp * TODO: 24270866Simp * o Support more UUCP compatible control sequences. 25270866Simp * o Dialing shoud not block monitor process. 26270866Simp * o Reading modem by select should be unified into main.c 27270866Simp */ 28270866Simp#include "defs.h" 29270866Simp#include <ctype.h> 30270866Simp#include <sys/uio.h> 31270866Simp#ifndef isblank 32270866Simp#define isblank(c) ((c) == '\t' || (c) == ' ') 33270866Simp#endif 34270866Simp#include <sys/time.h> 35270866Simp#include <fcntl.h> 36270866Simp#include <errno.h> 37270866Simp#include <signal.h> 38270866Simp#include <sys/wait.h> 39270866Simp#include "timeout.h" 40270866Simp#include "vars.h" 41270866Simp 42270866Simp#define IBSIZE 200 43270866Simp 44270866Simpstatic int TimeoutSec; 45270866Simpstatic int abort_next, timeout_next; 46270866Simpstatic int numaborts; 47270866Simpchar *AbortStrings[50]; 48270866Simpchar inbuff[IBSIZE]; 49270866Simp 50270866Simpextern int ChangeParity(char *); 51270866Simp 52270866Simp#define MATCH 1 53270866Simp#define NOMATCH 0 54270866Simp#define ABORT -1 55270866Simp 56270866Simpstatic char * 57270866Simpfindblank(p, instring) 58270866Simpchar *p; 59270866Simpint instring; 60270866Simp{ 61270866Simp if (instring) { 62270866Simp while (*p) { 63270866Simp if (*p == '\\') { 64270866Simp strcpy(p, p + 1); 65270866Simp if (!*p) 66270866Simp break; 67270866Simp } else if (*p == '"') 68270866Simp return(p); 69270866Simp p++; 70270866Simp } 71270866Simp } else { 72270866Simp while (*p) { 73270866Simp if (isblank(*p)) 74270866Simp return(p); 75270866Simp p++; 76270866Simp } 77270866Simp } 78270866Simp return p; 79270866Simp} 80270866Simp 81270866Simpint 82270866SimpMakeArgs(script, pvect) 83270866Simpchar *script; 84270866Simpchar **pvect; 85270866Simp{ 86270866Simp int nargs, nb; 87270866Simp int instring; 88270866Simp 89270866Simp nargs = 0; 90270866Simp while (*script) { 91270866Simp nb = strspn(script, " \t"); 92270866Simp script += nb; 93270866Simp if (*script) { 94270866Simp if (*script == '"') { 95270866Simp instring = 1; 96270866Simp script++; 97270866Simp if (*script == '\0') 98270866Simp return(nargs); 99270866Simp } else 100270866Simp instring = 0; 101270866Simp *pvect++ = script; 102270866Simp nargs++; 103270866Simp script = findblank(script, instring); 104270866Simp if (*script) 105270866Simp *script++ = '\0'; 106270866Simp } 107270866Simp } 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