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