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