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