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