Home | History | Annotate | Line # | Download | only in inetd
      1 /*	$NetBSD: parse.c,v 1.5 2022/08/10 08:37:53 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1998, 2003 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
      9  * NASA Ames Research Center and by Matthias Scheler.
     10  *
     11  * Redistribution and use in source and binary forms, with or without
     12  * modification, are permitted provided that the following conditions
     13  * are met:
     14  * 1. Redistributions of source code must retain the above copyright
     15  *    notice, this list of conditions and the following disclaimer.
     16  * 2. Redistributions in binary form must reproduce the above copyright
     17  *    notice, this list of conditions and the following disclaimer in the
     18  *    documentation and/or other materials provided with the distribution.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30  * POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  * Copyright (c) 1983, 1991, 1993, 1994
     35  *	The Regents of the University of California.  All rights reserved.
     36  *
     37  * Redistribution and use in source and binary forms, with or without
     38  * modification, are permitted provided that the following conditions
     39  * are met:
     40  * 1. Redistributions of source code must retain the above copyright
     41  *    notice, this list of conditions and the following disclaimer.
     42  * 2. Redistributions in binary form must reproduce the above copyright
     43  *    notice, this list of conditions and the following disclaimer in the
     44  *    documentation and/or other materials provided with the distribution.
     45  * 3. Neither the name of the University nor the names of its contributors
     46  *    may be used to endorse or promote products derived from this software
     47  *    without specific prior written permission.
     48  *
     49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     59  * SUCH DAMAGE.
     60  */
     61 
     62 #include <sys/cdefs.h>
     63 #ifndef lint
     64 #if 0
     65 static char sccsid[] = "@(#)inetd.c	8.4 (Berkeley) 4/13/94";
     66 #else
     67 __RCSID("$NetBSD: parse.c,v 1.5 2022/08/10 08:37:53 christos Exp $");
     68 #endif
     69 #endif /* not lint */
     70 
     71 /*
     72  * This file contains code and state for loading and managing servtabs.
     73  * The "positional" syntax parsing is performed in this file. See parse_v2.c
     74  * for "key-values" syntax parsing.
     75  */
     76 
     77 #include <sys/param.h>
     78 #include <sys/stat.h>
     79 #include <sys/socket.h>
     80 #include <sys/queue.h>
     81 
     82 #include <ctype.h>
     83 #include <err.h>
     84 #include <errno.h>
     85 #include <fcntl.h>
     86 #include <glob.h>
     87 #include <libgen.h>
     88 #include <stdio.h>
     89 #include <stdlib.h>
     90 #include <string.h>
     91 #include <syslog.h>
     92 #include <unistd.h>
     93 
     94 #include "inetd.h"
     95 
     96 static void	config(void);
     97 static void	endconfig(void);
     98 static struct servtab	*enter(struct servtab *);
     99 static struct servtab	*getconfigent(char **);
    100 #ifdef DEBUG_ENABLE
    101 static void	print_service(const char *, struct servtab *);
    102 #endif
    103 static struct servtab	init_servtab(void);
    104 static void	include_configs(char *);
    105 static int	glob_error(const char *, int);
    106 static void	read_glob_configs(char *);
    107 static void	prepare_next_config(const char*);
    108 static bool	is_same_service(const struct servtab *, const struct servtab *);
    109 static char	*gen_file_pattern(const char *, const char *);
    110 static bool	check_no_reinclude(const char *);
    111 static void	include_matched_path(char *);
    112 static void	purge_unchecked(void);
    113 static void	freeconfig(struct servtab *);
    114 static char	*skip(char **);
    115 
    116 size_t	line_number;
    117 FILE	*fconfig;
    118 /* Temporary storage for new servtab */
    119 static struct	servtab serv;
    120 /* Current line from current config file */
    121 static char	line[LINE_MAX];
    122 char    *defhost;
    123 #ifdef IPSEC
    124 char *policy;
    125 #endif
    126 
    127 /*
    128  * Recursively merge loaded service definitions with any defined
    129  * in the current or included config files.
    130  */
    131 static void
    132 config(void)
    133 {
    134 	struct servtab *sep, *cp;
    135 	/*
    136 	 * Current position in line, used with key-values notation,
    137 	 * saves cp across getconfigent calls.
    138 	 */
    139 	char *current_pos;
    140 	size_t n;
    141 
    142 	/* open config file from beginning */
    143 	fconfig = fopen(CONFIG, "r");
    144 	if (fconfig == NULL) {
    145 		DPRINTF("Could not open file \"%s\": %s",
    146 		    CONFIG, strerror(errno));
    147 		syslog(LOG_ERR, "%s: %m", CONFIG);
    148 		return;
    149 	}
    150 
    151 	/* First call to nextline will advance line_number to 1 */
    152 	line_number = 0;
    153 
    154 	/* Start parsing at the beginning of the first line */
    155 	current_pos = nextline(fconfig);
    156 
    157 	while ((cp = getconfigent(&current_pos)) != NULL) {
    158 		/* Find an already existing service definition */
    159 		for (sep = servtab; sep != NULL; sep = sep->se_next)
    160 			if (is_same_service(sep, cp))
    161 				break;
    162 		if (sep != NULL) {
    163 			int i;
    164 
    165 #define SWAP(type, a, b) {type c = a; a = b; b = c;}
    166 
    167 			/*
    168 			 * sep->se_wait may be holding the pid of a daemon
    169 			 * that we're waiting for.  If so, don't overwrite
    170 			 * it unless the config file explicitly says don't
    171 			 * wait.
    172 			 */
    173 			if (cp->se_bi == 0 &&
    174 			    (sep->se_wait == 1 || cp->se_wait == 0))
    175 				sep->se_wait = cp->se_wait;
    176 			SWAP(char *, sep->se_user, cp->se_user);
    177 			SWAP(char *, sep->se_group, cp->se_group);
    178 			SWAP(char *, sep->se_server, cp->se_server);
    179 			for (i = 0; i < MAXARGV; i++)
    180 				SWAP(char *, sep->se_argv[i], cp->se_argv[i]);
    181 #ifdef IPSEC
    182 			SWAP(char *, sep->se_policy, cp->se_policy);
    183 #endif
    184 			SWAP(service_type, cp->se_type, sep->se_type);
    185 			SWAP(size_t, cp->se_service_max, sep->se_service_max);
    186 			SWAP(size_t, cp->se_ip_max, sep->se_ip_max);
    187 #undef SWAP
    188 			if (isrpcservice(sep))
    189 				unregister_rpc(sep);
    190 			sep->se_rpcversl = cp->se_rpcversl;
    191 			sep->se_rpcversh = cp->se_rpcversh;
    192 			freeconfig(cp);
    193 #ifdef DEBUG_ENABLE
    194 			if (debug)
    195 				print_service("REDO", sep);
    196 #endif
    197 		} else {
    198 			sep = enter(cp);
    199 #ifdef DEBUG_ENABLE
    200 			if (debug)
    201 				print_service("ADD ", sep);
    202 #endif
    203 		}
    204 		sep->se_checked = 1;
    205 
    206 		/*
    207 		 * Remainder of config(void) checks validity of servtab options
    208 		 * and sets up the service by setting up sockets
    209 		 * (in setup(servtab)).
    210 		 */
    211 		switch (sep->se_family) {
    212 		case AF_LOCAL:
    213 			if (sep->se_fd != -1)
    214 				break;
    215 			n = strlen(sep->se_service);
    216 			if (n >= sizeof(sep->se_ctrladdr_un.sun_path)) {
    217 				syslog(LOG_ERR, "%s/%s: address too long",
    218 				    sep->se_service, sep->se_proto);
    219 				sep->se_checked = 0;
    220 				continue;
    221 			}
    222 			(void)unlink(sep->se_service);
    223 			strlcpy(sep->se_ctrladdr_un.sun_path,
    224 			    sep->se_service, n + 1);
    225 			sep->se_ctrladdr_un.sun_family = AF_LOCAL;
    226 			sep->se_ctrladdr_size = (socklen_t)(n +
    227 			    sizeof(sep->se_ctrladdr_un) -
    228 			    sizeof(sep->se_ctrladdr_un.sun_path));
    229 			if (!ISMUX(sep))
    230 				setup(sep);
    231 			break;
    232 		case AF_INET:
    233 #ifdef INET6
    234 		case AF_INET6:
    235 #endif
    236 		    {
    237 			struct addrinfo hints, *res;
    238 			char *host;
    239 			const char *port;
    240 			int error;
    241 			int s;
    242 
    243 			/* check if the family is supported */
    244 			s = socket(sep->se_family, SOCK_DGRAM, 0);
    245 			if (s < 0) {
    246 				syslog(LOG_WARNING,
    247 				    "%s/%s: %s: the address family is not "
    248 				    "supported by the kernel",
    249 				    sep->se_service, sep->se_proto,
    250 				    sep->se_hostaddr);
    251 				sep->se_checked = false;
    252 				continue;
    253 			}
    254 			close(s);
    255 
    256 			memset(&hints, 0, sizeof(hints));
    257 			hints.ai_family = sep->se_family;
    258 			hints.ai_socktype = sep->se_socktype;
    259 			hints.ai_flags = AI_PASSIVE;
    260 			if (strcmp(sep->se_hostaddr, "*") == 0)
    261 				host = NULL;
    262 			else
    263 				host = sep->se_hostaddr;
    264 			if (isrpcservice(sep) || ISMUX(sep))
    265 				port = "0";
    266 			else
    267 				port = sep->se_service;
    268 			error = getaddrinfo(host, port, &hints, &res);
    269 			if (error != 0) {
    270 				if (error == EAI_SERVICE) {
    271 					/* gai_strerror not friendly enough */
    272 					syslog(LOG_WARNING, SERV_FMT ": "
    273 					    "unknown service",
    274 					    SERV_PARAMS(sep));
    275 				} else {
    276 					syslog(LOG_ERR, SERV_FMT ": %s: %s",
    277 					    SERV_PARAMS(sep),
    278 					    sep->se_hostaddr,
    279 					    gai_strerror(error));
    280 				}
    281 				sep->se_checked = false;
    282 				continue;
    283 			}
    284 			if (res->ai_next != NULL) {
    285 				syslog(LOG_ERR, SERV_FMT
    286 				    ": %s: resolved to multiple addr",
    287 				    SERV_PARAMS(sep),
    288 				    sep->se_hostaddr);
    289 				sep->se_checked = false;
    290 				freeaddrinfo(res);
    291 				continue;
    292 			}
    293 			memcpy(&sep->se_ctrladdr, res->ai_addr,
    294 				res->ai_addrlen);
    295 			if (ISMUX(sep)) {
    296 				sep->se_fd = -1;
    297 				freeaddrinfo(res);
    298 				continue;
    299 			}
    300 			sep->se_ctrladdr_size = res->ai_addrlen;
    301 			freeaddrinfo(res);
    302 #ifdef RPC
    303 			if (isrpcservice(sep)) {
    304 				struct rpcent *rp;
    305 
    306 				sep->se_rpcprog = atoi(sep->se_service);
    307 				if (sep->se_rpcprog == 0) {
    308 					rp = getrpcbyname(sep->se_service);
    309 					if (rp == 0) {
    310 						syslog(LOG_ERR,
    311 						    SERV_FMT
    312 						    ": unknown service",
    313 						    SERV_PARAMS(sep));
    314 						sep->se_checked = false;
    315 						continue;
    316 					}
    317 					sep->se_rpcprog = rp->r_number;
    318 				}
    319 				if (sep->se_fd == -1 && !ISMUX(sep))
    320 					setup(sep);
    321 				if (sep->se_fd != -1)
    322 					register_rpc(sep);
    323 			} else
    324 #endif /* RPC */
    325 			{
    326 				if (sep->se_fd >= 0)
    327 					close_sep(sep);
    328 				if (sep->se_fd == -1 && !ISMUX(sep))
    329 					setup(sep);
    330 			}
    331 		    }
    332 		}
    333 	}
    334 	endconfig();
    335 }
    336 
    337 static struct servtab *
    338 enter(struct servtab *cp)
    339 {
    340 	struct servtab *sep;
    341 
    342 	sep = malloc(sizeof (*sep));
    343 	if (sep == NULL) {
    344 		syslog(LOG_ERR, "Out of memory.");
    345 		exit(EXIT_FAILURE);
    346 	}
    347 	*sep = *cp;
    348 	sep->se_fd = -1;
    349 	sep->se_rpcprog = -1;
    350 	sep->se_next = servtab;
    351 	servtab = sep;
    352 	return (sep);
    353 }
    354 
    355 static void
    356 endconfig(void)
    357 {
    358 	if (fconfig != NULL) {
    359 		(void) fclose(fconfig);
    360 		fconfig = NULL;
    361 	}
    362 	if (defhost != NULL) {
    363 		free(defhost);
    364 		defhost = NULL;
    365 	}
    366 
    367 #ifdef IPSEC
    368 	if (policy != NULL) {
    369 		free(policy);
    370 		policy = NULL;
    371 	}
    372 #endif
    373 
    374 }
    375 
    376 #define LOG_EARLY_ENDCONF() \
    377 	ERR("Exiting %s early. Some services will be unavailable", CONFIG)
    378 
    379 #define LOG_TOO_FEW_ARGS() \
    380 	ERR("Expected more arguments")
    381 
    382 /* Parse the next service and apply any directives, and returns it as servtab */
    383 static struct servtab *
    384 getconfigent(char **current_pos)
    385 {
    386 	struct servtab *sep = &serv;
    387 	int argc, val;
    388 	char *cp, *cp0, *arg, *buf0, *buf1, *sz0, *sz1;
    389 	static char TCPMUX_TOKEN[] = "tcpmux/";
    390 #define MUX_LEN		(sizeof(TCPMUX_TOKEN)-1)
    391 	char *hostdelim;
    392 
    393 	/*
    394 	 * Pre-condition: current_pos points into line,
    395 	 * line contains config line. Continue where the last getconfigent
    396 	 * left off. Allows for multiple service definitions per line.
    397 	 */
    398 	cp = *current_pos;
    399 
    400 	if (/*CONSTCOND*/false) {
    401 		/*
    402 		 * Go to the next line, but only after attempting to read the
    403 		 * current one! Keep reading until we find a valid definition
    404 		 * or EOF.
    405 		 */
    406 more:
    407 		cp = nextline(fconfig);
    408 	}
    409 
    410 	if (cp == NULL) {
    411 		/* EOF or I/O error, let config() know to exit the file */
    412 		return NULL;
    413 	}
    414 
    415 	/* Comments and IPsec policies */
    416 	if (cp[0] == '#') {
    417 #ifdef IPSEC
    418 		/* lines starting with #@ is not a comment, but the policy */
    419 		if (cp[1] == '@') {
    420 			char *p;
    421 			for (p = cp + 2; isspace((unsigned char)*p); p++)
    422 				;
    423 			if (*p == '\0') {
    424 				if (policy)
    425 					free(policy);
    426 				policy = NULL;
    427 			} else {
    428 				if (ipsecsetup_test(p) < 0) {
    429 					ERR("Invalid IPsec policy \"%s\"", p);
    430 					LOG_EARLY_ENDCONF();
    431 					/*
    432 					 * Stop reading the current config to
    433 					 * prevent services from being run
    434 					 * without IPsec.
    435 					 */
    436 					return NULL;
    437 				} else {
    438 					if (policy)
    439 						free(policy);
    440 					policy = newstr(p);
    441 				}
    442 			}
    443 		}
    444 #endif
    445 
    446 		goto more;
    447 	}
    448 
    449 	/* Parse next token: listen-addr/hostname, service-spec, .include */
    450 	arg = skip(&cp);
    451 
    452 	if (cp == NULL) {
    453 		goto more;
    454 	}
    455 
    456 	if (arg[0] == '.') {
    457 		if (strcmp(&arg[1], "include") == 0) {
    458 			/* include directive */
    459 			arg = skip(&cp);
    460 			if (arg == NULL) {
    461 				LOG_TOO_FEW_ARGS();
    462 				return NULL;
    463 			}
    464 			include_configs(arg);
    465 			goto more;
    466 		} else {
    467 			ERR("Unknown directive '%s'", &arg[1]);
    468 			goto more;
    469 		}
    470 	}
    471 
    472 	/* After this point, we might need to store data in a servtab */
    473 	*sep = init_servtab();
    474 
    475 	/* Check for a host name. */
    476 	hostdelim = strrchr(arg, ':');
    477 	if (hostdelim != NULL) {
    478 		*hostdelim = '\0';
    479 		if (arg[0] == '[' && hostdelim > arg && hostdelim[-1] == ']') {
    480 			hostdelim[-1] = '\0';
    481 			sep->se_hostaddr = newstr(arg + 1);
    482 		} else
    483 			sep->se_hostaddr = newstr(arg);
    484 		arg = hostdelim + 1;
    485 		/*
    486 		 * If the line is of the form `host:', then just change the
    487 		 * default host for the following lines.
    488 		 */
    489 		if (*arg == '\0') {
    490 			arg = skip(&cp);
    491 			if (cp == NULL) {
    492 				free(defhost);
    493 				defhost = sep->se_hostaddr;
    494 				goto more;
    495 			}
    496 		}
    497 	} else {
    498 		/* No host address found, set it to NULL to indicate absence */
    499 		sep->se_hostaddr = NULL;
    500 	}
    501 	if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
    502 		char *c = arg + MUX_LEN;
    503 		if (*c == '+') {
    504 			sep->se_type = MUXPLUS_TYPE;
    505 			c++;
    506 		} else
    507 			sep->se_type = MUX_TYPE;
    508 		sep->se_service = newstr(c);
    509 	} else {
    510 		sep->se_service = newstr(arg);
    511 		sep->se_type = NORM_TYPE;
    512 	}
    513 
    514 	DPRINTCONF("Found service definition '%s'", sep->se_service);
    515 
    516 	/* on/off/socktype */
    517 	arg = skip(&cp);
    518 	if (arg == NULL) {
    519 		LOG_TOO_FEW_ARGS();
    520 		freeconfig(sep);
    521 		goto more;
    522 	}
    523 
    524 	/* Check for new v2 syntax */
    525 	if (strcmp(arg, "on") == 0 || strncmp(arg, "on#", 3) == 0) {
    526 
    527 		if (arg[2] == '#') {
    528 			cp = nextline(fconfig);
    529 		}
    530 
    531 		switch(parse_syntax_v2(sep, &cp)) {
    532 		case V2_SUCCESS:
    533 			*current_pos = cp;
    534 			return sep;
    535 		case V2_SKIP:
    536 			/*
    537 			 * Skip invalid definitions, freeconfig is called in
    538 			 * parse_v2.c
    539 			 */
    540 			*current_pos = cp;
    541 			freeconfig(sep);
    542 			goto more;
    543 		case V2_ERROR:
    544 			/*
    545 			 * Unrecoverable error, stop reading. freeconfig
    546 			 * is called in parse_v2.c
    547 			 */
    548 			LOG_EARLY_ENDCONF();
    549 			freeconfig(sep);
    550 			return NULL;
    551 		}
    552 	} else if (strcmp(arg, "off") == 0 || strncmp(arg, "off#", 4) == 0) {
    553 
    554 		if (arg[3] == '#') {
    555 			cp = nextline(fconfig);
    556 		}
    557 
    558 		/* Parse syntax the same as with 'on', but ignore the result */
    559 		switch(parse_syntax_v2(sep, &cp)) {
    560 		case V2_SUCCESS:
    561 		case V2_SKIP:
    562 			*current_pos = cp;
    563 			freeconfig(sep);
    564 			goto more;
    565 		case V2_ERROR:
    566 			/* Unrecoverable error, stop reading */
    567 			LOG_EARLY_ENDCONF();
    568 			freeconfig(sep);
    569 			return NULL;
    570 		}
    571 	} else {
    572 		/* continue parsing v1 */
    573 		parse_socktype(arg, sep);
    574 		if (sep->se_socktype == SOCK_STREAM) {
    575 			parse_accept_filter(arg, sep);
    576 		}
    577 		if (sep->se_hostaddr == NULL) {
    578 			/* Set host to current default */
    579 			sep->se_hostaddr = newstr(defhost);
    580 		}
    581 	}
    582 
    583 	/* protocol */
    584 	arg = skip(&cp);
    585 	if (arg == NULL) {
    586 		LOG_TOO_FEW_ARGS();
    587 		freeconfig(sep);
    588 		goto more;
    589 	}
    590 	if (sep->se_type == NORM_TYPE &&
    591 	    strncmp(arg, "faith/", strlen("faith/")) == 0) {
    592 		arg += strlen("faith/");
    593 		sep->se_type = FAITH_TYPE;
    594 	}
    595 	sep->se_proto = newstr(arg);
    596 
    597 #define	MALFORMED(arg) \
    598 do { \
    599 	ERR("%s: malformed buffer size option `%s'", \
    600 	    sep->se_service, (arg)); \
    601 	freeconfig(sep); \
    602 	goto more; \
    603 } while (false)
    604 
    605 #define	GETVAL(arg) \
    606 do { \
    607 	if (!isdigit((unsigned char)*(arg))) \
    608 		MALFORMED(arg); \
    609 	val = (int)strtol((arg), &cp0, 10); \
    610 	if (cp0 != NULL) { \
    611 		if (cp0[1] != '\0') \
    612 			MALFORMED((arg)); \
    613 		if (cp0[0] == 'k') \
    614 			val *= 1024; \
    615 		if (cp0[0] == 'm') \
    616 			val *= 1024 * 1024; \
    617 	} \
    618 	if (val < 1) { \
    619 		ERR("%s: invalid buffer size `%s'", \
    620 		    sep->se_service, (arg)); \
    621 		freeconfig(sep); \
    622 		goto more; \
    623 	} \
    624 } while (false)
    625 
    626 #define	ASSIGN(arg) \
    627 do { \
    628 	if (strcmp((arg), "sndbuf") == 0) \
    629 		sep->se_sndbuf = val; \
    630 	else if (strcmp((arg), "rcvbuf") == 0) \
    631 		sep->se_rcvbuf = val; \
    632 	else \
    633 		MALFORMED((arg)); \
    634 } while (false)
    635 
    636 	/*
    637 	 * Extract the send and receive buffer sizes before parsing
    638 	 * the protocol.
    639 	 */
    640 	sep->se_sndbuf = sep->se_rcvbuf = 0;
    641 	buf0 = buf1 = sz0 = sz1 = NULL;
    642 	if ((buf0 = strchr(sep->se_proto, ',')) != NULL) {
    643 		/* Not meaningful for Tcpmux services. */
    644 		if (ISMUX(sep)) {
    645 			ERR("%s: can't specify buffer sizes for "
    646 			    "tcpmux services", sep->se_service);
    647 			goto more;
    648 		}
    649 
    650 		/* Skip the , */
    651 		*buf0++ = '\0';
    652 
    653 		/* Check to see if another socket buffer size was specified. */
    654 		if ((buf1 = strchr(buf0, ',')) != NULL) {
    655 			/* Skip the , */
    656 			*buf1++ = '\0';
    657 
    658 			/* Make sure a 3rd one wasn't specified. */
    659 			if (strchr(buf1, ',') != NULL) {
    660 				ERR("%s: too many buffer sizes",
    661 				    sep->se_service);
    662 				goto more;
    663 			}
    664 
    665 			/* Locate the size. */
    666 			if ((sz1 = strchr(buf1, '=')) == NULL)
    667 				MALFORMED(buf1);
    668 
    669 			/* Skip the = */
    670 			*sz1++ = '\0';
    671 		}
    672 
    673 		/* Locate the size. */
    674 		if ((sz0 = strchr(buf0, '=')) == NULL)
    675 			MALFORMED(buf0);
    676 
    677 		/* Skip the = */
    678 		*sz0++ = '\0';
    679 
    680 		GETVAL(sz0);
    681 		ASSIGN(buf0);
    682 
    683 		if (buf1 != NULL) {
    684 			GETVAL(sz1);
    685 			ASSIGN(buf1);
    686 		}
    687 	}
    688 
    689 #undef ASSIGN
    690 #undef GETVAL
    691 #undef MALFORMED
    692 
    693 	if (parse_protocol(sep)) {
    694 		freeconfig(sep);
    695 		goto more;
    696 	}
    697 
    698 	/* wait/nowait:max */
    699 	arg = skip(&cp);
    700 	if (arg == NULL) {
    701 		LOG_TOO_FEW_ARGS();
    702 		freeconfig(sep);
    703 		goto more;
    704 	}
    705 
    706 	/* Rate limiting parsing */ {
    707 		char *cp1;
    708 		if ((cp1 = strchr(arg, ':')) == NULL)
    709 			cp1 = strchr(arg, '.');
    710 		if (cp1 != NULL) {
    711 			int rstatus;
    712 			*cp1++ = '\0';
    713 			sep->se_service_max = (size_t)strtou(cp1, NULL, 10, 0,
    714 			    SERVTAB_COUNT_MAX, &rstatus);
    715 
    716 			if (rstatus != 0) {
    717 				if (rstatus != ERANGE) {
    718 					/* For compatibility w/ atoi parsing */
    719 					sep->se_service_max = 0;
    720 				}
    721 
    722 				WRN("Improper \"max\" value '%s', "
    723 				    "using '%zu' instead: %s",
    724 				    cp1,
    725 				    sep->se_service_max,
    726 				    strerror(rstatus));
    727 			}
    728 
    729 		} else
    730 			sep->se_service_max = TOOMANY;
    731 	}
    732 	if (parse_wait(sep, strcmp(arg, "wait") == 0)) {
    733 		freeconfig(sep);
    734 		goto more;
    735 	}
    736 
    737 	/* Parse user:group token */
    738 	arg = skip(&cp);
    739 	if (arg == NULL) {
    740 		LOG_TOO_FEW_ARGS();
    741 		freeconfig(sep);
    742 		goto more;
    743 	}
    744 	char* separator = strchr(arg, ':');
    745 	if (separator == NULL) {
    746 		/* Backwards compatibility, allow dot instead of colon */
    747 		separator = strchr(arg, '.');
    748 	}
    749 
    750 	if (separator == NULL) {
    751 		/* Only user was specified */
    752 		sep->se_group = NULL;
    753 	} else {
    754 		*separator = '\0';
    755 		sep->se_group = newstr(separator + 1);
    756 	}
    757 
    758 	sep->se_user = newstr(arg);
    759 
    760 	/* Parser server-program (path to binary or "internal") */
    761 	arg = skip(&cp);
    762 	if (arg == NULL) {
    763 		LOG_TOO_FEW_ARGS();
    764 		freeconfig(sep);
    765 		goto more;
    766 	}
    767 	if (parse_server(sep, arg)) {
    768 		freeconfig(sep);
    769 		goto more;
    770 	}
    771 
    772 	argc = 0;
    773 	for (arg = skip(&cp); cp != NULL; arg = skip(&cp)) {
    774 		if (argc < MAXARGV)
    775 			sep->se_argv[argc++] = newstr(arg);
    776 	}
    777 	while (argc <= MAXARGV)
    778 		sep->se_argv[argc++] = NULL;
    779 #ifdef IPSEC
    780 	sep->se_policy = policy != NULL ? newstr(policy) : NULL;
    781 #endif
    782 	/* getconfigent read a positional service def, move to next line */
    783 	*current_pos = nextline(fconfig);
    784 	return (sep);
    785 }
    786 
    787 void
    788 freeconfig(struct servtab *cp)
    789 {
    790 	int i;
    791 
    792 	free(cp->se_hostaddr);
    793 	free(cp->se_service);
    794 	free(cp->se_proto);
    795 	free(cp->se_user);
    796 	free(cp->se_group);
    797 	free(cp->se_server);
    798 	for (i = 0; i < MAXARGV; i++)
    799 		free(cp->se_argv[i]);
    800 #ifdef IPSEC
    801 	free(cp->se_policy);
    802 #endif
    803 }
    804 
    805 /*
    806  * Get next token *in the current service definition* from config file.
    807  * Allows multi-line parse if single space or single tab-indented.
    808  * Things in quotes are considered single token.
    809  * Advances cp to next token.
    810  */
    811 static char *
    812 skip(char **cpp)
    813 {
    814 	char *cp = *cpp;
    815 	char *start;
    816 	char quote;
    817 
    818 	if (*cpp == NULL)
    819 		return (NULL);
    820 
    821 again:
    822 	while (*cp == ' ' || *cp == '\t')
    823 		cp++;
    824 	if (*cp == '\0') {
    825 		int c;
    826 
    827 		c = getc(fconfig);
    828 		(void) ungetc(c, fconfig);
    829 		if (c == ' ' || c == '\t')
    830 			if ((cp = nextline(fconfig)) != NULL)
    831 				goto again;
    832 		*cpp = NULL;
    833 		return (NULL);
    834 	}
    835 	start = cp;
    836 	/* Parse shell-style quotes */
    837 	quote = '\0';
    838 	while (*cp != '\0' && (quote != '\0' || (*cp != ' ' && *cp != '\t'))) {
    839 		if (*cp == '\'' || *cp == '"') {
    840 			if (quote != '\0' && *cp != quote)
    841 				cp++;
    842 			else {
    843 				if (quote != '\0')
    844 					quote = '\0';
    845 				else
    846 					quote = *cp;
    847 				memmove(cp, cp+1, strlen(cp));
    848 			}
    849 		} else
    850 			cp++;
    851 	}
    852 	if (*cp != '\0')
    853 		*cp++ = '\0';
    854 	*cpp = cp;
    855 	return (start);
    856 }
    857 
    858 char *
    859 nextline(FILE *fd)
    860 {
    861 	char *cp;
    862 
    863 	if (fgets(line, (int)sizeof(line), fd) == NULL) {
    864 		if (ferror(fd) != 0) {
    865 			ERR("Error when reading next line: %s",
    866 			    strerror(errno));
    867 		}
    868 		return NULL;
    869 	}
    870 	cp = strchr(line, '\n');
    871 	if (cp != NULL)
    872 		*cp = '\0';
    873 	line_number++;
    874 	return line;
    875 }
    876 
    877 char *
    878 newstr(const char *cp)
    879 {
    880 	char *dp;
    881 	if ((dp = strdup((cp != NULL) ? cp : "")) != NULL)
    882 		return (dp);
    883 	syslog(LOG_ERR, "strdup: %m");
    884 	exit(EXIT_FAILURE);
    885 	/*NOTREACHED*/
    886 }
    887 
    888 #ifdef DEBUG_ENABLE
    889 /*
    890  * print_service:
    891  *	Dump relevant information to stderr
    892  */
    893 static void
    894 print_service(const char *action, struct servtab *sep)
    895 {
    896 
    897 	if (isrpcservice(sep))
    898 		fprintf(stderr,
    899 		    "%s: %s rpcprog=%d, rpcvers = %d/%d, proto=%s, "
    900 		    "wait.max=%d.%zu, "
    901 		    "user:group=%s:%s builtin=%lx server=%s"
    902 #ifdef IPSEC
    903 		    " policy=\"%s\""
    904 #endif
    905 		    "\n",
    906 		    action, sep->se_service,
    907 		    sep->se_rpcprog, sep->se_rpcversh, sep->se_rpcversl,
    908 		    sep->se_proto, sep->se_wait, sep->se_service_max,
    909 		    sep->se_user, sep->se_group,
    910 		    (long)sep->se_bi, sep->se_server
    911 #ifdef IPSEC
    912 		    , (sep->se_policy != NULL ? sep->se_policy : "")
    913 #endif
    914 		    );
    915 	else
    916 		fprintf(stderr,
    917 		    "%s: %s:%s proto=%s%s, wait.max=%d.%zu, user:group=%s:%s "
    918 		    "builtin=%lx "
    919 		    "server=%s"
    920 #ifdef IPSEC
    921 		    " policy=%s"
    922 #endif
    923 		    "\n",
    924 		    action, sep->se_hostaddr, sep->se_service,
    925 		    sep->se_type == FAITH_TYPE ? "faith/" : "",
    926 		    sep->se_proto,
    927 		    sep->se_wait, sep->se_service_max, sep->se_user,
    928 		    sep->se_group, (long)sep->se_bi, sep->se_server
    929 #ifdef IPSEC
    930 		    , (sep->se_policy != NULL ? sep->se_policy : "")
    931 #endif
    932 		    );
    933 }
    934 #endif
    935 
    936 void
    937 config_root(void)
    938 {
    939 	struct servtab *sep;
    940 	/* Uncheck services */
    941 	for (sep = servtab; sep != NULL; sep = sep->se_next) {
    942 		sep->se_checked = false;
    943 	}
    944 	defhost = newstr("*");
    945 #ifdef IPSEC
    946 	policy = NULL;
    947 #endif
    948 	fconfig = NULL;
    949 	config();
    950 	purge_unchecked();
    951 }
    952 
    953 static void
    954 purge_unchecked(void)
    955 {
    956 	struct servtab *sep, **sepp = &servtab;
    957 	int servtab_count = 0;
    958 	while ((sep = *sepp) != NULL) {
    959 		if (sep->se_checked) {
    960 			sepp = &sep->se_next;
    961 			servtab_count++;
    962 			continue;
    963 		}
    964 		*sepp = sep->se_next;
    965 		if (sep->se_fd >= 0)
    966 			close_sep(sep);
    967 		if (isrpcservice(sep))
    968 			unregister_rpc(sep);
    969 		if (sep->se_family == AF_LOCAL)
    970 			(void)unlink(sep->se_service);
    971 #ifdef DEBUG_ENABLE
    972 		if (debug)
    973 			print_service("FREE", sep);
    974 #endif
    975 		freeconfig(sep);
    976 		free(sep);
    977 	}
    978 	DPRINTF("%d service(s) loaded.", servtab_count);
    979 }
    980 
    981 static bool
    982 is_same_service(const struct servtab *sep, const struct servtab *cp)
    983 {
    984 	return
    985 	    strcmp(sep->se_service, cp->se_service) == 0 &&
    986 	    strcmp(sep->se_hostaddr, cp->se_hostaddr) == 0 &&
    987 	    strcmp(sep->se_proto, cp->se_proto) == 0 &&
    988 	    sep->se_family == cp->se_family &&
    989 	    ISMUX(sep) == ISMUX(cp);
    990 }
    991 
    992 int
    993 parse_protocol(struct servtab *sep)
    994 {
    995 	int val;
    996 
    997 	if (strcmp(sep->se_proto, "unix") == 0) {
    998 		sep->se_family = AF_LOCAL;
    999 	} else {
   1000 		val = (int)strlen(sep->se_proto);
   1001 		if (val == 0) {
   1002 			ERR("%s: invalid protocol specified",
   1003 			    sep->se_service);
   1004 			return -1;
   1005 		}
   1006 		val = sep->se_proto[val - 1];
   1007 		switch (val) {
   1008 		case '4':	/*tcp4 or udp4*/
   1009 			sep->se_family = AF_INET;
   1010 			break;
   1011 #ifdef INET6
   1012 		case '6':	/*tcp6 or udp6*/
   1013 			sep->se_family = AF_INET6;
   1014 			break;
   1015 #endif
   1016 		default:
   1017 			/*
   1018 			 * Use 'default' IP version which is IPv4, may
   1019 			 * eventually be changed to AF_INET6
   1020 			 */
   1021 			sep->se_family = AF_INET;
   1022 			break;
   1023 		}
   1024 		if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
   1025 #ifdef RPC
   1026 			char *cp1, *ccp;
   1027 			cp1 = strchr(sep->se_service, '/');
   1028 			if (cp1 == 0) {
   1029 				ERR("%s: no rpc version",
   1030 				    sep->se_service);
   1031 				return -1;
   1032 			}
   1033 			*cp1++ = '\0';
   1034 			sep->se_rpcversl = sep->se_rpcversh =
   1035 			    (int)strtol(cp1, &ccp, 0);
   1036 			if (ccp == cp1) {
   1037 		badafterall:
   1038 				ERR("%s/%s: bad rpc version",
   1039 				    sep->se_service, cp1);
   1040 				return -1;
   1041 			}
   1042 			if (*ccp == '-') {
   1043 				cp1 = ccp + 1;
   1044 				sep->se_rpcversh = (int)strtol(cp1, &ccp, 0);
   1045 				if (ccp == cp1)
   1046 					goto badafterall;
   1047 			}
   1048 #else
   1049 			ERR("%s: rpc services not supported",
   1050 			    sep->se_service);
   1051 			return -1;
   1052 #endif /* RPC */
   1053 		}
   1054 	}
   1055 	return 0;
   1056 }
   1057 
   1058 int
   1059 parse_wait(struct servtab *sep, int wait)
   1060 {
   1061 	if (!ISMUX(sep)) {
   1062 		sep->se_wait = wait;
   1063 		return 0;
   1064 	}
   1065 	/*
   1066 	 * Silently enforce "nowait" for TCPMUX services since
   1067 	 * they don't have an assigned port to listen on.
   1068 	 */
   1069 	sep->se_wait = 0;
   1070 
   1071 	if (strncmp(sep->se_proto, "tcp", 3)) {
   1072 		ERR("bad protocol for tcpmux service %s",
   1073 			sep->se_service);
   1074 		return -1;
   1075 	}
   1076 	if (sep->se_socktype != SOCK_STREAM) {
   1077 		ERR("bad socket type for tcpmux service %s",
   1078 		    sep->se_service);
   1079 		return -1;
   1080 	}
   1081 	return 0;
   1082 }
   1083 
   1084 int
   1085 parse_server(struct servtab *sep, const char *arg)
   1086 {
   1087 	sep->se_server = newstr(arg);
   1088 	if (strcmp(sep->se_server, "internal") != 0) {
   1089 		sep->se_bi = NULL;
   1090 		return 0;
   1091 	}
   1092 
   1093 	if (!try_biltin(sep)) {
   1094 		ERR("Internal service %s unknown", sep->se_service);
   1095 		return -1;
   1096 	}
   1097 	return 0;
   1098 }
   1099 
   1100 /* TODO test to make sure accept filter still works */
   1101 void
   1102 parse_accept_filter(char *arg, struct servtab *sep)
   1103 {
   1104 	char *accf, *accf_arg;
   1105 	/* one and only one accept filter */
   1106 	accf = strchr(arg, ':');
   1107 	if (accf == NULL)
   1108 		return;
   1109 	if (accf != strrchr(arg, ':') || *(accf + 1) == '\0') {
   1110 		/* more than one ||  nothing beyond */
   1111 		sep->se_socktype = -1;
   1112 		return;
   1113 	}
   1114 
   1115 	accf++;			/* skip delimiter */
   1116 	strlcpy(sep->se_accf.af_name, accf, sizeof(sep->se_accf.af_name));
   1117 	accf_arg = strchr(accf, ',');
   1118 	if (accf_arg == NULL)	/* zero or one arg, no more */
   1119 		return;
   1120 
   1121 	if (strrchr(accf, ',') != accf_arg) {
   1122 		sep->se_socktype = -1;
   1123 	} else {
   1124 		accf_arg++;
   1125 		strlcpy(sep->se_accf.af_arg, accf_arg,
   1126 		    sizeof(sep->se_accf.af_arg));
   1127 	}
   1128 }
   1129 
   1130 void
   1131 parse_socktype(char* arg, struct servtab* sep)
   1132 {
   1133 	/* stream socket may have an accept filter, only check first chars */
   1134 	if (strncmp(arg, "stream", sizeof("stream") - 1) == 0)
   1135 		sep->se_socktype = SOCK_STREAM;
   1136 	else if (strcmp(arg, "dgram") == 0)
   1137 		sep->se_socktype = SOCK_DGRAM;
   1138 	else if (strcmp(arg, "rdm") == 0)
   1139 		sep->se_socktype = SOCK_RDM;
   1140 	else if (strcmp(arg, "seqpacket") == 0)
   1141 		sep->se_socktype = SOCK_SEQPACKET;
   1142 	else if (strcmp(arg, "raw") == 0)
   1143 		sep->se_socktype = SOCK_RAW;
   1144 	else
   1145 		sep->se_socktype = -1;
   1146 }
   1147 
   1148 static struct servtab
   1149 init_servtab(void)
   1150 {
   1151 	/* This does not set every field to default. See enter() as well */
   1152 	return (struct servtab) {
   1153 		/*
   1154 		 * Set se_max to non-zero so uninitialized value is not
   1155 	 	 * a valid value. Useful in v2 syntax parsing.
   1156 		 */
   1157 		.se_service_max = SERVTAB_UNSPEC_SIZE_T,
   1158 		.se_ip_max = SERVTAB_UNSPEC_SIZE_T,
   1159 		.se_wait = SERVTAB_UNSPEC_VAL,
   1160 		.se_socktype = SERVTAB_UNSPEC_VAL,
   1161 		.se_rl_ip_list = SLIST_HEAD_INITIALIZER(se_ip_list_head)
   1162 		/* All other fields initialized to 0 or null */
   1163 	};
   1164 }
   1165 
   1166 /* Include directives bookkeeping structure */
   1167 struct file_list {
   1168 	/* Absolute path used for checking for circular references */
   1169 	char *abs;
   1170 	/* Pointer to the absolute path of the parent config file,
   1171 	 * on the stack */
   1172 	struct file_list *next;
   1173 } *file_list_head;
   1174 
   1175 static void
   1176 include_configs(char *pattern)
   1177 {
   1178 	/* Allocate global per-config state on the thread stack */
   1179 	const char* save_CONFIG;
   1180 	FILE	*save_fconfig;
   1181 	size_t	save_line_number;
   1182 	char    *save_defhost;
   1183 	struct	file_list new_file;
   1184 #ifdef IPSEC
   1185 	char *save_policy;
   1186 #endif
   1187 
   1188 	/* Store current globals on the stack */
   1189 	save_CONFIG = CONFIG;
   1190 	save_fconfig = fconfig;
   1191 	save_line_number = line_number;
   1192 	save_defhost = defhost;
   1193 	new_file.abs = realpath(CONFIG, NULL);
   1194 	new_file.next = file_list_head;
   1195 #ifdef IPSEC
   1196 	save_policy = policy;
   1197 #endif
   1198 	/* Put new_file at the top of the config stack */
   1199 	file_list_head = &new_file;
   1200 	read_glob_configs(pattern);
   1201 	free(new_file.abs);
   1202 	/* Pop new_file off the stack */
   1203 	file_list_head = new_file.next;
   1204 
   1205 	/* Restore global per-config state */
   1206 	CONFIG = save_CONFIG;
   1207 	fconfig = save_fconfig;
   1208 	line_number = save_line_number;
   1209 	defhost = save_defhost;
   1210 #ifdef IPSEC
   1211 	policy = save_policy;
   1212 #endif
   1213 }
   1214 
   1215 static void
   1216 prepare_next_config(const char *file_name)
   1217 {
   1218 	/* Setup new state that is normally only done in main */
   1219 	CONFIG = file_name;
   1220 
   1221 	/* Inherit default host and IPsec policy */
   1222 	defhost = newstr(defhost);
   1223 
   1224 #ifdef IPSEC
   1225 	policy = (policy == NULL) ? NULL : newstr(policy);
   1226 #endif
   1227 }
   1228 
   1229 static void
   1230 read_glob_configs(char *pattern)
   1231 {
   1232 	glob_t results;
   1233 	char *full_pattern;
   1234 	int glob_result;
   1235 	full_pattern = gen_file_pattern(CONFIG, pattern);
   1236 
   1237 	DPRINTCONF("Found include directive '%s'", full_pattern);
   1238 
   1239 	glob_result = glob(full_pattern, GLOB_NOSORT, glob_error, &results);
   1240 	switch(glob_result) {
   1241 	case 0:
   1242 		/* No glob errors */
   1243 		break;
   1244 	case GLOB_ABORTED:
   1245 		ERR("Error while searching for include files");
   1246 		break;
   1247 	case GLOB_NOMATCH:
   1248 		/* It's fine if no files were matched. */
   1249 		DPRINTCONF("No files matched pattern '%s'", full_pattern);
   1250 		break;
   1251 	case GLOB_NOSPACE:
   1252 		ERR("Error when searching for include files: %s",
   1253 		    strerror(errno));
   1254 		break;
   1255 	default:
   1256 		ERR("Unknown glob(3) error %d", errno);
   1257 		break;
   1258 	}
   1259 	free(full_pattern);
   1260 
   1261 	for (size_t i = 0; i < results.gl_pathc; i++) {
   1262 		include_matched_path(results.gl_pathv[i]);
   1263 	}
   1264 
   1265 	globfree(&results);
   1266 }
   1267 
   1268 static void
   1269 include_matched_path(char *glob_path)
   1270 {
   1271 	struct stat sb;
   1272 	char *tmp;
   1273 
   1274 	if (lstat(glob_path, &sb) != 0) {
   1275 		ERR("Error calling stat on path '%s': %s", glob_path,
   1276 		    strerror(errno));
   1277 		return;
   1278 	}
   1279 
   1280 	if (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode)) {
   1281 		DPRINTCONF("'%s' is not a file.", glob_path);
   1282 		ERR("The matched path '%s' is not a regular file", glob_path);
   1283 		return;
   1284 	}
   1285 
   1286 	DPRINTCONF("Include '%s'", glob_path);
   1287 
   1288 	if (S_ISLNK(sb.st_mode)) {
   1289 		tmp = glob_path;
   1290 		glob_path = realpath(tmp, NULL);
   1291 	}
   1292 
   1293 	/* Ensure the file is not being reincluded .*/
   1294 	if (check_no_reinclude(glob_path)) {
   1295 		prepare_next_config(glob_path);
   1296 		config();
   1297 	} else {
   1298 		DPRINTCONF("File '%s' already included in current include "
   1299 		    "chain", glob_path);
   1300 		WRN("Including file '%s' would cause a circular "
   1301 		    "dependency", glob_path);
   1302 	}
   1303 
   1304 	if (S_ISLNK(sb.st_mode)) {
   1305 		free(glob_path);
   1306 		glob_path = tmp;
   1307 	}
   1308 }
   1309 
   1310 static bool
   1311 check_no_reinclude(const char *glob_path)
   1312 {
   1313 	struct file_list *cur = file_list_head;
   1314 	char *abs_path = realpath(glob_path, NULL);
   1315 
   1316 	if (abs_path == NULL) {
   1317 		ERR("Error checking real path for '%s': %s",
   1318 			glob_path, strerror(errno));
   1319 		return false;
   1320 	}
   1321 
   1322 	DPRINTCONF("Absolute path '%s'", abs_path);
   1323 
   1324 	for (cur = file_list_head; cur != NULL; cur = cur->next) {
   1325 		if (strcmp(cur->abs, abs_path) == 0) {
   1326 			/* file included more than once */
   1327 			/* TODO relative or abs path for logging error? */
   1328 			free(abs_path);
   1329 			return false;
   1330 		}
   1331 	}
   1332 	free(abs_path);
   1333 	return true;
   1334 }
   1335 
   1336 /* Resolve the pattern relative to the config file the pattern is from  */
   1337 static char *
   1338 gen_file_pattern(const char *cur_config, const char *pattern)
   1339 {
   1340 	if (pattern[0] == '/') {
   1341 		/* Absolute paths don't need any normalization */
   1342 		return newstr(pattern);
   1343 	}
   1344 
   1345 	/* pattern is relative */
   1346 	/* Find the end of the file's directory */
   1347 	size_t i, last = 0;
   1348 	for (i = 0; cur_config[i] != '\0'; i++) {
   1349 		if (cur_config[i] == '/') {
   1350 			last = i;
   1351 		}
   1352 	}
   1353 
   1354 	if (last == 0) {
   1355 		/* cur_config is just a filename, pattern already correct */
   1356 		return newstr(pattern);
   1357 	}
   1358 
   1359 	/* Relativize pattern to cur_config file's directory */
   1360 	char *full_pattern = malloc(last + 1 + strlen(pattern) + 1);
   1361 	if (full_pattern == NULL) {
   1362 		syslog(LOG_ERR, "Out of memory.");
   1363 		exit(EXIT_FAILURE);
   1364 	}
   1365 	memcpy(full_pattern, cur_config, last);
   1366 	full_pattern[last] = '/';
   1367 	strcpy(&full_pattern[last + 1], pattern);
   1368 	return full_pattern;
   1369 }
   1370 
   1371 static int
   1372 glob_error(const char *path, int error)
   1373 {
   1374 	WRN("Error while resolving path '%s': %s", path, strerror(error));
   1375 	return 0;
   1376 }
   1377