xref: /illumos-gate/usr/src/lib/libnsl/dial/sysfiles.c (revision 3d393ee6c37fa10ac512ed6d36109ad616dc7c1a)
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