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