1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 /* 26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include "mt.h" 33 #include "uucp.h" 34 35 #include <unistd.h> 36 #include <string.h> 37 #include "sysfiles.h" 38 #include <sys/stropts.h> 39 40 /* 41 * manage systems files (Systems, Devices, and Dialcodes families). 42 * 43 * also manage new file Devconfig, allows per-device setup. 44 * present use is to specify what streams modules to push/pop for 45 * AT&T TLI/streams network. 46 * 47 * TODO: 48 * call bsfix()? 49 * combine the 3 versions of everything (sys, dev, and dial) into one. 50 * allow arbitrary classes of service. 51 * need verifysys() for uucheck. 52 * nameserver interface? 53 * pass sysname (or 0) to getsysline(). (might want reg. exp. or 54 * NS processing) 55 */ 56 57 /* private variables */ 58 static void tokenize(void); 59 static void nameparse(void); 60 static void setfile(char **, char *); 61 static void setioctl(char **, char *); 62 static void scansys(const char *); 63 static void scancfg(char *, char *); 64 static void setconfig(void); 65 static int namematch(const char *label, char *line, const char *name); 66 static int nextdialers(void); 67 static int nextdevices(void); 68 static int nextsystems(void); 69 static int getline(FILE *, char *); 70 71 /* pointer arrays might be dynamically allocated */ 72 static char *Systems[64]; /* list of Systems files */ 73 static char *Devices[64]; /* list of Devices files */ 74 static char *Dialers[64]; /* list of Dialers files */ 75 static char *Pops[64]; /* list of STREAMS modules to be popped */ 76 static char *Pushes[64]; /* list of STREAMS modules to be pushed */ 77 78 static int nsystems; /* index into list of Systems files */ 79 static int ndevices; /* index into list of Devices files */ 80 static int ndialers; /* index into list of Dialers files */ 81 static int npops; /* index into list of STREAMS modules */ 82 /* to be popped */ 83 static int npushes; /* index into list of STREAMS modules */ 84 /* to be pushed */ 85 86 static unsigned connecttime, expecttime; 87 88 static FILE *fsystems; 89 static FILE *fdevices; 90 static FILE *fdialers; 91 92 /* this might be dynamically allocated */ 93 #define NTOKENS 16 94 static char *tokens[NTOKENS], **tokptr; 95 96 /* export these */ 97 static void setservice(const char *service); 98 static void sysreset(void); 99 static void devreset(void); 100 static void dialreset(void); 101 static void setdevcfg(char *, char *); 102 static void setservice(const char *); 103 104 /* import these */ 105 extern char *strsave(const char *); 106 static int eaccess(char *, mode_t); 107 108 /* 109 * setservice init's Systems, Devices, Dialers lists from Sysfiles 110 */ 111 static void 112 setservice(const char *service) 113 { 114 setconfig(); 115 scansys(service); 116 } 117 118 /* 119 * setdevcfg init's Pops, Pushes lists from Devconfig 120 */ 121 122 static void 123 setdevcfg(char *service, char *device) 124 { 125 scancfg(service, device); 126 } 127 128 /* administrative files access */ 129 static int 130 sysaccess(int type) 131 { 132 char errformat[BUFSIZ]; 133 134 switch (type) { 135 case ACCESS_SYSTEMS: 136 return (access(Systems[nsystems], R_OK)); 137 case ACCESS_DEVICES: 138 return (access(Devices[ndevices], R_OK)); 139 case ACCESS_DIALERS: 140 return (access(Dialers[ndialers], R_OK)); 141 case EACCESS_SYSTEMS: 142 return (eaccess(Systems[nsystems], R_OK)); 143 case EACCESS_DEVICES: 144 return (eaccess(Devices[ndevices], R_OK)); 145 case EACCESS_DIALERS: 146 return (eaccess(Dialers[ndialers], R_OK)); 147 } 148 (void) sprintf(errformat, "bad access type %d", type); 149 logent(errformat, "sysaccess"); 150 return (FAIL); 151 } 152 153 154 /* 155 * read Sysfiles, set up lists of Systems/Devices/Dialers file names. 156 * allow multiple entries for a given service, allow a service 157 * type to describe resources more than once, e.g., systems=foo:baz systems=bar. 158 */ 159 static void 160 scansys(const char *service) 161 { FILE *f; 162 char *tok, buf[BUFSIZ]; 163 164 Systems[0] = Devices[0] = Dialers[0] = NULL; 165 if ((f = fopen(SYSFILES, "rF")) != 0) { 166 while (getline(f, buf) > 0) { 167 /* got a (logical) line from Sysfiles */ 168 /* strtok's of this buf continue in tokenize() */ 169 tok = strtok(buf, " \t"); 170 if (namematch("service=", tok, service)) { 171 tokenize(); 172 nameparse(); 173 } 174 } 175 (void) fclose(f); 176 } 177 178 /* if didn't find entries in Sysfiles, use defaults */ 179 if (Systems[0] == NULL) { 180 Systems[0] = strsave(SYSTEMS); 181 ASSERT(Systems[0] != NULL, "Ct_ALLOCATE", "scansys: Systems", 182 0); 183 Systems[1] = NULL; 184 } 185 if (Devices[0] == NULL) { 186 Devices[0] = strsave(DEVICES); 187 ASSERT(Devices[0] != NULL, "Ct_ALLOCATE", "scansys: Devices", 188 0); 189 Devices[1] = NULL; 190 } 191 if (Dialers[0] == NULL) { 192 Dialers[0] = strsave(DIALERS); 193 ASSERT(Dialers[0] != NULL, "Ct_ALLOCATE", "scansys: Dialers", 194 0); 195 Dialers[1] = NULL; 196 } 197 } 198 199 200 /* 201 * read Devconfig. allow multiple entries for a given service, allow a service 202 * type to describe resources more than once, e.g., push=foo:baz push=bar. 203 */ 204 static void 205 scancfg(char *service, char *device) 206 { FILE *f; 207 char *tok, buf[BUFSIZ]; 208 209 /* (re)initialize device-specific information */ 210 npops = npushes = 0; 211 Pops[0] = Pushes[0] = NULL; 212 connecttime = CONNECTTIME; 213 expecttime = EXPECTTIME; 214 215 if ((f = fopen(DEVCONFIG, "rF")) != 0) { 216 while (getline(f, buf) > 0) { 217 /* got a (logical) line from Devconfig */ 218 /* strtok's of this buf continue in tokenize() */ 219 tok = strtok(buf, " \t"); 220 if (namematch("service=", tok, service)) { 221 tok = strtok((char *)0, " \t"); 222 if (namematch("device=", tok, device)) { 223 tokenize(); 224 nameparse(); 225 } 226 } 227 } 228 (void) fclose(f); 229 } 230 return; 231 232 } 233 234 /* 235 * given a file pointer and buffer, construct logical line in buffer 236 * (i.e., concatenate lines ending in '\'). return length of line 237 * ASSUMES that buffer is BUFSIZ long! 238 */ 239 240 static int 241 getline(FILE *f, char *line) 242 { char *lptr, *lend; 243 244 lptr = line; 245 while (fgets(lptr, (line + BUFSIZ) - lptr, f) != NULL) { 246 lend = lptr + strlen(lptr); 247 if (lend == lptr || lend[-1] != '\n') 248 /* empty buf or line too long! */ 249 break; 250 *--lend = '\0'; /* lop off ending '\n' */ 251 if (lend == line) /* empty line - ignore */ 252 continue; 253 lptr = lend; 254 if (lend[-1] != '\\') 255 break; 256 /* continuation */ 257 lend[-1] = ' '; 258 } 259 return (lptr - line); 260 } 261 262 /* 263 * given a label (e.g., "service=", "device="), a name ("cu", "uucico"), 264 * and a line: if line begins with the label and if the name appears 265 * in a colon-separated list of names following the label, return true; 266 * else return false 267 */ 268 static int 269 namematch(const char *label, char *line, const char *name) 270 { 271 char *lend; 272 273 if (strncmp(label, line, strlen(label)) != SAME) 274 return (FALSE); /* probably a comment line */ 275 line += strlen(label); 276 if (*line == '\0') 277 return (FALSE); 278 /* 279 * can't use strtok() in the following because scansys(), 280 * scancfg() do an initializing call to strtok() before 281 * coming here and then CONTINUE calling strtok() in tokenize(), 282 * after returning from namematch(). 283 */ 284 while ((lend = strchr(line, ':')) != NULL) { 285 *lend = '\0'; 286 if (strcmp(line, name) == SAME) 287 return (TRUE); 288 line = lend+1; 289 } 290 return (strcmp(line, name) == SAME); 291 } 292 293 /* 294 * tokenize() continues pulling tokens out of a buffer -- the 295 * initializing call to strtok must have been made before calling 296 * tokenize() -- and starts stuffing 'em into tokptr. 297 */ 298 static void 299 tokenize(void) 300 { 301 char *tok; 302 303 tokptr = tokens; 304 while ((tok = strtok(NULL, " \t")) != NULL) { 305 *tokptr++ = tok; 306 if (tokptr - tokens >= NTOKENS) 307 break; 308 } 309 *tokptr = NULL; 310 } 311 312 /* 313 * look at top token in array: should be line of the form 314 * name=item1:item2:item3... 315 * if name is one we recognize, then call set[file|ioctl] to set up 316 * corresponding list. otherwise, log bad name. 317 */ 318 static void 319 nameparse(void) 320 { 321 char **line, *equals; 322 int temp; 323 324 #define setuint(a, b, c) a = (((temp = atoi(b)) <= 0) ? (c) : temp) 325 326 for (line = tokens; (line - tokens) < NTOKENS && *line; line++) { 327 equals = strchr(*line, '='); 328 if (equals == NULL) 329 continue; /* may be meaningful someday? */ 330 *equals = '\0'; 331 /* ignore entry with empty rhs */ 332 if (*++equals == '\0') 333 continue; 334 if (strcmp(*line, "systems") == SAME) 335 setfile(Systems, equals); 336 else if (strcmp(*line, "devices") == SAME) 337 setfile(Devices, equals); 338 else if (strcmp(*line, "dialers") == SAME) 339 setfile(Dialers, equals); 340 else if (strcmp(*line, "pop") == SAME) 341 setioctl(Pops, equals); 342 else if (strcmp(*line, "push") == SAME) 343 setioctl(Pushes, equals); 344 else if (strcmp(*line, "connecttime") == SAME) 345 setuint(connecttime, equals, CONNECTTIME); 346 else if (strcmp(*line, "expecttime") == SAME) 347 setuint(expecttime, equals, EXPECTTIME); 348 else if (strcmp(*line, "msgtime") == SAME) 349 continue; 350 else { 351 char errformat[BUFSIZ]; 352 353 (void) snprintf(errformat, sizeof (errformat), 354 "unrecognized label %s", *line); 355 logent(errformat, "Sysfiles|Devconfig"); 356 } 357 } 358 } 359 360 /* 361 * given the list for a particular type (systems, devices,...) 362 * and a line of colon-separated files, add 'em to list 363 */ 364 365 static void 366 setfile(char **type, char *line) 367 { 368 char **tptr, *tok; 369 char expandpath[BUFSIZ]; 370 371 if (*line == 0) 372 return; 373 tptr = type; 374 while (*tptr) /* skip over existing entries to */ 375 tptr++; /* concatenate multiple entries */ 376 377 for (tok = strtok(line, ":"); tok != NULL; tok = strtok(NULL, ":")) { 378 expandpath[0] = '\0'; 379 if (*tok != '/') 380 /* by default, file names are relative to SYSDIR */ 381 (void) snprintf(expandpath, sizeof (expandpath), 382 "%s/", SYSDIR); 383 (void) strcat(expandpath, tok); 384 if (eaccess(expandpath, R_OK) != 0) 385 /* if we can't read it, no point in adding to list */ 386 continue; 387 *tptr = strsave(expandpath); 388 ASSERT(*tptr != NULL, "Ct_ALLOCATE", "setfile: tptr", 0); 389 tptr++; 390 } 391 } 392 393 /* 394 * given the list for a particular ioctl (push, pop) 395 * and a line of colon-separated modules, add 'em to list 396 */ 397 398 static void 399 setioctl(char **type, char *line) 400 { 401 char **tptr, *tok; 402 403 if (*line == 0) 404 return; 405 tptr = type; 406 while (*tptr) /* skip over existing entries to */ 407 tptr++; /* concatenate multiple entries */ 408 for (tok = strtok(line, ":"); tok != NULL; tok = strtok(NULL, ":")) { 409 *tptr = strsave(tok); 410 ASSERT(*tptr != NULL, "Ct_ALLOCATE", "setioctl: tptr", 0); 411 tptr++; 412 } 413 } 414 415 /* 416 * reset Systems files 417 */ 418 static void 419 sysreset(void) 420 { 421 if (fsystems) 422 (void) fclose(fsystems); 423 fsystems = NULL; 424 nsystems = 0; 425 devreset(); 426 } 427 428 /* 429 * reset Devices files 430 */ 431 static void 432 devreset(void) 433 { 434 if (fdevices) 435 (void) fclose(fdevices); 436 fdevices = NULL; 437 ndevices = 0; 438 dialreset(); 439 } 440 441 /* 442 * reset Dialers files 443 */ 444 static void 445 dialreset(void) 446 { 447 if (fdialers) 448 (void) fclose(fdialers); 449 fdialers = NULL; 450 ndialers = 0; 451 } 452 453 /* 454 * get next line from Systems file 455 * return TRUE if successful, FALSE if not 456 */ 457 static int 458 getsysline(char *buf, int len) 459 { 460 if (Systems[0] == NULL) 461 /* not initialized via setservice() - use default */ 462 setservice("uucico"); 463 464 /* initialize devices and dialers whenever a new line is read */ 465 /* from systems */ 466 devreset(); 467 if (fsystems == NULL) 468 if (nextsystems() == FALSE) 469 return (FALSE); 470 471 for (;;) { 472 while (fgets(buf, len, fsystems) != NULL) 473 if ((*buf != '#') && (*buf != ' ') && 474 (*buf != '\t') && (*buf != '\n')) 475 return (TRUE); 476 if (nextsystems() == FALSE) 477 return (FALSE); 478 } 479 } 480 481 /* 482 * move to next systems file. return TRUE if successful, FALSE if not 483 */ 484 static int 485 nextsystems(void) 486 { 487 devreset(); 488 489 if (fsystems != NULL) { 490 (void) fclose(fsystems); 491 nsystems++; 492 } else { 493 nsystems = 0; 494 } 495 for (; Systems[nsystems] != NULL; nsystems++) 496 if ((fsystems = fopen(Systems[nsystems], "rF")) != NULL) 497 return (TRUE); 498 return (FALSE); 499 } 500 501 /* 502 * get next line from Devices file 503 * return TRUE if successful, FALSE if not 504 */ 505 static int 506 getdevline(char *buf, int len) 507 { 508 if (Devices[0] == NULL) 509 /* not initialized via setservice() - use default */ 510 setservice("uucico"); 511 512 if (fdevices == NULL) 513 if (nextdevices() == FALSE) 514 return (FALSE); 515 for (;;) { 516 if (fgets(buf, len, fdevices) != NULL) 517 return (TRUE); 518 if (nextdevices() == FALSE) 519 return (FALSE); 520 } 521 } 522 523 /* 524 * move to next devices file. return TRUE if successful, FALSE if not 525 */ 526 static int 527 nextdevices(void) 528 { 529 if (fdevices != NULL) { 530 (void) fclose(fdevices); 531 ndevices++; 532 } else { 533 ndevices = 0; 534 } 535 for (; Devices[ndevices] != NULL; ndevices++) 536 if ((fdevices = fopen(Devices[ndevices], "rF")) != NULL) 537 return (TRUE); 538 return (FALSE); 539 } 540 541 542 /* 543 * get next line from Dialers file 544 * return TRUE if successful, FALSE if not 545 */ 546 547 static int 548 getdialline(char *buf, int len) 549 { 550 if (Dialers[0] == NULL) 551 /* not initialized via setservice() - use default */ 552 setservice("uucico"); 553 554 if (fdialers == NULL) 555 if (nextdialers() == FALSE) 556 return (FALSE); 557 for (;;) { 558 if (fgets(buf, len, fdialers) != NULL) 559 return (TRUE); 560 if (nextdialers() == FALSE) 561 return (FALSE); 562 } 563 } 564 565 /* 566 * move to next dialers file. return TRUE if successful, FALSE if not 567 */ 568 static int 569 nextdialers(void) 570 { 571 if (fdialers) { 572 (void) fclose(fdialers); 573 ndialers++; 574 } else { 575 ndialers = 0; 576 } 577 578 for (; Dialers[ndialers] != NULL; ndialers++) 579 if ((fdialers = fopen(Dialers[ndialers], "rF")) != NULL) 580 return (TRUE); 581 return (FALSE); 582 } 583 584 /* 585 * get next module to be popped 586 * return TRUE if successful, FALSE if not 587 */ 588 static int 589 getpop(char *buf, size_t len, int *optional) 590 { 591 int slen; 592 593 if (Pops[0] == NULL || Pops[npops] == NULL) 594 return (FALSE); 595 596 /* if the module name is enclosed in parentheses, */ 597 /* is optional. set flag & strip parens */ 598 slen = strlen(Pops[npops]) - 1; 599 if (Pops[npops][0] == '(' && Pops[npops][slen] == ')') { 600 *optional = 1; 601 len = (slen < len ? slen : len); 602 (void) strncpy(buf, &(Pops[npops++][1]), len); 603 } else { 604 *optional = 0; 605 (void) strncpy(buf, Pops[npops++], len); 606 } 607 buf[len-1] = '\0'; 608 return (TRUE); 609 } 610 611 /* 612 * get next module to be pushed 613 * return TRUE if successful, FALSE if not 614 */ 615 static int 616 getpush(char *buf, size_t len) 617 { 618 if (Pushes[0] == NULL || Pushes[npushes] == NULL) 619 return (FALSE); 620 (void) strncpy(buf, Pushes[npushes++], len); 621 return (TRUE); 622 } 623 624 /* 625 * pop/push requested modules 626 * return TRUE if successful, FALSE if not 627 */ 628 static int 629 pop_push(int fd) 630 { 631 char strmod[FMNAMESZ], onstream[FMNAMESZ]; 632 int optional; 633 634 /* check for streams modules to pop */ 635 while (getpop(strmod, sizeof (strmod), &optional)) { 636 DEBUG(5, (optional ? 637 (const char *)"pop_push: optionally POPing %s\n" : 638 (const char *)"pop_push: POPing %s\n"), strmod); 639 if (ioctl(fd, I_LOOK, onstream) == -1) { 640 DEBUG(5, "pop_push: I_LOOK on fd %d failed ", fd); 641 DEBUG(5, "errno %d\n", errno); 642 return (FALSE); 643 } 644 if (strcmp(strmod, onstream) != SAME) { 645 if (optional) 646 continue; 647 DEBUG(5, "pop_push: I_POP: %s not there\n", strmod); 648 return (FALSE); 649 } 650 if (ioctl(fd, I_POP, 0) == -1) { 651 DEBUG(5, "pop_push: I_POP on fd %d failed ", fd); 652 DEBUG(5, "errno %d\n", errno); 653 return (FALSE); 654 } 655 } 656 657 /* check for streams modules to push */ 658 while (getpush(strmod, sizeof (strmod))) { 659 DEBUG(5, "pop_push: PUSHing %s\n", strmod); 660 if (ioctl(fd, I_PUSH, strmod) == -1) { 661 DEBUG(5, "pop_push: I_PUSH on fd %d failed ", fd); 662 DEBUG(5, "errno %d\n", errno); 663 return (FALSE); 664 } 665 } 666 return (TRUE); 667 } 668 669 #ifndef SMALL 670 /* 671 * return name of currently open Systems file 672 */ 673 static char * 674 currsys(void) 675 { 676 return (Systems[nsystems]); 677 } 678 679 /* 680 * return name of currently open Devices file 681 */ 682 static char * 683 currdev(void) 684 { 685 return (Devices[ndevices]); 686 } 687 688 /* 689 * return name of currently open Dialers file 690 */ 691 static char * 692 currdial(void) 693 { 694 return (Dialers[ndialers]); 695 } 696 #endif 697 698 /* 699 * set configuration parameters provided in Config file 700 */ 701 static void 702 setconfig(void) 703 { 704 FILE *f; 705 char buf[BUFSIZ]; 706 char *tok; 707 extern char _ProtoCfg[]; 708 709 if ((f = fopen(CONFIG, "rF")) != 0) { 710 while (getline(f, buf) > 0) { 711 /* got a (logical) line from Config file */ 712 tok = strtok(buf, " \t"); 713 if ((tok != NULL) && (*tok != '#')) { 714 /* got a token */ 715 /* 716 * this probably should be table driven when 717 * the list of configurable parameters grows. 718 */ 719 if (strncmp("Protocol=", tok, strlen("Protocol=")) == 720 SAME) { 721 tok += strlen("Protocol="); 722 if (*tok != '\0') { 723 if (_ProtoCfg[0] != '\0') { 724 /*EMPTY*/ 725 DEBUG(7, "Protocol string %s ", 726 tok); 727 DEBUG(7, "overrides %s\n", 728 _ProtoCfg); 729 } 730 (void) strcpy(_ProtoCfg, tok); 731 } 732 } else { 733 /*EMPTY*/ 734 DEBUG(7, "Unknown configuration parameter %s\n", 735 tok); 736 } 737 } 738 } 739 (void) fclose(f); 740 } 741 } 742