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