1/*	$NetBSD: conf.c,v 1.14 2019/05/23 04:34:25 kre Exp $	*/
2
3/*
4 * Copyright (c) 1992, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * This code is derived from software donated to Berkeley by
8 * Jan-Simon Pendry.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 *	from: Id: conf.c,v 1.2 1992/05/27 07:09:27 jsp Exp
35 *	@(#)conf.c	8.2 (Berkeley) 3/27/94
36 */
37
38#include <sys/cdefs.h>
39#ifndef lint
40__RCSID("$NetBSD: conf.c,v 1.14 2019/05/23 04:34:25 kre Exp $");
41#endif /* not lint */
42
43#include <sys/types.h>
44#include <sys/param.h>
45#include <sys/syslog.h>
46#include <stdio.h>
47#include <stdlib.h>
48#include <unistd.h>
49#include <string.h>
50#include <errno.h>
51#include <limits.h>
52#include <regex.h>
53
54#include "portald.h"
55
56#define	ALLOC(ty)	(xmalloc(sizeof(ty)))
57
58typedef struct path path;
59struct path {
60	qelem p_q;		/* 2-way linked list */
61	int p_lno;		/* Line number of this record */
62	char *p_args;		/* copy of arg string (malloc) */
63	char *p_key;		/* Pathname to match (also p_argv[0]) */
64	regex_t p_re;		/* RE to match against pathname (malloc) */
65	int p_use_re;		/* true if entry is RE */
66	int p_argc;		/* number of elements in arg string */
67	char **p_argv;		/* argv[] pointers into arg string (malloc) */
68};
69
70static	void	ins_que(qelem *, qelem *);
71static	path   *palloc(char *, int, const char *);
72static	void	pfree(path *);
73static	int	pinsert(path *, qelem *);
74static	void	preplace(qelem *, qelem *);
75static	void	readfp(qelem *, FILE *, const char *);
76static	void	rem_que(qelem *);
77static	void   *xmalloc(size_t);
78
79/*
80 * Add an element to a 2-way list,
81 * just after (pred)
82 */
83static void
84ins_que(qelem *elem, qelem *pred)
85{
86	qelem *p = pred->q_forw;
87	elem->q_back = pred;
88	elem->q_forw = p;
89	pred->q_forw = elem;
90	p->q_back = elem;
91}
92
93/*
94 * Remove an element from a 2-way list
95 */
96static void
97rem_que(qelem *elem)
98{
99	qelem *p = elem->q_forw;
100	qelem *p2 = elem->q_back;
101	p2->q_forw = p;
102	p->q_back = p2;
103}
104
105/*
106 * Error checking malloc
107 */
108static void *
109xmalloc(size_t siz)
110{
111	void *p = malloc(siz);
112
113	if (p)
114		return p;
115
116	syslog(LOG_ERR, "malloc: failed to get %lu bytes", (u_long)siz);
117	exit(1);
118}
119
120/*
121 * Insert the path in the list.
122 * If there is already an element with the same key then
123 * the *second* one is ignored (return 0).  If the key is
124 * not found then the path is added to the end of the list
125 * and 1 is returned.
126 */
127static int
128pinsert(path *p0, qelem *q0)
129{
130	qelem *q;
131
132	if (p0->p_argc == 0)
133		return 0;
134
135	for (q = q0->q_forw; q != q0; q = q->q_forw) {
136		path *p = (path *)q;
137
138		if (strcmp(p->p_key, p0->p_key) == 0)
139			return 0;
140	}
141	ins_que(&p0->p_q, q0->q_back);
142	return 1;
143
144}
145
146static path *
147palloc(char *cline, int lno, const char *conf_file)
148{
149	int c, errcode;
150	char *s;
151	char *key;
152	path *p;
153	char **ap;
154
155	/*
156	 * Do a pass through the string to count the number
157	 * of arguments.   Stop if we encounter a comment.
158	 */
159	c = 0;
160	key = strdup(cline);
161	for (s = key; s != NULL; ) {
162		char *val;
163
164		if (*s == '#') {	/* '#" at beginning of word */
165			cline[s-key] = '\0';	/* delete comment -> EOL */
166			break;
167		}
168
169		while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0')
170			;
171		if (val)
172			c++;
173	}
174	c++;
175	free(key);
176
177	if (c <= 1)
178		return 0;
179
180	/*
181	 * Now do another pass and generate a new path structure
182	 */
183	p = ALLOC(path);
184	p->p_argc = 0;
185	p->p_argv = xmalloc(c * sizeof(char *));
186	p->p_args = strdup(cline);
187	ap = p->p_argv;
188	for (s = p->p_args; s != NULL; ) {
189		char *val;
190
191		while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0')
192			;
193		if (val) {
194			*ap++ = val;
195			p->p_argc++;
196		}
197	}
198	*ap = 0;
199
200#ifdef DEBUG
201	for (c = 0; c < p->p_argc; c++)
202		printf("%sv[%d] = %s\n", c?"\t":"", c, p->p_argv[c]);
203#endif
204
205	p->p_key = p->p_argv[0];
206	p->p_use_re = 0;
207	if (strpbrk(p->p_key, RE_CHARS)) {
208		errcode = regcomp(&p->p_re, p->p_key, REG_EXTENDED|REG_NOSUB);
209		if (errcode == 0)
210			p->p_use_re = 1;
211		else {
212			char buf[200];
213			regerror(errcode, &p->p_re, buf, sizeof(buf));
214
215			syslog(LOG_WARNING, "%s, line %d: regcomp \"%s\": %s",
216	  		  conf_file, p->p_lno, p->p_key, buf);
217		}
218	}
219	p->p_lno = lno;
220
221	return p;
222}
223
224/*
225 * Free a path structure
226 */
227static void
228pfree(path *p)
229{
230	free(p->p_args);
231	free((char *)p->p_argv);
232	if (p->p_use_re)
233		regfree(&p->p_re);
234	free((char *)p);
235}
236
237/*
238 * Discard all currently held path structures on q0.
239 * and add all the ones on xq.
240 */
241static void
242preplace(qelem *q0, qelem *xq)
243{
244	/*
245	 * While the list is not empty,
246	 * take the first element off the list
247	 * and free it.
248	 */
249	while (q0->q_forw != q0) {
250		qelem *q = q0->q_forw;
251		rem_que(q);
252		pfree((path *)q);
253	}
254	while (xq->q_forw != xq) {
255		qelem *q = xq->q_forw;
256		rem_que(q);
257		ins_que(q, q0);
258	}
259}
260
261/*
262 * Read the lines from the configuration file and
263 * add them to the list of paths.
264 */
265static void
266readfp(qelem *q0, FILE *fp, const char *conf_file)
267{
268	char cline[LINE_MAX];
269	int nread = 0;
270	qelem q;
271
272	/*
273	 * Make a new empty list.
274	 */
275	q.q_forw = q.q_back = &q;
276
277	/*
278	 * Read the lines from the configuration file.
279	 */
280	while (fgets(cline, sizeof(cline), fp)) {
281		path *p = palloc(cline, nread+1, conf_file);
282
283		if (p && !pinsert(p, &q))
284			pfree(p);
285		nread++;
286	}
287
288	/*
289	 * If some records were read, then throw
290	 * away the old list and replace with the
291	 * new one.
292	 */
293	if (nread)
294		preplace(q0, &q);
295}
296
297/*
298 * Read the configuration file (conf) and replace
299 * the existing path list with the new version.
300 * If the file is not readable, then no changes take place
301 */
302int
303conf_read(qelem *q, const char *conf)
304{
305	FILE *fp = fopen(conf, "r");
306
307	if (fp) {
308		readfp(q, fp, conf);
309		(void)fclose(fp);
310		return 0;
311	} else {
312		int sverrno = errno;
313
314		syslog(LOG_WARNING, "open config file \"%s\": %m", conf);
315		errno = sverrno;
316		return -1;
317	}
318}
319
320
321char **
322conf_match(qelem *q0, char *key)
323{
324	qelem *q;
325
326	for (q = q0->q_forw; q != q0; q = q->q_forw) {
327		path *p = (path *)q;
328
329		if (p->p_use_re) {
330			if (regexec(&p->p_re, key, 0, NULL, 0) == 0)
331				return p->p_argv + 1;
332		} else {
333			if (strncmp(p->p_key, key, strlen(p->p_key)) == 0)
334				return p->p_argv + 1;
335		}
336	}
337
338	return 0;
339}
340