1 /*- 2 * Copyright (c) 1985 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.proprietary.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)condevs.c 5.23 (Berkeley) 05/17/93"; 10 #endif /* not lint */ 11 12 extern int errno; 13 extern const char *const sys_errlist[]; 14 15 /* 16 * Here are various dialers to establish the machine-machine connection. 17 * conn.c/condevs.c was glued together by Mike Mitchell. 18 * The dialers were supplied by many people, to whom we are grateful. 19 * 20 * --------------------------------------------------------------------- 21 * NOTE: 22 * There is a bug that occurs at least on PDP11s due to a limitation of 23 * setjmp/longjmp. If the routine that does a setjmp is interrupted 24 * and longjmp-ed to, it loses its register variables (on a pdp11). 25 * What works is if the routine that does the setjmp 26 * calls a routine and it is the *subroutine* that is interrupted. 27 * 28 * Anyway, in conclusion, condevs.c is plagued with register variables 29 * that are used inside 30 * if (setjmp(...)) { 31 * .... 32 * } 33 * 34 * THE FIX: Don't declare variables to be register 35 */ 36 37 #include "condevs.h" 38 #include "pathnames.h" 39 40 struct condev condevs[] = { 41 { "DIR", "direct", diropn, nulldev, dircls }, 42 #ifdef DATAKIT 43 { "DK", "datakit", dkopn, nulldev, nulldev }, 44 #endif DATAKIT 45 #ifdef PNET 46 { "PNET", "pnet", pnetopn, nulldev, nulldev }, 47 #endif PNET 48 #ifdef UNETTCP 49 { "TCP", "TCP", unetopn, nulldev, unetcls }, 50 #endif UNETTCP 51 #ifdef BSDTCP 52 { "TCP", "TCP", bsdtcpopn, nulldev, bsdtcpcls }, 53 #endif BSDTCP 54 #ifdef MICOM 55 { "MICOM", "micom", micopn, nulldev, miccls }, 56 #endif MICOM 57 #ifdef DN11 58 { "ACU", "dn11", Acuopn, dnopn, dncls }, 59 #endif DN11 60 #ifdef HAYES 61 { "ACU", "hayes", Acuopn, hyspopn, hyscls }, 62 { "ACU", "hayespulse", Acuopn, hyspopn, hyscls }, 63 { "ACU", "hayestone", Acuopn, hystopn, hyscls }, 64 { "WATS", "hayestone", Acuopn, hystopn, hyscls }, 65 #endif HAYES 66 #ifdef HAYES2400 67 { "ACU", "hayes2400", Acuopn, hyspopn24, hyscls24 }, 68 { "ACU", "hayes2400pulse", Acuopn, hyspopn24, hyscls24 }, 69 { "ACU", "hayes2400tone", Acuopn, hystopn24, hyscls24 }, 70 #endif HAYES2400 71 #ifdef HAYESQ /* a version of hayes that doesn't use result codes */ 72 { "ACU", "hayesq", Acuopn, hysqpopn, hysqcls }, 73 { "ACU", "hayesqpulse", Acuopn, hysqpopn, hysqcls }, 74 { "ACU", "hayesqtone", Acuopn, hysqtopn, hysqcls }, 75 #endif HAYESQ 76 #ifdef CDS224 77 { "ACU", "cds224", Acuopn, cdsopn224, cdscls224}, 78 #endif CDS224 79 #ifdef NOVATION 80 { "ACU", "novation", Acuopn, novopn, novcls}, 81 #endif NOVATION 82 #ifdef DF02 83 { "ACU", "DF02", Acuopn, df2opn, df2cls }, 84 #endif DF02 85 #ifdef DF112 86 { "ACU", "DF112P", Acuopn, df12popn, df12cls }, 87 { "ACU", "DF112T", Acuopn, df12topn, df12cls }, 88 #endif DF112 89 #ifdef VENTEL 90 { "ACU", "ventel", Acuopn, ventopn, ventcls }, 91 #endif VENTEL 92 #ifdef PENRIL 93 { "ACU", "penril", Acuopn, penopn, pencls }, 94 #endif PENRIL 95 #ifdef VADIC 96 { "ACU", "vadic", Acuopn, vadopn, vadcls }, 97 #endif VADIC 98 #ifdef VA212 99 { "ACU", "va212", Acuopn, va212opn, va212cls }, 100 #endif VA212 101 #ifdef VA811S 102 { "ACU", "va811s", Acuopn, va811opn, va811cls }, 103 #endif VA811S 104 #ifdef VA820 105 { "ACU", "va820", Acuopn, va820opn, va820cls }, 106 { "WATS", "va820", Acuopn, va820opn, va820cls }, 107 #endif VA820 108 #ifdef RVMACS 109 { "ACU", "rvmacs", Acuopn, rvmacsopn, rvmacscls }, 110 #endif RVMACS 111 #ifdef VMACS 112 { "ACU", "vmacs", Acuopn, vmacsopn, vmacscls }, 113 #endif VMACS 114 #ifdef SYTEK 115 { "SYTEK", "sytek", sykopn, nulldev, sykcls }, 116 #endif SYTEK 117 #ifdef ATT2224 118 { "ACU", "att", Acuopn, attopn, attcls }, 119 #endif ATT2224 120 121 122 /* Insert new entries before this line */ 123 { NULL, NULL, NULL, NULL, NULL } 124 }; 125 126 /* 127 * nulldev a null device (returns CF_DIAL) 128 */ 129 nulldev() 130 { 131 return CF_DIAL; 132 } 133 134 /* 135 * nodev a null device (returns CF_NODEV) 136 */ 137 nodev() 138 { 139 return CF_NODEV; 140 } 141 142 /* 143 * Generic devices look through L-devices and call the CU_open routines for 144 * appropriate devices. Some things, like the tcp/ip interface, or direct 145 * connect, do not use the CU_open entry. ACUs must search to find the 146 * right routine to call. 147 */ 148 149 /* 150 * diropn(flds) connect to hardware line 151 * 152 * return codes: 153 * > 0 - file number - ok 154 * FAIL - failed 155 */ 156 diropn(flds) 157 register char *flds[]; 158 { 159 register int dcr, status; 160 struct Devices dev; 161 char dcname[20]; 162 FILE *dfp; 163 #ifdef VMSDTR /* Modem control on vms(works dtr) */ 164 int modem_control; 165 short iosb[4]; 166 int sys$qiow(); /* use this for long reads on vms */ 167 int ret; 168 long mode[2]; 169 modem_control = 0; 170 #endif 171 dfp = fopen(DEVFILE, "r"); 172 if (dfp == NULL) { 173 syslog(LOG_ERR, "fopen(%s) failed: %m", DEVFILE); 174 cleanup(FAIL); 175 } 176 while ((status = rddev(dfp, &dev)) != FAIL) { 177 #ifdef VMSDTR /* Modem control on vms(works dtr) */ 178 /* If we find MOD in the device type field we go into action */ 179 if (strcmp(dev.D_type, "MOD") == SAME) { 180 modem_control = 1; 181 DEBUG(7, "Setting Modem control to %d",modem_control); 182 } 183 if (strcmp(flds[F_CLASS], dev.D_class) != SAME) 184 continue; 185 /* 186 * Modem control on vms(works dtr) Take anything in MOD class. 187 * It probably should work differently anyway so we can have 188 * multiple hardwired lines. 189 */ 190 if (!modem_control&&strcmp(flds[F_PHONE], dev.D_line) != SAME) 191 #else !VMSDTR 192 if (strcmp(flds[F_CLASS], dev.D_class) != SAME) 193 continue; 194 if (strcmp(flds[F_PHONE], dev.D_line) != SAME) 195 #endif !VMSDTR 196 continue; 197 if (mlock(dev.D_line) != FAIL) 198 break; 199 } 200 fclose(dfp); 201 if (status == FAIL) { 202 logent("DEVICE", "NO"); 203 return CF_NODEV; 204 } 205 206 sprintf(dcname, "%s/%s", _PATH_DEV, dev.D_line); 207 if (setjmp(Sjbuf)) { 208 DEBUG(4, "Open timed out\n", CNULL); 209 delock(dev.D_line); 210 return CF_DIAL; 211 } 212 signal(SIGALRM, alarmtr); 213 /* For PC Pursuit, it could take a while to call back */ 214 alarm( strcmp(flds[F_LINE], "PCP") ? 10 : MAXMSGTIME*4 ); 215 getnextfd(); 216 errno = 0; 217 DEBUG(4,"Opening %s\n",dcname); 218 dcr = open(dcname, 2); /* read/write */ 219 #ifdef VMSDTR /* Modem control on vms(works dtr) */ 220 fflush(stdout); 221 if (modem_control) { /* Did we have MOD in the device type field ? */ 222 /* Sense the current terminal setup and save it */ 223 if ((ret = sys$qiow(_$EFN,(fd_fab_pointer[dcr]->fab).fab$l_stv, 224 IO$_SENSEMODE,iosb,0,0,mode,8,0,0,0,0)) 225 != SS$_NORMAL) { 226 DEBUG(7, "ret status on sense failed on Modem sense=%x<", ret); 227 return CF_DIAL; 228 } 229 mode[1] |= TT$M_MODEM; /* Or in modem control(DTR) */ 230 /* Now set the new terminal characteristics */ 231 /* This is temporary and will go away when we let go of it */ 232 if ((ret = sys$qiow(_$EFN,(fd_fab_pointer[dcr]->fab).fab$l_stv, 233 IO$_SETMODE,iosb,0,0,mode,8,0,0,0,0)) 234 != SS$_NORMAL) { 235 DEBUG(7, "ret status on sense failed on Modem setup=%x<", ret); 236 return CF_DIAL; 237 } 238 } 239 #endif VMSDTR 240 next_fd = -1; 241 alarm(0); 242 if (dcr < 0) { 243 if (errno == EACCES) 244 logent(dev.D_line, "CANT OPEN"); 245 DEBUG(4, "OPEN FAILED: errno %d\n", errno); 246 delock(dev.D_line); 247 return CF_DIAL; 248 } 249 fflush(stdout); 250 if (fixline(dcr, dev.D_speed) == FAIL) { 251 DEBUG(4, "FIXLINE FAILED\n", CNULL); 252 return CF_DIAL; 253 } 254 strcpy(devSel, dev.D_line); /* for latter unlock */ 255 CU_end = dircls; 256 return dcr; 257 } 258 259 dircls(fd) 260 register int fd; 261 { 262 if (fd > 0) { 263 close(fd); 264 delock(devSel); 265 } 266 } 267 268 /* 269 * open an ACU and dial the number. The condevs table 270 * will be searched until a dialing unit is found that is free. 271 * 272 * return codes: >0 - file number - o.k. 273 * FAIL - failed 274 */ 275 char devSel[20]; /* used for later unlock() */ 276 277 Acuopn(flds) 278 register char *flds[]; 279 { 280 char phone[MAXPH+1]; 281 register struct condev *cd; 282 register int fd, acustatus; 283 register FILE *dfp; 284 struct Devices dev; 285 int retval = CF_NODEV; 286 char nobrand[MAXPH], *line; 287 288 exphone(flds[F_PHONE], phone); 289 if (snccmp(flds[F_LINE], "LOCAL") == SAME) 290 line = "ACU"; 291 else 292 line = flds[F_LINE]; 293 devSel[0] = '\0'; 294 nobrand[0] = '\0'; 295 DEBUG(4, "Dialing %s\n", phone); 296 dfp = fopen(DEVFILE, "r"); 297 if (dfp == NULL) { 298 syslog(LOG_ERR, "fopen(%s) failed: %m", DEVFILE); 299 cleanup(FAIL); 300 } 301 302 acustatus = 0; /* none found, none locked */ 303 while (rddev(dfp, &dev) != FAIL) { 304 /* 305 * for each ACU L.sys line, try at most twice 306 * (TRYCALLS) to establish carrier. The old way tried every 307 * available dialer, which on big sites takes forever! 308 * Sites with a single auto-dialer get one try. 309 * Sites with multiple dialers get a try on each of two 310 * different dialers. 311 * To try 'harder' to connect to a remote site, 312 * use multiple L.sys entries. 313 */ 314 if (acustatus > TRYCALLS) 315 break; 316 if (strcmp(flds[F_CLASS], dev.D_class) != SAME) 317 continue; 318 if (snccmp(line, dev.D_type) != SAME) 319 continue; 320 if (dev.D_brand[0] == '\0') { 321 logent("Acuopn","No 'brand' name on ACU"); 322 continue; 323 } 324 for(cd = condevs; cd->CU_meth != NULL; cd++) { 325 if (snccmp(line, cd->CU_meth) == SAME) { 326 if (snccmp(dev.D_brand, cd->CU_brand) == SAME) { 327 nobrand[0] = '\0'; 328 break; 329 } 330 strncpy(nobrand, dev.D_brand, sizeof nobrand); 331 } 332 } 333 334 if (acustatus < 1) 335 acustatus = 1; /* has been found */ 336 337 if (mlock(dev.D_line) == FAIL) 338 continue; 339 340 #ifdef DIALINOUT 341 #ifdef ALLACUINOUT 342 if (1) { 343 #else !ALLACUINOUT 344 if (snccmp("inout", dev.D_calldev) == SAME) { 345 #endif !ALLACUINOUT 346 if (disable(dev.D_line) == FAIL) { 347 delock(dev.D_line); 348 continue; 349 } 350 } else 351 reenable(); 352 #endif DIALINOUT 353 354 DEBUG(4, "Using %s\n", cd->CU_brand); 355 acustatus++; 356 fd = (*(cd->CU_open))(phone, flds, &dev); 357 if (fd > 0) { 358 CU_end = cd->CU_clos; /* point CU_end at close func */ 359 fclose(dfp); 360 strcpy(devSel, dev.D_line); /* save for later unlock() */ 361 return fd; 362 } else 363 delock(dev.D_line); 364 retval = CF_DIAL; 365 } 366 fclose(dfp); 367 if (acustatus == 0) { 368 if (nobrand[0]) 369 logent(nobrand, "unsupported ACU type"); 370 else 371 logent("L-devices", "No appropriate ACU"); 372 } 373 if (acustatus == 1) 374 logent("DEVICE", "NO"); 375 return retval; 376 } 377 378 /* 379 * intervaldelay: delay execution for numerator/denominator seconds. 380 */ 381 382 #ifdef INTERVALTIMER 383 #include <sys/time.h> 384 #define uucpdelay(num,denom) intervaldelay(num,denom) 385 intervaldelay(num,denom) 386 int num, denom; 387 { 388 struct timeval tv; 389 tv.tv_sec = num / denom; 390 tv.tv_usec = (num * 1000000L / denom ) % 1000000L; 391 (void) select (0, (int *)0, (int *)0, (int *)0, &tv); 392 } 393 #endif INTERVALTIMER 394 395 #ifdef FASTTIMER 396 #define uucpdelay(num,denom) nap(60*num/denom) 397 /* Sleep in increments of 60ths of second. */ 398 nap (time) 399 register int time; 400 { 401 static int fd; 402 403 if (fd == 0) 404 fd = open (FASTTIMER, 0); 405 406 read (fd, 0, time); 407 } 408 #endif FASTTIMER 409 410 #ifdef FTIME 411 #define uucpdelay(num,denom) ftimedelay(1000*num/denom) 412 ftimedelay(n) 413 { 414 static struct timeb loctime; 415 register i = loctime.millitm; 416 417 ftime(&loctime); 418 while (abs((int)(loctime.millitm - i))<n) ftime(&loctime) 419 ; 420 } 421 #endif FTIME 422 423 #ifdef BUSYLOOP 424 #define uucpdelay(num,denom) busyloop(CPUSPEED*num/denom) 425 #define CPUSPEED 1000000 /* VAX 780 is 1MIPS */ 426 #define DELAY(n) { register long N = (n); while (--N > 0); } 427 busyloop(n) 428 { 429 DELAY(n); 430 } 431 #endif BUSYLOOP 432 433 slowrite(fd, str) 434 register char *str; 435 { 436 DEBUG(6, "slowrite ", CNULL); 437 while (*str) { 438 DEBUG(6, "%c", *str); 439 uucpdelay(1, 10); /* delay 1/10 second */ 440 write(fd, str, 1); 441 str++; 442 } 443 DEBUG(6, "\n", CNULL); 444 } 445 446 #define BSPEED B150 447 448 /* 449 * send a break 450 */ 451 genbrk(fn, bnulls) 452 register int fn, bnulls; 453 { 454 #ifdef USG 455 if (ioctl(fn, TCSBRK, STBNULL) < 0) 456 DEBUG(5, "break TCSBRK %s\n", sys_errlist[errno]); 457 #else !USG 458 # ifdef TIOCSBRK 459 if (ioctl(fn, TIOCSBRK, STBNULL) < 0) 460 DEBUG(5, "break TIOCSBRK %s\n", sys_errlist[errno]); 461 # ifdef TIOCCBRK 462 uucpdelay(bnulls, 10); 463 if (ioctl(fn, TIOCCBRK, STBNULL) < 0) 464 DEBUG(5, "break TIOCCBRK %s\n", sys_errlist[errno]); 465 # endif TIOCCBRK 466 DEBUG(4, "ioctl %f second break\n", (float) bnulls/10 ); 467 # else !TIOCSBRK 468 struct sgttyb ttbuf; 469 register int sospeed; 470 471 if (ioctl(fn, TIOCGETP, &ttbuf) < 0) 472 DEBUG(5, "break TIOCGETP %s\n", sys_errlist[errno]); 473 sospeed = ttbuf.sg_ospeed; 474 ttbuf.sg_ospeed = BSPEED; 475 if (ioctl(fn, TIOCSETP, &ttbuf) < 0) 476 DEBUG(5, "break TIOCSETP %s\n", sys_errlist[errno]); 477 if (write(fn, "\0\0\0\0\0\0\0\0\0\0\0\0", bnulls) != bnulls) { 478 badbreak: 479 logent(sys_errlist[errno], "BAD WRITE genbrk"); 480 alarm(0); 481 longjmp(Sjbuf, 3); 482 } 483 ttbuf.sg_ospeed = sospeed; 484 if (ioctl(fn, TIOCSETP, &ttbuf) < 0) 485 DEBUG(5, "break ioctl %s\n", sys_errlist[errno]); 486 if (write(fn, "@", 1) != 1) 487 goto badbreak; 488 DEBUG(4, "sent BREAK nulls - %d\n", bnulls); 489 #endif !TIOCSBRK 490 #endif !USG 491 } 492 493 494 #ifdef DIALINOUT 495 /* DIALIN/OUT CODE (WLS) */ 496 /* 497 * disable and reenable: allow a single line to be use for dialin/dialout 498 * 499 */ 500 501 char enbdev[16]; 502 503 disable(dev) 504 register char *dev; 505 { 506 register char *rdev; 507 508 /* strip off directory prefixes */ 509 rdev = dev; 510 while (*rdev) 511 rdev++; 512 while (--rdev >= dev && *rdev != '/') 513 ; 514 rdev++; 515 516 if (enbdev[0]) { 517 if (strcmp(enbdev, rdev) == SAME) 518 return SUCCESS; /* already disabled */ 519 delock(enbdev); 520 reenable(); /* else, reenable the old one */ 521 } 522 DEBUG(4, "Disable %s\n", rdev); 523 if (enbcall("disable", rdev) == FAIL) 524 return FAIL; 525 strcpy(enbdev, rdev); 526 return SUCCESS; 527 } 528 529 reenable() 530 { 531 if (enbdev[0] == '\0') 532 return; 533 DEBUG(4, "Reenable %s\n", enbdev); 534 (void) enbcall("enable", enbdev); 535 enbdev[0] = '\0'; 536 } 537 538 enbcall(type, dev) 539 char *type, *dev; 540 { 541 int pid; 542 register char *p; 543 int fildes[2]; 544 int status; 545 FILE *fil; 546 char buf[80]; 547 548 fflush(stderr); 549 fflush(stdout); 550 pipe(fildes); 551 if ((pid = fork()) == 0) { 552 DEBUG(4, DIALINOUT, CNULL); 553 DEBUG(4, " %s", type); 554 DEBUG(4, " %s\n", dev); 555 close(fildes[0]); 556 close(0); close(1); close(2); 557 open(_PATH_DEVNULL,0); 558 dup(fildes[1]); dup(fildes[1]); 559 setuid(geteuid()); /* for chown(uid()) in acu program */ 560 execl(DIALINOUT, "acu", type, dev, Rmtname, (char *)0); 561 exit(-1); 562 } 563 if (pid<0) 564 return FAIL; 565 566 close(fildes[1]); 567 fil = fdopen(fildes[0],"r"); 568 if (fil!=NULL) { 569 #ifdef BSD4_2 570 setlinebuf(fil); 571 #endif BSD4_2 572 while (fgets(buf, sizeof buf, fil) != NULL) { 573 p = buf + strlen(buf) - 1; 574 if (*p == '\n') 575 *p = '\0'; 576 DEBUG(4, "ACUCNTRL: %s\n", buf); 577 } 578 } 579 while(wait(&status) != pid) 580 ; 581 fclose(fil); 582 return status ? FAIL : SUCCESS; 583 } 584 #endif DIALINOUT 585