17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*23a1cceaSRoger A. Faulkner  * Common Development and Distribution License (the "License").
6*23a1cceaSRoger A. Faulkner  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*23a1cceaSRoger A. Faulkner 
227c478bd9Sstevel@tonic-gate /*
23*23a1cceaSRoger A. Faulkner  * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #include <ctype.h>
277c478bd9Sstevel@tonic-gate #include <errno.h>
287c478bd9Sstevel@tonic-gate #include <locale.h>
297c478bd9Sstevel@tonic-gate #include <stdarg.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <strings.h>
337c478bd9Sstevel@tonic-gate #include <string.h>
347c478bd9Sstevel@tonic-gate #include <syslog.h>
357c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
367c478bd9Sstevel@tonic-gate #include <assert.h>
377c478bd9Sstevel@tonic-gate #include <sys/types.h>
387c478bd9Sstevel@tonic-gate #include <sys/stat.h>
397c478bd9Sstevel@tonic-gate #include <unistd.h>
407c478bd9Sstevel@tonic-gate #include <fcntl.h>
417c478bd9Sstevel@tonic-gate #include "nfslog_config.h"
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #define	ERROR_BUFSZ	100
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate /*
467c478bd9Sstevel@tonic-gate  * This flag controls where error messages go.
477c478bd9Sstevel@tonic-gate  * Zero means that messages go to stderr.
487c478bd9Sstevel@tonic-gate  * Non-zero means that messages go to syslog.
497c478bd9Sstevel@tonic-gate  */
507c478bd9Sstevel@tonic-gate boolean_t nfsl_errs_to_syslog;
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate /*
537c478bd9Sstevel@tonic-gate  * Pointer to the global entry in the list
547c478bd9Sstevel@tonic-gate  */
557c478bd9Sstevel@tonic-gate static nfsl_config_t *global = NULL;
567c478bd9Sstevel@tonic-gate 
577c478bd9Sstevel@tonic-gate /*
587c478bd9Sstevel@tonic-gate  * Pointer to the raw global entry in the list, this is the
597c478bd9Sstevel@tonic-gate  * global entry without the expanded paths. This is used to
607c478bd9Sstevel@tonic-gate  * complete configurations.
617c478bd9Sstevel@tonic-gate  */
627c478bd9Sstevel@tonic-gate static nfsl_config_t *global_raw = NULL;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate /*
657c478bd9Sstevel@tonic-gate  * Last modification time to config file.
667c478bd9Sstevel@tonic-gate  */
677c478bd9Sstevel@tonic-gate static timestruc_t config_last_modification = { 0 };
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * Whitespace characters to delimit fields in a line.
717c478bd9Sstevel@tonic-gate  */
727c478bd9Sstevel@tonic-gate static const char *whitespace = " \t";
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate static int getconfiglist(nfsl_config_t **, boolean_t);
757c478bd9Sstevel@tonic-gate static nfsl_config_t *create_config(char *, char *, char *, char *, char *,
767c478bd9Sstevel@tonic-gate 			char *, int, boolean_t, int *);
777c478bd9Sstevel@tonic-gate static nfsl_config_t *create_global_raw(int *);
787c478bd9Sstevel@tonic-gate static int update_config(nfsl_config_t *, char *, char *, char *,
797c478bd9Sstevel@tonic-gate 			char *, char *, char *, int, boolean_t, boolean_t);
807c478bd9Sstevel@tonic-gate static int update_field(char **, char *, char *, boolean_t *);
817c478bd9Sstevel@tonic-gate static nfsl_config_t *findconfig(nfsl_config_t **, char *, boolean_t,
827c478bd9Sstevel@tonic-gate 			nfsl_config_t **);
837c478bd9Sstevel@tonic-gate static nfsl_config_t *getlastconfig(nfsl_config_t *);
847c478bd9Sstevel@tonic-gate static void complete_with_global(char **, char **, char **, char **,
857c478bd9Sstevel@tonic-gate 			char **, int *);
867c478bd9Sstevel@tonic-gate #ifdef DEBUG
877c478bd9Sstevel@tonic-gate static void remove_config(nfsl_config_t **, nfsl_config_t *, nfsl_config_t **);
887c478bd9Sstevel@tonic-gate void nfsl_printconfig(nfsl_config_t *);
897c478bd9Sstevel@tonic-gate #endif /* DEBUG */
90*23a1cceaSRoger A. Faulkner static char *gataline(FILE *, char *, char *, int);
917c478bd9Sstevel@tonic-gate static int get_info(char *, char **, char **, char **, char **, char **,
927c478bd9Sstevel@tonic-gate 			char **, int *);
937c478bd9Sstevel@tonic-gate static void free_config(nfsl_config_t *);
947c478bd9Sstevel@tonic-gate static int is_legal_tag(char *);
957c478bd9Sstevel@tonic-gate static boolean_t is_complete_config(char *, char *, char *, char *);
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate /*
987c478bd9Sstevel@tonic-gate  * Read the configuration file and create a list of configuration
997c478bd9Sstevel@tonic-gate  * parameters.  Returns zero for success or an errno value.
1007c478bd9Sstevel@tonic-gate  * The caller is responsible for freeing the returned configlist by calling
1017c478bd9Sstevel@tonic-gate  * nfsl_freeconfig_list().
1027c478bd9Sstevel@tonic-gate  *
1037c478bd9Sstevel@tonic-gate  * If the configuration file does not exist, *listpp points to a config entry
1047c478bd9Sstevel@tonic-gate  * containing the hardwired defaults.
1057c478bd9Sstevel@tonic-gate  */
1067c478bd9Sstevel@tonic-gate int
1077c478bd9Sstevel@tonic-gate nfsl_getconfig_list(nfsl_config_t **listpp)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate 	int error = 0;
1107c478bd9Sstevel@tonic-gate 	char *locale;
1117c478bd9Sstevel@tonic-gate 
1127c478bd9Sstevel@tonic-gate 	/*
1137c478bd9Sstevel@tonic-gate 	 * Set the locale correctly so that we can correctly identify
1147c478bd9Sstevel@tonic-gate 	 * alphabetic characters.
1157c478bd9Sstevel@tonic-gate 	 */
1167c478bd9Sstevel@tonic-gate 	if ((locale = getenv("LC_ALL")) != NULL)
1177c478bd9Sstevel@tonic-gate 		(void) setlocale(LC_ALL, locale);
1187c478bd9Sstevel@tonic-gate 	else if ((locale = getenv("LC_CTYPE")) != NULL)
1197c478bd9Sstevel@tonic-gate 		(void) setlocale(LC_CTYPE, locale);
1207c478bd9Sstevel@tonic-gate 	else if ((locale = getenv("LANG")) != NULL)
1217c478bd9Sstevel@tonic-gate 		(void) setlocale(LC_CTYPE, locale);
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 	/*
1247c478bd9Sstevel@tonic-gate 	 * Allocate 'global_raw' structure, its contents are
1257c478bd9Sstevel@tonic-gate 	 * indirectly allocated by create_config().
1267c478bd9Sstevel@tonic-gate 	 */
1277c478bd9Sstevel@tonic-gate 	assert(global_raw == NULL);
1287c478bd9Sstevel@tonic-gate 	global_raw = create_global_raw(&error);
1297c478bd9Sstevel@tonic-gate 	if (global_raw == NULL)
1307c478bd9Sstevel@tonic-gate 		return (error);
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	/*
1337c478bd9Sstevel@tonic-gate 	 * Build global entry with hardwired defaults first.
1347c478bd9Sstevel@tonic-gate 	 */
1357c478bd9Sstevel@tonic-gate 	assert(global == NULL);
1367c478bd9Sstevel@tonic-gate 	global = create_config(DEFAULTTAG, DEFAULTDIR, BUFFERPATH, NULL,
1377c478bd9Sstevel@tonic-gate 			FHPATH, LOGPATH, TRANSLOG_BASIC, B_TRUE, &error);
1387c478bd9Sstevel@tonic-gate 	*listpp = global;
1397c478bd9Sstevel@tonic-gate 	if (global == NULL) {
1407c478bd9Sstevel@tonic-gate 		free_config(global_raw);
1417c478bd9Sstevel@tonic-gate 		return (error);
1427c478bd9Sstevel@tonic-gate 	}
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	if (error = getconfiglist(listpp, B_FALSE))
1457c478bd9Sstevel@tonic-gate 		nfsl_freeconfig_list(listpp);
1467c478bd9Sstevel@tonic-gate 	else {
1477c478bd9Sstevel@tonic-gate 		assert(global != NULL);
1487c478bd9Sstevel@tonic-gate 		/*
1497c478bd9Sstevel@tonic-gate 		 * The global entry was replaced with the one in the file,
1507c478bd9Sstevel@tonic-gate 		 * clear the UPDATED flag
1517c478bd9Sstevel@tonic-gate 		 */
1527c478bd9Sstevel@tonic-gate 		global->nc_flags &= ~NC_UPDATED;
1537c478bd9Sstevel@tonic-gate 	}
1547c478bd9Sstevel@tonic-gate 	return (error);
1557c478bd9Sstevel@tonic-gate }
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate /*
1587c478bd9Sstevel@tonic-gate  * Allocates memory for the 'global_raw' structure.
1597c478bd9Sstevel@tonic-gate  * The actual allocation of values for its components happens in
1607c478bd9Sstevel@tonic-gate  * update_config().
1617c478bd9Sstevel@tonic-gate  */
1627c478bd9Sstevel@tonic-gate static nfsl_config_t *
1637c478bd9Sstevel@tonic-gate create_global_raw(int *error)
1647c478bd9Sstevel@tonic-gate {
1657c478bd9Sstevel@tonic-gate 	nfsl_config_t *p;
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	*error = 0;
1687c478bd9Sstevel@tonic-gate 	if (p = (nfsl_config_t *)malloc(sizeof (*p)))
1697c478bd9Sstevel@tonic-gate 		(void) memset((void *)p, 0, sizeof (*p));
1707c478bd9Sstevel@tonic-gate 	else
1717c478bd9Sstevel@tonic-gate 		*error = ENOMEM;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate 	return (p);
1747c478bd9Sstevel@tonic-gate }
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate /*
1777c478bd9Sstevel@tonic-gate  * Checks if the the configuration file has been modified since we last
1787c478bd9Sstevel@tonic-gate  * read it, if not simply returns, otherwise it re-reads it adding new
1797c478bd9Sstevel@tonic-gate  * configuration entries. Note that existing entries that no longer
1807c478bd9Sstevel@tonic-gate  * exist in the configuration file are not removed. Existing entries
1817c478bd9Sstevel@tonic-gate  * that are modified in the configuration file are updated in the list
1827c478bd9Sstevel@tonic-gate  * as well.
1837c478bd9Sstevel@tonic-gate  * if 'updated' is defined then it is set to TRUE if the list was modified.
1847c478bd9Sstevel@tonic-gate  *
1857c478bd9Sstevel@tonic-gate  * Note that if an error occurs, the list may be corrupted.
1867c478bd9Sstevel@tonic-gate  * It is the responsibility of the caller to free the list.
1877c478bd9Sstevel@tonic-gate  * If the configuration file does not exist, we simply return the list
1887c478bd9Sstevel@tonic-gate  * that we previously had, log a message and return success.
1897c478bd9Sstevel@tonic-gate  */
1907c478bd9Sstevel@tonic-gate int
1917c478bd9Sstevel@tonic-gate nfsl_checkconfig_list(nfsl_config_t **listpp, boolean_t *updated)
1927c478bd9Sstevel@tonic-gate {
1937c478bd9Sstevel@tonic-gate 	struct stat st;
1947c478bd9Sstevel@tonic-gate 	int error = 0;
1957c478bd9Sstevel@tonic-gate 
1967c478bd9Sstevel@tonic-gate 	if (updated != NULL)
1977c478bd9Sstevel@tonic-gate 		*updated = B_FALSE;
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	if (stat(NFSL_CONFIG_FILE_PATH, &st) == -1) {
2007c478bd9Sstevel@tonic-gate 		error = errno;
2017c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
2027c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
2037c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH,
2047c478bd9Sstevel@tonic-gate 				strerror(error));
2057c478bd9Sstevel@tonic-gate 		} else {
2067c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
2077c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH,
2087c478bd9Sstevel@tonic-gate 				strerror(error));
2097c478bd9Sstevel@tonic-gate 		}
2107c478bd9Sstevel@tonic-gate 		return (0);
2117c478bd9Sstevel@tonic-gate 	}
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	if (config_last_modification.tv_sec == st.st_mtim.tv_sec &&
2147c478bd9Sstevel@tonic-gate 	    config_last_modification.tv_nsec == st.st_mtim.tv_nsec)
2157c478bd9Sstevel@tonic-gate 		return (0);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	if (updated != NULL)
2187c478bd9Sstevel@tonic-gate 		*updated = B_TRUE;
2197c478bd9Sstevel@tonic-gate 
2207c478bd9Sstevel@tonic-gate 	return (getconfiglist(listpp, B_TRUE));
2217c478bd9Sstevel@tonic-gate }
2227c478bd9Sstevel@tonic-gate 
2237c478bd9Sstevel@tonic-gate /*
2247c478bd9Sstevel@tonic-gate  * Does the real work. Reads the configuration file and creates the
2257c478bd9Sstevel@tonic-gate  * list of entries. Assumes that *listpp contains at least one entry.
2267c478bd9Sstevel@tonic-gate  * The caller is responsible for freeing any config entries added to
2277c478bd9Sstevel@tonic-gate  * the list whether this routine returns an error or not.
2287c478bd9Sstevel@tonic-gate  *
2297c478bd9Sstevel@tonic-gate  * Returns 0 on success and updates the '*listpp' config list,
2307c478bd9Sstevel@tonic-gate  * Returns non-zero error value otherwise.
2317c478bd9Sstevel@tonic-gate  */
2327c478bd9Sstevel@tonic-gate static int
2337c478bd9Sstevel@tonic-gate getconfiglist(nfsl_config_t **listpp, boolean_t updating)
2347c478bd9Sstevel@tonic-gate {
2357c478bd9Sstevel@tonic-gate 	FILE *fp;
2367c478bd9Sstevel@tonic-gate 	int error = 0;
2377c478bd9Sstevel@tonic-gate 	nfsl_config_t *listp = NULL, *tail = NULL;
2387c478bd9Sstevel@tonic-gate 	char linebuf[MAX_LINESZ];
2397c478bd9Sstevel@tonic-gate 	char errorbuf[ERROR_BUFSZ];
2407c478bd9Sstevel@tonic-gate 	char *tag, *defaultdir, *bufferpath, *rpclogpath, *fhpath, *logpath;
2417c478bd9Sstevel@tonic-gate 	int logformat;
2427c478bd9Sstevel@tonic-gate 	flock_t flock;
2437c478bd9Sstevel@tonic-gate 	struct stat st;
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	fp = fopen(NFSL_CONFIG_FILE_PATH, "r");
2467c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
2477c478bd9Sstevel@tonic-gate 		if (updating) {
2487c478bd9Sstevel@tonic-gate 			(void) sprintf(errorbuf, "Can't open %s",
2497c478bd9Sstevel@tonic-gate 				NFSL_CONFIG_FILE_PATH);
2507c478bd9Sstevel@tonic-gate 		} else {
2517c478bd9Sstevel@tonic-gate 			(void) sprintf(errorbuf,
2527c478bd9Sstevel@tonic-gate 				"Can't open %s - using hardwired defaults",
2537c478bd9Sstevel@tonic-gate 				NFSL_CONFIG_FILE_PATH);
2547c478bd9Sstevel@tonic-gate 		}
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate 		/*
2577c478bd9Sstevel@tonic-gate 		 * Use hardwired config.
2587c478bd9Sstevel@tonic-gate 		 */
2597c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog)
2607c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext("%s"), errorbuf);
2617c478bd9Sstevel@tonic-gate 		else
2627c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("%s\n"), errorbuf);
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 		return (0);
2657c478bd9Sstevel@tonic-gate 	}
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	(void) memset((void *) &flock, 0, sizeof (flock));
2687c478bd9Sstevel@tonic-gate 	flock.l_type = F_RDLCK;
2697c478bd9Sstevel@tonic-gate 	if (fcntl(fileno(fp), F_SETLKW, &flock) == -1) {
2707c478bd9Sstevel@tonic-gate 		error = errno;
2717c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
2727c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
2737c478bd9Sstevel@tonic-gate 				"Can't lock %s - %s"), NFSL_CONFIG_FILE_PATH,
2747c478bd9Sstevel@tonic-gate 				strerror(error));
2757c478bd9Sstevel@tonic-gate 		} else {
2767c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
2777c478bd9Sstevel@tonic-gate 				"Can't lock %s - %s\n"), NFSL_CONFIG_FILE_PATH,
2787c478bd9Sstevel@tonic-gate 				strerror(error));
2797c478bd9Sstevel@tonic-gate 		}
2807c478bd9Sstevel@tonic-gate 		goto done;
2817c478bd9Sstevel@tonic-gate 	}
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate 	assert (*listpp != NULL);
2847c478bd9Sstevel@tonic-gate 	tail = getlastconfig(*listpp);
2857c478bd9Sstevel@tonic-gate 
286*23a1cceaSRoger A. Faulkner 	while (gataline(fp, NFSL_CONFIG_FILE_PATH, linebuf, sizeof (linebuf))) {
2877c478bd9Sstevel@tonic-gate 		if (linebuf[0] == '\0') {
2887c478bd9Sstevel@tonic-gate 			/*
2897c478bd9Sstevel@tonic-gate 			 * ignore lines that exceed max size
2907c478bd9Sstevel@tonic-gate 			 */
2917c478bd9Sstevel@tonic-gate 			continue;
2927c478bd9Sstevel@tonic-gate 		}
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 		if (error = get_info(linebuf, &tag, &defaultdir, &bufferpath,
2957c478bd9Sstevel@tonic-gate 		    &rpclogpath, &fhpath, &logpath, &logformat))
2967c478bd9Sstevel@tonic-gate 			break;
2977c478bd9Sstevel@tonic-gate 
2987c478bd9Sstevel@tonic-gate 		if (listp = findconfig(listpp, tag, B_FALSE, &tail)) {
2997c478bd9Sstevel@tonic-gate 			/*
3007c478bd9Sstevel@tonic-gate 			 * An entry with the same tag name exists,
3017c478bd9Sstevel@tonic-gate 			 * update the fields that changed.
3027c478bd9Sstevel@tonic-gate 			 */
3037c478bd9Sstevel@tonic-gate 			error = update_config(listp, tag, defaultdir,
3047c478bd9Sstevel@tonic-gate 					bufferpath, rpclogpath, fhpath, logpath,
3057c478bd9Sstevel@tonic-gate 					logformat, B_TRUE, B_TRUE);
3067c478bd9Sstevel@tonic-gate 			if (error)
3077c478bd9Sstevel@tonic-gate 				break;
3087c478bd9Sstevel@tonic-gate 		} else {
3097c478bd9Sstevel@tonic-gate 			/*
3107c478bd9Sstevel@tonic-gate 			 * New entry, create it.
3117c478bd9Sstevel@tonic-gate 			 */
3127c478bd9Sstevel@tonic-gate 			listp = create_config(tag, defaultdir,
3137c478bd9Sstevel@tonic-gate 					bufferpath, rpclogpath, fhpath,
3147c478bd9Sstevel@tonic-gate 					logpath, logformat, B_TRUE, &error);
3157c478bd9Sstevel@tonic-gate 			if (listp == NULL)
3167c478bd9Sstevel@tonic-gate 				break;
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate 			if (*listpp == NULL)
3197c478bd9Sstevel@tonic-gate 				*listpp = listp;
3207c478bd9Sstevel@tonic-gate 			else
3217c478bd9Sstevel@tonic-gate 				tail->nc_next = listp;
3227c478bd9Sstevel@tonic-gate 			tail = listp;
3237c478bd9Sstevel@tonic-gate 		}
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 		assert(global != NULL);
3267c478bd9Sstevel@tonic-gate 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	if (!error) {
3297c478bd9Sstevel@tonic-gate 		/*
3307c478bd9Sstevel@tonic-gate 		 * Get mtime while we have file locked
3317c478bd9Sstevel@tonic-gate 		 */
3327c478bd9Sstevel@tonic-gate 		if (error = fstat(fileno(fp), &st)) {
3337c478bd9Sstevel@tonic-gate 			error = errno;
3347c478bd9Sstevel@tonic-gate 			if (nfsl_errs_to_syslog) {
3357c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR, gettext(
3367c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH,
3377c478bd9Sstevel@tonic-gate 				strerror(error));
3387c478bd9Sstevel@tonic-gate 			} else {
3397c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
3407c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH,
3417c478bd9Sstevel@tonic-gate 				strerror(error));
3427c478bd9Sstevel@tonic-gate 			}
3437c478bd9Sstevel@tonic-gate 		}
3447c478bd9Sstevel@tonic-gate 		config_last_modification = st.st_mtim;
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 
3477c478bd9Sstevel@tonic-gate done:
3487c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
3497c478bd9Sstevel@tonic-gate 	return (error);
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate /*
3537c478bd9Sstevel@tonic-gate  * Creates the config structure with the values specified by the
3547c478bd9Sstevel@tonic-gate  * parameters. If defaultdir has been specified, all relative paths
3557c478bd9Sstevel@tonic-gate  * are prepended with this defaultdir.
3567c478bd9Sstevel@tonic-gate  * If 'complete' is set then this must represent a complete config entry
3577c478bd9Sstevel@tonic-gate  * as specified by is_complete_config(), otherwise no work is perfomed, and
3587c478bd9Sstevel@tonic-gate  * NULL is returned.
3597c478bd9Sstevel@tonic-gate  *
3607c478bd9Sstevel@tonic-gate  * Returns the newly created config structure on success.
3617c478bd9Sstevel@tonic-gate  * Returns NULL on failure and sets error to the appropriate error.
3627c478bd9Sstevel@tonic-gate  */
3637c478bd9Sstevel@tonic-gate static nfsl_config_t *
3647c478bd9Sstevel@tonic-gate create_config(
3657c478bd9Sstevel@tonic-gate 	char *tag,
3667c478bd9Sstevel@tonic-gate 	char *defaultdir,
3677c478bd9Sstevel@tonic-gate 	char *bufferpath,
3687c478bd9Sstevel@tonic-gate 	char *rpclogpath,
3697c478bd9Sstevel@tonic-gate 	char *fhpath,
3707c478bd9Sstevel@tonic-gate 	char *logpath,
3717c478bd9Sstevel@tonic-gate 	int   logformat,
3727c478bd9Sstevel@tonic-gate 	boolean_t complete,
3737c478bd9Sstevel@tonic-gate 	int  *error)
3747c478bd9Sstevel@tonic-gate {
3757c478bd9Sstevel@tonic-gate 	nfsl_config_t *config;
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	if ((config = (nfsl_config_t *)malloc(sizeof (*config))) == NULL) {
3787c478bd9Sstevel@tonic-gate 		*error = ENOMEM;
3797c478bd9Sstevel@tonic-gate 		return (NULL);
3807c478bd9Sstevel@tonic-gate 	}
3817c478bd9Sstevel@tonic-gate 	(void) memset((void *)config, 0, sizeof (*config));
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate 	*error = update_config(config, tag, defaultdir, bufferpath, rpclogpath,
3847c478bd9Sstevel@tonic-gate 			fhpath, logpath, logformat, complete, B_TRUE);
3857c478bd9Sstevel@tonic-gate 	if (*error) {
3867c478bd9Sstevel@tonic-gate 		free(config);
3877c478bd9Sstevel@tonic-gate 		return (NULL);
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	config->nc_flags &= ~NC_UPDATED;	/* This is a new entry */
3917c478bd9Sstevel@tonic-gate 
3927c478bd9Sstevel@tonic-gate 	return (config);
3937c478bd9Sstevel@tonic-gate }
3947c478bd9Sstevel@tonic-gate 
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /*
3977c478bd9Sstevel@tonic-gate  * Updates the configuration entry with the new information provided,
3987c478bd9Sstevel@tonic-gate  * sets NC_UPDATED to indicate so. The entry is left untouched if all
3997c478bd9Sstevel@tonic-gate  * the fields are the same (except for 'nc_rpccookie', 'nc_transcookie'
4007c478bd9Sstevel@tonic-gate  * and 'nc_next').
4017c478bd9Sstevel@tonic-gate  * Prepends each path component with 'defauldir' if 'prepend' is set.
4027c478bd9Sstevel@tonic-gate  *
4037c478bd9Sstevel@tonic-gate  * Returns 0 on success, error otherwise.
4047c478bd9Sstevel@tonic-gate  * On error, the config entry is left in an inconsistent state.
4057c478bd9Sstevel@tonic-gate  * The only thing the caller can really do with it is free it.
4067c478bd9Sstevel@tonic-gate  */
4077c478bd9Sstevel@tonic-gate static int
4087c478bd9Sstevel@tonic-gate update_config(
4097c478bd9Sstevel@tonic-gate 	nfsl_config_t *config,
4107c478bd9Sstevel@tonic-gate 	char *tag,
4117c478bd9Sstevel@tonic-gate 	char *defaultdir,
4127c478bd9Sstevel@tonic-gate 	char *bufferpath,
4137c478bd9Sstevel@tonic-gate 	char *rpclogpath,
4147c478bd9Sstevel@tonic-gate 	char *fhpath,
4157c478bd9Sstevel@tonic-gate 	char *logpath,
4167c478bd9Sstevel@tonic-gate 	int   logformat,
4177c478bd9Sstevel@tonic-gate 	boolean_t complete,
4187c478bd9Sstevel@tonic-gate 	boolean_t prepend)
4197c478bd9Sstevel@tonic-gate {
4207c478bd9Sstevel@tonic-gate 	boolean_t updated, config_updated = B_FALSE;
4217c478bd9Sstevel@tonic-gate 	int error = 0;
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	if (complete && !is_complete_config(tag, bufferpath, fhpath, logpath)) {
4247c478bd9Sstevel@tonic-gate 		/*
4257c478bd9Sstevel@tonic-gate 		 * Not a complete entry
4267c478bd9Sstevel@tonic-gate 		 */
4277c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
4287c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
4297c478bd9Sstevel@tonic-gate 			"update_config: \"%s\" not a complete config entry."),
4307c478bd9Sstevel@tonic-gate 			tag);
4317c478bd9Sstevel@tonic-gate 		} else {
4327c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
4337c478bd9Sstevel@tonic-gate 			"update_config: \"%s\" not a complete config entry.\n"),
4347c478bd9Sstevel@tonic-gate 			tag);
4357c478bd9Sstevel@tonic-gate 		}
4367c478bd9Sstevel@tonic-gate 		return (EINVAL);
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	assert(tag != NULL);
4407c478bd9Sstevel@tonic-gate 	if (config->nc_name == NULL) {
4417c478bd9Sstevel@tonic-gate 		/*
4427c478bd9Sstevel@tonic-gate 		 * New entry
4437c478bd9Sstevel@tonic-gate 		 */
4447c478bd9Sstevel@tonic-gate 		if ((config->nc_name = strdup(tag)) == NULL) {
4457c478bd9Sstevel@tonic-gate 			error = ENOMEM;
4467c478bd9Sstevel@tonic-gate 			goto errout;
4477c478bd9Sstevel@tonic-gate 		}
4487c478bd9Sstevel@tonic-gate 	} else
4497c478bd9Sstevel@tonic-gate 		assert(strcmp(config->nc_name, tag) == 0);
4507c478bd9Sstevel@tonic-gate 
4517c478bd9Sstevel@tonic-gate 	if (error = update_field(
4527c478bd9Sstevel@tonic-gate 	    &config->nc_defaultdir, defaultdir, NULL, &updated))
4537c478bd9Sstevel@tonic-gate 		goto errout;
4547c478bd9Sstevel@tonic-gate 	if (!prepend) {
4557c478bd9Sstevel@tonic-gate 		/*
4567c478bd9Sstevel@tonic-gate 		 * Do not prepend default directory.
4577c478bd9Sstevel@tonic-gate 		 */
4587c478bd9Sstevel@tonic-gate 		defaultdir = NULL;
4597c478bd9Sstevel@tonic-gate 	}
4607c478bd9Sstevel@tonic-gate 	config_updated |= updated;
4617c478bd9Sstevel@tonic-gate 	if (error = update_field(
4627c478bd9Sstevel@tonic-gate 	    &config->nc_bufferpath, bufferpath, defaultdir, &updated))
4637c478bd9Sstevel@tonic-gate 		goto errout;
4647c478bd9Sstevel@tonic-gate 	config_updated |= updated;
4657c478bd9Sstevel@tonic-gate 	if (error = update_field(
4667c478bd9Sstevel@tonic-gate 	    &config->nc_rpclogpath, rpclogpath, defaultdir, &updated))
4677c478bd9Sstevel@tonic-gate 		goto errout;
4687c478bd9Sstevel@tonic-gate 	config_updated |= updated;
4697c478bd9Sstevel@tonic-gate 	if (error = update_field(
4707c478bd9Sstevel@tonic-gate 	    &config->nc_fhpath, fhpath, defaultdir, &updated))
4717c478bd9Sstevel@tonic-gate 		goto errout;
4727c478bd9Sstevel@tonic-gate 	config_updated |= updated;
4737c478bd9Sstevel@tonic-gate 	if (error = update_field(
4747c478bd9Sstevel@tonic-gate 	    &config->nc_logpath, logpath, defaultdir, &updated))
4757c478bd9Sstevel@tonic-gate 		goto errout;
4767c478bd9Sstevel@tonic-gate 	config_updated |= updated;
4777c478bd9Sstevel@tonic-gate 	updated = (config->nc_logformat != logformat);
4787c478bd9Sstevel@tonic-gate 	if (updated)
4797c478bd9Sstevel@tonic-gate 		config->nc_logformat = logformat;
4807c478bd9Sstevel@tonic-gate 	config_updated |= updated;
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	if (config_updated)
4837c478bd9Sstevel@tonic-gate 		config->nc_flags |= NC_UPDATED;
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 	if (strcmp(tag, DEFAULTTAG) == 0) {
4867c478bd9Sstevel@tonic-gate 		/*
4877c478bd9Sstevel@tonic-gate 		 * Have the default global config point to this entry.
4887c478bd9Sstevel@tonic-gate 		 */
4897c478bd9Sstevel@tonic-gate 		global = config;
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 		/*
4927c478bd9Sstevel@tonic-gate 		 * Update the global_raw configuration entry.
4937c478bd9Sstevel@tonic-gate 		 * Make sure no expanding of paths occurs.
4947c478bd9Sstevel@tonic-gate 		 */
4957c478bd9Sstevel@tonic-gate 		if (error = update_config(global_raw, DEFAULTRAWTAG, defaultdir,
4967c478bd9Sstevel@tonic-gate 			bufferpath, rpclogpath, fhpath, logpath, logformat,
4977c478bd9Sstevel@tonic-gate 			complete, B_FALSE))
4987c478bd9Sstevel@tonic-gate 				goto errout;
4997c478bd9Sstevel@tonic-gate 	}
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	return (error);
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate errout:
5047c478bd9Sstevel@tonic-gate 	if (nfsl_errs_to_syslog) {
5057c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, gettext(
5067c478bd9Sstevel@tonic-gate 			"update_config: Can't process \"%s\" config entry: %s"),
5077c478bd9Sstevel@tonic-gate 			tag, strerror(error));
5087c478bd9Sstevel@tonic-gate 	} else {
5097c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
5107c478bd9Sstevel@tonic-gate 		"update_config: Can't process \"%s\" config entry: %s\n"),
5117c478bd9Sstevel@tonic-gate 		tag, strerror(error));
5127c478bd9Sstevel@tonic-gate 	}
5137c478bd9Sstevel@tonic-gate 	return (error);
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate /*
5177c478bd9Sstevel@tonic-gate  * Prepends 'prependir' to 'new' if 'prependir' is defined.
5187c478bd9Sstevel@tonic-gate  * Compares the value of '*old' with 'new', if it has changed,
5197c478bd9Sstevel@tonic-gate  * then sets whatever 'old' references equal to 'new'.
5207c478bd9Sstevel@tonic-gate  * Returns 0 on success, error otherwise.
5217c478bd9Sstevel@tonic-gate  * Sets '*updated' to B_TRUE if field was modified.
5227c478bd9Sstevel@tonic-gate  * The value of '*updated' is undefined on error.
5237c478bd9Sstevel@tonic-gate  */
5247c478bd9Sstevel@tonic-gate static int
5257c478bd9Sstevel@tonic-gate update_field(
5267c478bd9Sstevel@tonic-gate 	char **old,		/* pointer to config field */
5277c478bd9Sstevel@tonic-gate 	char *new,		/* updated value */
5287c478bd9Sstevel@tonic-gate 	char *prependdir,	/* prepend this directory to new */
5297c478bd9Sstevel@tonic-gate 	boolean_t *updated)	/* field was modified */
5307c478bd9Sstevel@tonic-gate {
5317c478bd9Sstevel@tonic-gate 	char *tmp_new = NULL;
5327c478bd9Sstevel@tonic-gate 	int need_update = 0;
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	if (new != NULL) {
5357c478bd9Sstevel@tonic-gate 		if (prependdir != NULL && new[0] != '/') {
5367c478bd9Sstevel@tonic-gate 			tmp_new = malloc(strlen(prependdir) + strlen(new) + 2);
5377c478bd9Sstevel@tonic-gate 			if (tmp_new == NULL)
5387c478bd9Sstevel@tonic-gate 				return (ENOMEM);
5397c478bd9Sstevel@tonic-gate 			(void) sprintf(tmp_new, "%s/%s", prependdir, new);
5407c478bd9Sstevel@tonic-gate 		} else {
5417c478bd9Sstevel@tonic-gate 			if ((tmp_new = strdup(new)) == NULL)
5427c478bd9Sstevel@tonic-gate 				return (ENOMEM);
5437c478bd9Sstevel@tonic-gate 		}
5447c478bd9Sstevel@tonic-gate 	}
5457c478bd9Sstevel@tonic-gate 
5467c478bd9Sstevel@tonic-gate 	if (tmp_new != NULL) {
5477c478bd9Sstevel@tonic-gate 		if (*old == NULL)
5487c478bd9Sstevel@tonic-gate 			need_update++;
5497c478bd9Sstevel@tonic-gate 		else if (strcmp(tmp_new, *old) != 0) {
5507c478bd9Sstevel@tonic-gate 			free(*old);
5517c478bd9Sstevel@tonic-gate 			need_update++;
5527c478bd9Sstevel@tonic-gate 		}
5537c478bd9Sstevel@tonic-gate 		if (need_update)
5547c478bd9Sstevel@tonic-gate 			*old = tmp_new;
5557c478bd9Sstevel@tonic-gate 	} else if (*old != NULL) {
5567c478bd9Sstevel@tonic-gate 		need_update++;
5577c478bd9Sstevel@tonic-gate 		free(*old);
5587c478bd9Sstevel@tonic-gate 		*old = NULL;
5597c478bd9Sstevel@tonic-gate 	}
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 	*updated = need_update != 0;
5627c478bd9Sstevel@tonic-gate 	return (0);
5637c478bd9Sstevel@tonic-gate }
5647c478bd9Sstevel@tonic-gate 
5657c478bd9Sstevel@tonic-gate #ifdef DEBUG
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate  * Removes and frees the 'config' entry from the list
5687c478bd9Sstevel@tonic-gate  * pointed to by '*listpp'.
5697c478bd9Sstevel@tonic-gate  * No error is reported if the entry does not exist.
5707c478bd9Sstevel@tonic-gate  * Updates '*tail' to point to the last item in the list.
5717c478bd9Sstevel@tonic-gate  */
5727c478bd9Sstevel@tonic-gate static void
5737c478bd9Sstevel@tonic-gate remove_config(
5747c478bd9Sstevel@tonic-gate 	nfsl_config_t **listpp,
5757c478bd9Sstevel@tonic-gate 	nfsl_config_t *config,
5767c478bd9Sstevel@tonic-gate 	nfsl_config_t **tail)
5777c478bd9Sstevel@tonic-gate {
5787c478bd9Sstevel@tonic-gate 	nfsl_config_t *p, *prev;
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	prev = *listpp;
5817c478bd9Sstevel@tonic-gate 	for (p = *listpp; p != NULL; p = p->nc_next) {
5827c478bd9Sstevel@tonic-gate 		if (p == config) {
5837c478bd9Sstevel@tonic-gate 			if (p == prev) {
5847c478bd9Sstevel@tonic-gate 				/*
5857c478bd9Sstevel@tonic-gate 				 * first element of the list
5867c478bd9Sstevel@tonic-gate 				 */
5877c478bd9Sstevel@tonic-gate 				*listpp = prev->nc_next;
5887c478bd9Sstevel@tonic-gate 			} else
5897c478bd9Sstevel@tonic-gate 				prev->nc_next = p->nc_next;
5907c478bd9Sstevel@tonic-gate 			free_config(p);
5917c478bd9Sstevel@tonic-gate 			break;
5927c478bd9Sstevel@tonic-gate 		}
5937c478bd9Sstevel@tonic-gate 		prev = p;
5947c478bd9Sstevel@tonic-gate 	}
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	/*
5977c478bd9Sstevel@tonic-gate 	 * Find tail of the list.
5987c478bd9Sstevel@tonic-gate 	 */
5997c478bd9Sstevel@tonic-gate 	for (*tail = prev; (*tail)->nc_next != NULL; *tail = (*tail)->nc_next)
6007c478bd9Sstevel@tonic-gate 		;
6017c478bd9Sstevel@tonic-gate }
6027c478bd9Sstevel@tonic-gate #endif /* DEBUG */
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate static void
6057c478bd9Sstevel@tonic-gate free_config(nfsl_config_t *config)
6067c478bd9Sstevel@tonic-gate {
6077c478bd9Sstevel@tonic-gate 	if (config == NULL)
6087c478bd9Sstevel@tonic-gate 		return;
6097c478bd9Sstevel@tonic-gate 	if (config->nc_name)
6107c478bd9Sstevel@tonic-gate 		free(config->nc_name);
6117c478bd9Sstevel@tonic-gate 	if (config->nc_defaultdir)
6127c478bd9Sstevel@tonic-gate 		free(config->nc_defaultdir);
6137c478bd9Sstevel@tonic-gate 	if (config->nc_bufferpath)
6147c478bd9Sstevel@tonic-gate 		free(config->nc_bufferpath);
6157c478bd9Sstevel@tonic-gate 	if (config->nc_rpclogpath)
6167c478bd9Sstevel@tonic-gate 		free(config->nc_rpclogpath);
6177c478bd9Sstevel@tonic-gate 	if (config->nc_fhpath)
6187c478bd9Sstevel@tonic-gate 		free(config->nc_fhpath);
6197c478bd9Sstevel@tonic-gate 	if (config->nc_logpath)
6207c478bd9Sstevel@tonic-gate 		free(config->nc_logpath);
6217c478bd9Sstevel@tonic-gate 	if (config == global)
6227c478bd9Sstevel@tonic-gate 		global = NULL;
6237c478bd9Sstevel@tonic-gate 	if (config == global_raw)
6247c478bd9Sstevel@tonic-gate 		global_raw = NULL;
6257c478bd9Sstevel@tonic-gate 	free(config);
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate void
6297c478bd9Sstevel@tonic-gate nfsl_freeconfig_list(nfsl_config_t **listpp)
6307c478bd9Sstevel@tonic-gate {
6317c478bd9Sstevel@tonic-gate 	nfsl_config_t *next;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	if (*listpp == NULL)
6347c478bd9Sstevel@tonic-gate 		return;
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate 	do {
6377c478bd9Sstevel@tonic-gate 		next = (*listpp)->nc_next;
6387c478bd9Sstevel@tonic-gate 		free_config(*listpp);
6397c478bd9Sstevel@tonic-gate 		*listpp = next;
6407c478bd9Sstevel@tonic-gate 	} while (*listpp);
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 	free_config(global_raw);
6437c478bd9Sstevel@tonic-gate }
6447c478bd9Sstevel@tonic-gate 
6457c478bd9Sstevel@tonic-gate /*
6467c478bd9Sstevel@tonic-gate  * Returns a pointer to the first instance of 'tag' in the list.
6477c478bd9Sstevel@tonic-gate  * If 'remove' is true, then the entry is removed from the list and
6487c478bd9Sstevel@tonic-gate  * a pointer to it is returned.
6497c478bd9Sstevel@tonic-gate  * If '*tail' is not NULL, then it will point to the last element of
6507c478bd9Sstevel@tonic-gate  * the list. Note that this function assumes that *tail already
6517c478bd9Sstevel@tonic-gate  * points at the last element of the list.
6527c478bd9Sstevel@tonic-gate  * Returns NULL if the entry does not exist.
6537c478bd9Sstevel@tonic-gate  */
6547c478bd9Sstevel@tonic-gate static nfsl_config_t *
6557c478bd9Sstevel@tonic-gate findconfig(
6567c478bd9Sstevel@tonic-gate 	nfsl_config_t **listpp,
6577c478bd9Sstevel@tonic-gate 	char *tag, boolean_t remove,
6587c478bd9Sstevel@tonic-gate 	nfsl_config_t **tail)
6597c478bd9Sstevel@tonic-gate {
6607c478bd9Sstevel@tonic-gate 	nfsl_config_t *p, *prev;
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	prev = *listpp;
6637c478bd9Sstevel@tonic-gate 	for (p = *listpp; p != NULL; p = p->nc_next) {
6647c478bd9Sstevel@tonic-gate 		if (strcmp(p->nc_name, tag) == 0) {
6657c478bd9Sstevel@tonic-gate 			if (remove) {
6667c478bd9Sstevel@tonic-gate 				if (p == prev) {
6677c478bd9Sstevel@tonic-gate 					/*
6687c478bd9Sstevel@tonic-gate 					 * first element of the list
6697c478bd9Sstevel@tonic-gate 					 */
6707c478bd9Sstevel@tonic-gate 					*listpp = prev->nc_next;
6717c478bd9Sstevel@tonic-gate 				} else
6727c478bd9Sstevel@tonic-gate 					prev->nc_next = p->nc_next;
6737c478bd9Sstevel@tonic-gate 
6747c478bd9Sstevel@tonic-gate 				if (tail != NULL && p == *tail) {
6757c478bd9Sstevel@tonic-gate 					/*
6767c478bd9Sstevel@tonic-gate 					 * Only update *tail if we removed
6777c478bd9Sstevel@tonic-gate 					 * the last element of the list, and we
6787c478bd9Sstevel@tonic-gate 					 * requested *tail to be updated.
6797c478bd9Sstevel@tonic-gate 					 */
6807c478bd9Sstevel@tonic-gate 					*tail = prev;
6817c478bd9Sstevel@tonic-gate 				}
6827c478bd9Sstevel@tonic-gate 			}
6837c478bd9Sstevel@tonic-gate 			return (p);
6847c478bd9Sstevel@tonic-gate 		}
6857c478bd9Sstevel@tonic-gate 		prev = p;
6867c478bd9Sstevel@tonic-gate 	}
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	return (NULL);
6897c478bd9Sstevel@tonic-gate }
6907c478bd9Sstevel@tonic-gate 
6917c478bd9Sstevel@tonic-gate static nfsl_config_t *
6927c478bd9Sstevel@tonic-gate getlastconfig(nfsl_config_t *listp)
6937c478bd9Sstevel@tonic-gate {
6947c478bd9Sstevel@tonic-gate 	nfsl_config_t *lastp = NULL;
6957c478bd9Sstevel@tonic-gate 
6967c478bd9Sstevel@tonic-gate 	for (; listp != NULL; listp = listp->nc_next)
6977c478bd9Sstevel@tonic-gate 		lastp = listp;
6987c478bd9Sstevel@tonic-gate 
6997c478bd9Sstevel@tonic-gate 	return (lastp);
7007c478bd9Sstevel@tonic-gate }
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate /*
7037c478bd9Sstevel@tonic-gate  * Returns a pointer to the first instance of 'tag' in the list.
7047c478bd9Sstevel@tonic-gate  * Returns NULL if the entry does not exist.
7057c478bd9Sstevel@tonic-gate  * Sets 'error' if the update of the list failed if necessary, and
7067c478bd9Sstevel@tonic-gate  * returns NULL.
7077c478bd9Sstevel@tonic-gate  */
7087c478bd9Sstevel@tonic-gate nfsl_config_t *
7097c478bd9Sstevel@tonic-gate nfsl_findconfig(nfsl_config_t *listp, char *tag, int *error)
7107c478bd9Sstevel@tonic-gate {
7117c478bd9Sstevel@tonic-gate 	nfsl_config_t *config;
7127c478bd9Sstevel@tonic-gate 	boolean_t updated;
7137c478bd9Sstevel@tonic-gate 
7147c478bd9Sstevel@tonic-gate 	*error = 0;
7157c478bd9Sstevel@tonic-gate 	config = findconfig(&listp, tag, B_FALSE, (nfsl_config_t **)NULL);
7167c478bd9Sstevel@tonic-gate 	if (config == NULL) {
7177c478bd9Sstevel@tonic-gate 		/*
7187c478bd9Sstevel@tonic-gate 		 * Rebuild our list if the file has changed.
7197c478bd9Sstevel@tonic-gate 		 */
7207c478bd9Sstevel@tonic-gate 		if (*error = nfsl_checkconfig_list(&listp, &updated)) {
7217c478bd9Sstevel@tonic-gate 			/*
7227c478bd9Sstevel@tonic-gate 			 * List may be corrupted, notify caller.
7237c478bd9Sstevel@tonic-gate 			 */
7247c478bd9Sstevel@tonic-gate 			return (NULL);
7257c478bd9Sstevel@tonic-gate 		}
7267c478bd9Sstevel@tonic-gate 		if (updated) {
7277c478bd9Sstevel@tonic-gate 			/*
7287c478bd9Sstevel@tonic-gate 			 * Search for tag again.
7297c478bd9Sstevel@tonic-gate 			 */
7307c478bd9Sstevel@tonic-gate 			config = findconfig(&listp, tag, B_FALSE,
7317c478bd9Sstevel@tonic-gate 				(nfsl_config_t **)NULL);
7327c478bd9Sstevel@tonic-gate 		}
7337c478bd9Sstevel@tonic-gate 	}
7347c478bd9Sstevel@tonic-gate 
7357c478bd9Sstevel@tonic-gate 	return (config);
7367c478bd9Sstevel@tonic-gate }
7377c478bd9Sstevel@tonic-gate 
7387c478bd9Sstevel@tonic-gate /*
7397c478bd9Sstevel@tonic-gate  * Use the raw global values if any of the parameters is not defined.
7407c478bd9Sstevel@tonic-gate  */
7417c478bd9Sstevel@tonic-gate static void
7427c478bd9Sstevel@tonic-gate complete_with_global(
7437c478bd9Sstevel@tonic-gate 	char **defaultdir,
7447c478bd9Sstevel@tonic-gate 	char **bufferpath,
7457c478bd9Sstevel@tonic-gate 	char **rpclogpath,
7467c478bd9Sstevel@tonic-gate 	char **fhpath,
7477c478bd9Sstevel@tonic-gate 	char **logpath,
7487c478bd9Sstevel@tonic-gate 	int  *logformat)
7497c478bd9Sstevel@tonic-gate {
7507c478bd9Sstevel@tonic-gate 	if (*defaultdir == NULL)
7517c478bd9Sstevel@tonic-gate 		*defaultdir = global_raw->nc_defaultdir;
7527c478bd9Sstevel@tonic-gate 	if (*bufferpath == NULL)
7537c478bd9Sstevel@tonic-gate 		*bufferpath = global_raw->nc_bufferpath;
7547c478bd9Sstevel@tonic-gate 	if (*rpclogpath == NULL)
7557c478bd9Sstevel@tonic-gate 		*rpclogpath = global_raw->nc_rpclogpath;
7567c478bd9Sstevel@tonic-gate 	if (*fhpath == NULL)
7577c478bd9Sstevel@tonic-gate 		*fhpath = global_raw->nc_fhpath;
7587c478bd9Sstevel@tonic-gate 	if (*logpath == NULL)
7597c478bd9Sstevel@tonic-gate 		*logpath = global_raw->nc_logpath;
7607c478bd9Sstevel@tonic-gate 	if (*logformat == 0)
7617c478bd9Sstevel@tonic-gate 		*logformat = global_raw->nc_logformat;
7627c478bd9Sstevel@tonic-gate }
7637c478bd9Sstevel@tonic-gate 
7647c478bd9Sstevel@tonic-gate /*
7657c478bd9Sstevel@tonic-gate  * Parses 'linebuf'. Returns 0 if a valid tag is found, otherwise non-zero.
7667c478bd9Sstevel@tonic-gate  * Unknown tokens are silently ignored.
7677c478bd9Sstevel@tonic-gate  * It is the responsibility of the caller to make a copy of the non-NULL
7687c478bd9Sstevel@tonic-gate  * parameters if they need to be used before linebuf is freed.
7697c478bd9Sstevel@tonic-gate  */
7707c478bd9Sstevel@tonic-gate static int
7717c478bd9Sstevel@tonic-gate get_info(
7727c478bd9Sstevel@tonic-gate 	char *linebuf,
7737c478bd9Sstevel@tonic-gate 	char **tag,
7747c478bd9Sstevel@tonic-gate 	char **defaultdir,
7757c478bd9Sstevel@tonic-gate 	char **bufferpath,
7767c478bd9Sstevel@tonic-gate 	char **rpclogpath,
7777c478bd9Sstevel@tonic-gate 	char **fhpath,
7787c478bd9Sstevel@tonic-gate 	char **logpath,
7797c478bd9Sstevel@tonic-gate 	int  *logformat)
7807c478bd9Sstevel@tonic-gate {
7817c478bd9Sstevel@tonic-gate 	char *tok;
7827c478bd9Sstevel@tonic-gate 	char *tmp;
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	/* tag */
7857c478bd9Sstevel@tonic-gate 	*tag = NULL;
7867c478bd9Sstevel@tonic-gate 	tok = strtok(linebuf, whitespace);
7877c478bd9Sstevel@tonic-gate 	if (tok == NULL)
7887c478bd9Sstevel@tonic-gate 		goto badtag;
7897c478bd9Sstevel@tonic-gate 	if (!is_legal_tag(tok))
7907c478bd9Sstevel@tonic-gate 		goto badtag;
7917c478bd9Sstevel@tonic-gate 	*tag = tok;
7927c478bd9Sstevel@tonic-gate 
7937c478bd9Sstevel@tonic-gate 	*defaultdir = *bufferpath = *rpclogpath = NULL;
7947c478bd9Sstevel@tonic-gate 	*fhpath = *logpath = NULL;
7957c478bd9Sstevel@tonic-gate 	*logformat = 0;
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	while (tok = strtok(NULL, whitespace)) {
7987c478bd9Sstevel@tonic-gate 		if (strncmp(tok, "defaultdir=", strlen("defaultdir=")) == 0) {
7997c478bd9Sstevel@tonic-gate 			*defaultdir = tok + strlen("defaultdir=");
8007c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "buffer=", strlen("buffer=")) == 0) {
8017c478bd9Sstevel@tonic-gate 			*bufferpath = tok + strlen("buffer=");
8027c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "rpclog=", strlen("rpclog=")) == 0) {
8037c478bd9Sstevel@tonic-gate 			*rpclogpath = tok + strlen("rpclog=");
8047c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "fhtable=", strlen("fhtable=")) == 0) {
8057c478bd9Sstevel@tonic-gate 			*fhpath = tok + strlen("fhtable=");
8067c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "log=", strlen("log=")) == 0) {
8077c478bd9Sstevel@tonic-gate 			*logpath = tok + strlen("log=");
8087c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "logformat=",
8097c478bd9Sstevel@tonic-gate 				strlen("logformat=")) == 0) {
8107c478bd9Sstevel@tonic-gate 			tmp = tok + strlen("logformat=");
8117c478bd9Sstevel@tonic-gate 			if (strncmp(tmp, "extended", strlen("extended")) == 0) {
8127c478bd9Sstevel@tonic-gate 				*logformat = TRANSLOG_EXTENDED;
8137c478bd9Sstevel@tonic-gate 			} else {
8147c478bd9Sstevel@tonic-gate 				/*
8157c478bd9Sstevel@tonic-gate 				 * Use transaction log basic format if
8167c478bd9Sstevel@tonic-gate 				 * 'extended' was not specified.
8177c478bd9Sstevel@tonic-gate 				 */
8187c478bd9Sstevel@tonic-gate 				*logformat = TRANSLOG_BASIC;
8197c478bd9Sstevel@tonic-gate 			}
8207c478bd9Sstevel@tonic-gate 		}
8217c478bd9Sstevel@tonic-gate 	}
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 	if (strcmp(*tag, DEFAULTTAG) != 0) {
8247c478bd9Sstevel@tonic-gate 		/*
8257c478bd9Sstevel@tonic-gate 		 * Use global values for fields not specified if
8267c478bd9Sstevel@tonic-gate 		 * this tag is not the global tag.
8277c478bd9Sstevel@tonic-gate 		 */
8287c478bd9Sstevel@tonic-gate 		complete_with_global(defaultdir, bufferpath,
8297c478bd9Sstevel@tonic-gate 			rpclogpath, fhpath, logpath, logformat);
8307c478bd9Sstevel@tonic-gate 	}
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate 	return (0);
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate badtag:
8357c478bd9Sstevel@tonic-gate 	if (nfsl_errs_to_syslog) {
8367c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, gettext(
8377c478bd9Sstevel@tonic-gate 			"Bad tag found in config file."));
8387c478bd9Sstevel@tonic-gate 	} else {
8397c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
8407c478bd9Sstevel@tonic-gate 			"Bad tag found in config file.\n"));
8417c478bd9Sstevel@tonic-gate 	}
8427c478bd9Sstevel@tonic-gate 	return (-1);
8437c478bd9Sstevel@tonic-gate }
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate /*
8467c478bd9Sstevel@tonic-gate  * Returns True if we have all the elements of a complete configuration
8477c478bd9Sstevel@tonic-gate  * entry. A complete configuration has tag, bufferpath, fhpath and logpath
8487c478bd9Sstevel@tonic-gate  * defined to non-zero strings.
8497c478bd9Sstevel@tonic-gate  */
8507c478bd9Sstevel@tonic-gate static boolean_t
8517c478bd9Sstevel@tonic-gate is_complete_config(
8527c478bd9Sstevel@tonic-gate 	char *tag,
8537c478bd9Sstevel@tonic-gate 	char *bufferpath,
8547c478bd9Sstevel@tonic-gate 	char *fhpath,
8557c478bd9Sstevel@tonic-gate 	char *logpath)
8567c478bd9Sstevel@tonic-gate {
8577c478bd9Sstevel@tonic-gate 	assert(tag != NULL);
8587c478bd9Sstevel@tonic-gate 	assert(strlen(tag) > 0);
8597c478bd9Sstevel@tonic-gate 
8607c478bd9Sstevel@tonic-gate 	if ((bufferpath != NULL && strlen(bufferpath) > 0) &&
8617c478bd9Sstevel@tonic-gate 	    (fhpath != NULL && strlen(fhpath) > 0) &&
8627c478bd9Sstevel@tonic-gate 	    (logpath != NULL && strlen(logpath) > 0))
8637c478bd9Sstevel@tonic-gate 		return (B_TRUE);
8647c478bd9Sstevel@tonic-gate 	return (B_FALSE);
8657c478bd9Sstevel@tonic-gate }
8667c478bd9Sstevel@tonic-gate 
8677c478bd9Sstevel@tonic-gate #ifdef DEBUG
8687c478bd9Sstevel@tonic-gate /*
8697c478bd9Sstevel@tonic-gate  * Prints the configuration entry to stdout.
8707c478bd9Sstevel@tonic-gate  */
8717c478bd9Sstevel@tonic-gate void
8727c478bd9Sstevel@tonic-gate nfsl_printconfig(nfsl_config_t *config)
8737c478bd9Sstevel@tonic-gate {
8747c478bd9Sstevel@tonic-gate 	if (config->nc_name)
8757c478bd9Sstevel@tonic-gate 		(void) printf("tag=%s\t", config->nc_name);
8767c478bd9Sstevel@tonic-gate 	if (config->nc_defaultdir)
8777c478bd9Sstevel@tonic-gate 		(void) printf("defaultdir=%s\t", config->nc_defaultdir);
8787c478bd9Sstevel@tonic-gate 	if (config->nc_logpath)
8797c478bd9Sstevel@tonic-gate 		(void) printf("logpath=%s\t", config->nc_logpath);
8807c478bd9Sstevel@tonic-gate 	if (config->nc_fhpath)
8817c478bd9Sstevel@tonic-gate 		(void) printf("fhpath=%s\t", config->nc_fhpath);
8827c478bd9Sstevel@tonic-gate 	if (config->nc_bufferpath)
8837c478bd9Sstevel@tonic-gate 		(void) printf("bufpath=%s\t", config->nc_bufferpath);
8847c478bd9Sstevel@tonic-gate 	if (config->nc_rpclogpath)
8857c478bd9Sstevel@tonic-gate 		(void) printf("rpclogpath=%s\t", config->nc_rpclogpath);
8867c478bd9Sstevel@tonic-gate 	if (config->nc_logformat == TRANSLOG_BASIC)
8877c478bd9Sstevel@tonic-gate 		(void) printf("logformat=basic");
8887c478bd9Sstevel@tonic-gate 	else if (config->nc_logformat == TRANSLOG_EXTENDED)
8897c478bd9Sstevel@tonic-gate 		(void) printf("logformat=extended");
8907c478bd9Sstevel@tonic-gate 	else
8917c478bd9Sstevel@tonic-gate 		(void) printf("config->nc_logformat=UNKNOWN");
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	if (config->nc_flags & NC_UPDATED)
8947c478bd9Sstevel@tonic-gate 		(void) printf("\tflags=NC_UPDATED");
8957c478bd9Sstevel@tonic-gate 	(void) printf("\n");
8967c478bd9Sstevel@tonic-gate }
8977c478bd9Sstevel@tonic-gate 
8987c478bd9Sstevel@tonic-gate /*
8997c478bd9Sstevel@tonic-gate  * Prints the configuration list to stdout.
9007c478bd9Sstevel@tonic-gate  */
9017c478bd9Sstevel@tonic-gate void
9027c478bd9Sstevel@tonic-gate nfsl_printconfig_list(nfsl_config_t *listp)
9037c478bd9Sstevel@tonic-gate {
9047c478bd9Sstevel@tonic-gate 	for (; listp != NULL; listp = listp->nc_next) {
9057c478bd9Sstevel@tonic-gate 		nfsl_printconfig(listp);
9067c478bd9Sstevel@tonic-gate 		(void) printf("\n");
9077c478bd9Sstevel@tonic-gate 	}
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate #endif /* DEBUG */
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate /*
9127c478bd9Sstevel@tonic-gate  * Returns non-zero if the given string is allowable for a tag, zero if
9137c478bd9Sstevel@tonic-gate  * not.
9147c478bd9Sstevel@tonic-gate  */
9157c478bd9Sstevel@tonic-gate static int
9167c478bd9Sstevel@tonic-gate is_legal_tag(char *tag)
9177c478bd9Sstevel@tonic-gate {
9187c478bd9Sstevel@tonic-gate 	int i;
9197c478bd9Sstevel@tonic-gate 	int len;
9207c478bd9Sstevel@tonic-gate 
9217c478bd9Sstevel@tonic-gate 	if (tag == NULL)
9227c478bd9Sstevel@tonic-gate 		return (0);
9237c478bd9Sstevel@tonic-gate 	len = strlen(tag);
9247c478bd9Sstevel@tonic-gate 	if (len == 0)
9257c478bd9Sstevel@tonic-gate 		return (0);
9267c478bd9Sstevel@tonic-gate 
9277c478bd9Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
9287c478bd9Sstevel@tonic-gate 		char c;
9297c478bd9Sstevel@tonic-gate 
9307c478bd9Sstevel@tonic-gate 		c = tag[i];
9317c478bd9Sstevel@tonic-gate 		if (!(isalnum((unsigned char)c) || c == '_'))
9327c478bd9Sstevel@tonic-gate 			return (0);
9337c478bd9Sstevel@tonic-gate 	}
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	return (1);
9367c478bd9Sstevel@tonic-gate }
9377c478bd9Sstevel@tonic-gate 
9387c478bd9Sstevel@tonic-gate /*
939*23a1cceaSRoger A. Faulkner  * gataline attempts to get a line from the configuration file,
9407c478bd9Sstevel@tonic-gate  * upto LINESZ. A line in the file is a concatenation of lines if the
9417c478bd9Sstevel@tonic-gate  * continuation symbol '\' is used at the end of the line. Returns
9427c478bd9Sstevel@tonic-gate  * line on success, a NULL on EOF, and an empty string on lines > linesz.
9437c478bd9Sstevel@tonic-gate  */
9447c478bd9Sstevel@tonic-gate static char *
945*23a1cceaSRoger A. Faulkner gataline(FILE *fp, char *path, char *line, int linesz) {
9467c478bd9Sstevel@tonic-gate 	register char *p = line;
9477c478bd9Sstevel@tonic-gate 	register int len;
9487c478bd9Sstevel@tonic-gate 	int excess = 0;
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	*p = '\0';
9517c478bd9Sstevel@tonic-gate 
9527c478bd9Sstevel@tonic-gate 	for (;;) {
9537c478bd9Sstevel@tonic-gate 		if (fgets(p, linesz - (p-line), fp) == NULL) {
9547c478bd9Sstevel@tonic-gate 			return (*line ? line : NULL);   /* EOF */
9557c478bd9Sstevel@tonic-gate 		}
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 		len = strlen(line);
9587c478bd9Sstevel@tonic-gate 		if (len <= 0) {
9597c478bd9Sstevel@tonic-gate 			p = line;
9607c478bd9Sstevel@tonic-gate 			continue;
9617c478bd9Sstevel@tonic-gate 		}
9627c478bd9Sstevel@tonic-gate 		p = &line[len - 1];
9637c478bd9Sstevel@tonic-gate 
9647c478bd9Sstevel@tonic-gate 		/*
9657c478bd9Sstevel@tonic-gate 		 * Is input line too long?
9667c478bd9Sstevel@tonic-gate 		 */
9677c478bd9Sstevel@tonic-gate 		if (*p != '\n') {
9687c478bd9Sstevel@tonic-gate 			excess = 1;
9697c478bd9Sstevel@tonic-gate 			/*
9707c478bd9Sstevel@tonic-gate 			 * Perhaps last char read was '\'. Reinsert it
9717c478bd9Sstevel@tonic-gate 			 * into the stream to ease the parsing when we
9727c478bd9Sstevel@tonic-gate 			 * read the rest of the line to discard.
9737c478bd9Sstevel@tonic-gate 			 */
9747c478bd9Sstevel@tonic-gate 			(void) ungetc(*p, fp);
9757c478bd9Sstevel@tonic-gate 			break;
9767c478bd9Sstevel@tonic-gate 		}
9777c478bd9Sstevel@tonic-gate trim:
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate 		/* trim trailing white space */
9807c478bd9Sstevel@tonic-gate 		while (p >= line && isspace(*(uchar_t *)p))
9817c478bd9Sstevel@tonic-gate 		*p-- = '\0';
9827c478bd9Sstevel@tonic-gate 		if (p < line) {			/* empty line */
9837c478bd9Sstevel@tonic-gate 			p = line;
9847c478bd9Sstevel@tonic-gate 			continue;
9857c478bd9Sstevel@tonic-gate 		}
9867c478bd9Sstevel@tonic-gate 
9877c478bd9Sstevel@tonic-gate 		if (*p == '\\') {		/* continuation */
9887c478bd9Sstevel@tonic-gate 			*p = '\0';
9897c478bd9Sstevel@tonic-gate 			continue;
9907c478bd9Sstevel@tonic-gate 		}
9917c478bd9Sstevel@tonic-gate 
9927c478bd9Sstevel@tonic-gate 		/*
9937c478bd9Sstevel@tonic-gate 		 * Ignore comments. Comments start with '#'
9947c478bd9Sstevel@tonic-gate 		 * which must be preceded by a whitespace, unless
9957c478bd9Sstevel@tonic-gate 		 * '#' is the first character in the line.
9967c478bd9Sstevel@tonic-gate 		 */
9977c478bd9Sstevel@tonic-gate 		p = line;
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 		while (p = strchr(p, '#')) {
10007c478bd9Sstevel@tonic-gate 			if (p == line || isspace(*(p-1))) {
10017c478bd9Sstevel@tonic-gate 				*p-- = '\0';
10027c478bd9Sstevel@tonic-gate 				goto trim;
10037c478bd9Sstevel@tonic-gate 			}
10047c478bd9Sstevel@tonic-gate 			p++;
10057c478bd9Sstevel@tonic-gate 		}
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 		break;
10087c478bd9Sstevel@tonic-gate 	}
10097c478bd9Sstevel@tonic-gate 	if (excess) {
10107c478bd9Sstevel@tonic-gate 		int c;
10117c478bd9Sstevel@tonic-gate 
10127c478bd9Sstevel@tonic-gate 		/*
10137c478bd9Sstevel@tonic-gate 		 * discard rest of line and return an empty string.
10147c478bd9Sstevel@tonic-gate 		 * done to set the stream to the correct place when
10157c478bd9Sstevel@tonic-gate 		 * we are done with this line.
10167c478bd9Sstevel@tonic-gate 		 */
10177c478bd9Sstevel@tonic-gate 		while ((c = getc(fp)) != EOF) {
10187c478bd9Sstevel@tonic-gate 			*p = c;
10197c478bd9Sstevel@tonic-gate 			if (*p == '\n')		/* end of the long line */
10207c478bd9Sstevel@tonic-gate 				break;
10217c478bd9Sstevel@tonic-gate 			else if (*p == '\\') {		/* continuation */
10227c478bd9Sstevel@tonic-gate 				if (getc(fp) == EOF)	/* ignore next char */
10237c478bd9Sstevel@tonic-gate 					break;
10247c478bd9Sstevel@tonic-gate 			}
10257c478bd9Sstevel@tonic-gate 		}
10267c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
10277c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
10287c478bd9Sstevel@tonic-gate 				"%s: line too long - ignored (max %d chars)"),
10297c478bd9Sstevel@tonic-gate 				path, linesz-1);
10307c478bd9Sstevel@tonic-gate 		} else {
10317c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
10327c478bd9Sstevel@tonic-gate 				"%s: line too long - ignored (max %d chars)\n"),
10337c478bd9Sstevel@tonic-gate 				path, linesz-1);
10347c478bd9Sstevel@tonic-gate 		}
10357c478bd9Sstevel@tonic-gate 		*line = '\0';
10367c478bd9Sstevel@tonic-gate 	}
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	return (line);
10397c478bd9Sstevel@tonic-gate }
1040