1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1999 by Sun Microsystems, Inc.
24*7c478bd9Sstevel@tonic-gate  * All rights reserved.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <ctype.h>
30*7c478bd9Sstevel@tonic-gate #include <errno.h>
31*7c478bd9Sstevel@tonic-gate #include <locale.h>
32*7c478bd9Sstevel@tonic-gate #include <stdarg.h>
33*7c478bd9Sstevel@tonic-gate #include <stdio.h>
34*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
35*7c478bd9Sstevel@tonic-gate #include <strings.h>
36*7c478bd9Sstevel@tonic-gate #include <string.h>
37*7c478bd9Sstevel@tonic-gate #include <syslog.h>
38*7c478bd9Sstevel@tonic-gate #include <nfs/nfs.h>
39*7c478bd9Sstevel@tonic-gate #include <assert.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
42*7c478bd9Sstevel@tonic-gate #include <unistd.h>
43*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
44*7c478bd9Sstevel@tonic-gate #include "nfslog_config.h"
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate #define	ERROR_BUFSZ	100
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate /*
49*7c478bd9Sstevel@tonic-gate  * This flag controls where error messages go.
50*7c478bd9Sstevel@tonic-gate  * Zero means that messages go to stderr.
51*7c478bd9Sstevel@tonic-gate  * Non-zero means that messages go to syslog.
52*7c478bd9Sstevel@tonic-gate  */
53*7c478bd9Sstevel@tonic-gate boolean_t nfsl_errs_to_syslog;
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate /*
56*7c478bd9Sstevel@tonic-gate  * Pointer to the global entry in the list
57*7c478bd9Sstevel@tonic-gate  */
58*7c478bd9Sstevel@tonic-gate static nfsl_config_t *global = NULL;
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate /*
61*7c478bd9Sstevel@tonic-gate  * Pointer to the raw global entry in the list, this is the
62*7c478bd9Sstevel@tonic-gate  * global entry without the expanded paths. This is used to
63*7c478bd9Sstevel@tonic-gate  * complete configurations.
64*7c478bd9Sstevel@tonic-gate  */
65*7c478bd9Sstevel@tonic-gate static nfsl_config_t *global_raw = NULL;
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate /*
68*7c478bd9Sstevel@tonic-gate  * Last modification time to config file.
69*7c478bd9Sstevel@tonic-gate  */
70*7c478bd9Sstevel@tonic-gate static timestruc_t config_last_modification = { 0 };
71*7c478bd9Sstevel@tonic-gate 
72*7c478bd9Sstevel@tonic-gate /*
73*7c478bd9Sstevel@tonic-gate  * Whitespace characters to delimit fields in a line.
74*7c478bd9Sstevel@tonic-gate  */
75*7c478bd9Sstevel@tonic-gate static const char *whitespace = " \t";
76*7c478bd9Sstevel@tonic-gate 
77*7c478bd9Sstevel@tonic-gate static int getconfiglist(nfsl_config_t **, boolean_t);
78*7c478bd9Sstevel@tonic-gate static nfsl_config_t *create_config(char *, char *, char *, char *, char *,
79*7c478bd9Sstevel@tonic-gate 			char *, int, boolean_t, int *);
80*7c478bd9Sstevel@tonic-gate static nfsl_config_t *create_global_raw(int *);
81*7c478bd9Sstevel@tonic-gate static int update_config(nfsl_config_t *, char *, char *, char *,
82*7c478bd9Sstevel@tonic-gate 			char *, char *, char *, int, boolean_t, boolean_t);
83*7c478bd9Sstevel@tonic-gate static int update_field(char **, char *, char *, boolean_t *);
84*7c478bd9Sstevel@tonic-gate static nfsl_config_t *findconfig(nfsl_config_t **, char *, boolean_t,
85*7c478bd9Sstevel@tonic-gate 			nfsl_config_t **);
86*7c478bd9Sstevel@tonic-gate static nfsl_config_t *getlastconfig(nfsl_config_t *);
87*7c478bd9Sstevel@tonic-gate static void complete_with_global(char **, char **, char **, char **,
88*7c478bd9Sstevel@tonic-gate 			char **, int *);
89*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
90*7c478bd9Sstevel@tonic-gate static void remove_config(nfsl_config_t **, nfsl_config_t *, nfsl_config_t **);
91*7c478bd9Sstevel@tonic-gate void nfsl_printconfig(nfsl_config_t *);
92*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */
93*7c478bd9Sstevel@tonic-gate static char *getline(FILE *, char *, char *, int);
94*7c478bd9Sstevel@tonic-gate static int get_info(char *, char **, char **, char **, char **, char **,
95*7c478bd9Sstevel@tonic-gate 			char **, int *);
96*7c478bd9Sstevel@tonic-gate static void free_config(nfsl_config_t *);
97*7c478bd9Sstevel@tonic-gate static int is_legal_tag(char *);
98*7c478bd9Sstevel@tonic-gate static boolean_t is_complete_config(char *, char *, char *, char *);
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate /*
101*7c478bd9Sstevel@tonic-gate  * Read the configuration file and create a list of configuration
102*7c478bd9Sstevel@tonic-gate  * parameters.  Returns zero for success or an errno value.
103*7c478bd9Sstevel@tonic-gate  * The caller is responsible for freeing the returned configlist by calling
104*7c478bd9Sstevel@tonic-gate  * nfsl_freeconfig_list().
105*7c478bd9Sstevel@tonic-gate  *
106*7c478bd9Sstevel@tonic-gate  * If the configuration file does not exist, *listpp points to a config entry
107*7c478bd9Sstevel@tonic-gate  * containing the hardwired defaults.
108*7c478bd9Sstevel@tonic-gate  */
109*7c478bd9Sstevel@tonic-gate int
110*7c478bd9Sstevel@tonic-gate nfsl_getconfig_list(nfsl_config_t **listpp)
111*7c478bd9Sstevel@tonic-gate {
112*7c478bd9Sstevel@tonic-gate 	int error = 0;
113*7c478bd9Sstevel@tonic-gate 	char *locale;
114*7c478bd9Sstevel@tonic-gate 
115*7c478bd9Sstevel@tonic-gate 	/*
116*7c478bd9Sstevel@tonic-gate 	 * Set the locale correctly so that we can correctly identify
117*7c478bd9Sstevel@tonic-gate 	 * alphabetic characters.
118*7c478bd9Sstevel@tonic-gate 	 */
119*7c478bd9Sstevel@tonic-gate 	if ((locale = getenv("LC_ALL")) != NULL)
120*7c478bd9Sstevel@tonic-gate 		(void) setlocale(LC_ALL, locale);
121*7c478bd9Sstevel@tonic-gate 	else if ((locale = getenv("LC_CTYPE")) != NULL)
122*7c478bd9Sstevel@tonic-gate 		(void) setlocale(LC_CTYPE, locale);
123*7c478bd9Sstevel@tonic-gate 	else if ((locale = getenv("LANG")) != NULL)
124*7c478bd9Sstevel@tonic-gate 		(void) setlocale(LC_CTYPE, locale);
125*7c478bd9Sstevel@tonic-gate 
126*7c478bd9Sstevel@tonic-gate 	/*
127*7c478bd9Sstevel@tonic-gate 	 * Allocate 'global_raw' structure, its contents are
128*7c478bd9Sstevel@tonic-gate 	 * indirectly allocated by create_config().
129*7c478bd9Sstevel@tonic-gate 	 */
130*7c478bd9Sstevel@tonic-gate 	assert(global_raw == NULL);
131*7c478bd9Sstevel@tonic-gate 	global_raw = create_global_raw(&error);
132*7c478bd9Sstevel@tonic-gate 	if (global_raw == NULL)
133*7c478bd9Sstevel@tonic-gate 		return (error);
134*7c478bd9Sstevel@tonic-gate 
135*7c478bd9Sstevel@tonic-gate 	/*
136*7c478bd9Sstevel@tonic-gate 	 * Build global entry with hardwired defaults first.
137*7c478bd9Sstevel@tonic-gate 	 */
138*7c478bd9Sstevel@tonic-gate 	assert(global == NULL);
139*7c478bd9Sstevel@tonic-gate 	global = create_config(DEFAULTTAG, DEFAULTDIR, BUFFERPATH, NULL,
140*7c478bd9Sstevel@tonic-gate 			FHPATH, LOGPATH, TRANSLOG_BASIC, B_TRUE, &error);
141*7c478bd9Sstevel@tonic-gate 	*listpp = global;
142*7c478bd9Sstevel@tonic-gate 	if (global == NULL) {
143*7c478bd9Sstevel@tonic-gate 		free_config(global_raw);
144*7c478bd9Sstevel@tonic-gate 		return (error);
145*7c478bd9Sstevel@tonic-gate 	}
146*7c478bd9Sstevel@tonic-gate 
147*7c478bd9Sstevel@tonic-gate 	if (error = getconfiglist(listpp, B_FALSE))
148*7c478bd9Sstevel@tonic-gate 		nfsl_freeconfig_list(listpp);
149*7c478bd9Sstevel@tonic-gate 	else {
150*7c478bd9Sstevel@tonic-gate 		assert(global != NULL);
151*7c478bd9Sstevel@tonic-gate 		/*
152*7c478bd9Sstevel@tonic-gate 		 * The global entry was replaced with the one in the file,
153*7c478bd9Sstevel@tonic-gate 		 * clear the UPDATED flag
154*7c478bd9Sstevel@tonic-gate 		 */
155*7c478bd9Sstevel@tonic-gate 		global->nc_flags &= ~NC_UPDATED;
156*7c478bd9Sstevel@tonic-gate 	}
157*7c478bd9Sstevel@tonic-gate 	return (error);
158*7c478bd9Sstevel@tonic-gate }
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate /*
161*7c478bd9Sstevel@tonic-gate  * Allocates memory for the 'global_raw' structure.
162*7c478bd9Sstevel@tonic-gate  * The actual allocation of values for its components happens in
163*7c478bd9Sstevel@tonic-gate  * update_config().
164*7c478bd9Sstevel@tonic-gate  */
165*7c478bd9Sstevel@tonic-gate static nfsl_config_t *
166*7c478bd9Sstevel@tonic-gate create_global_raw(int *error)
167*7c478bd9Sstevel@tonic-gate {
168*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *p;
169*7c478bd9Sstevel@tonic-gate 
170*7c478bd9Sstevel@tonic-gate 	*error = 0;
171*7c478bd9Sstevel@tonic-gate 	if (p = (nfsl_config_t *)malloc(sizeof (*p)))
172*7c478bd9Sstevel@tonic-gate 		(void) memset((void *)p, 0, sizeof (*p));
173*7c478bd9Sstevel@tonic-gate 	else
174*7c478bd9Sstevel@tonic-gate 		*error = ENOMEM;
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate 	return (p);
177*7c478bd9Sstevel@tonic-gate }
178*7c478bd9Sstevel@tonic-gate 
179*7c478bd9Sstevel@tonic-gate /*
180*7c478bd9Sstevel@tonic-gate  * Checks if the the configuration file has been modified since we last
181*7c478bd9Sstevel@tonic-gate  * read it, if not simply returns, otherwise it re-reads it adding new
182*7c478bd9Sstevel@tonic-gate  * configuration entries. Note that existing entries that no longer
183*7c478bd9Sstevel@tonic-gate  * exist in the configuration file are not removed. Existing entries
184*7c478bd9Sstevel@tonic-gate  * that are modified in the configuration file are updated in the list
185*7c478bd9Sstevel@tonic-gate  * as well.
186*7c478bd9Sstevel@tonic-gate  * if 'updated' is defined then it is set to TRUE if the list was modified.
187*7c478bd9Sstevel@tonic-gate  *
188*7c478bd9Sstevel@tonic-gate  * Note that if an error occurs, the list may be corrupted.
189*7c478bd9Sstevel@tonic-gate  * It is the responsibility of the caller to free the list.
190*7c478bd9Sstevel@tonic-gate  * If the configuration file does not exist, we simply return the list
191*7c478bd9Sstevel@tonic-gate  * that we previously had, log a message and return success.
192*7c478bd9Sstevel@tonic-gate  */
193*7c478bd9Sstevel@tonic-gate int
194*7c478bd9Sstevel@tonic-gate nfsl_checkconfig_list(nfsl_config_t **listpp, boolean_t *updated)
195*7c478bd9Sstevel@tonic-gate {
196*7c478bd9Sstevel@tonic-gate 	struct stat st;
197*7c478bd9Sstevel@tonic-gate 	int error = 0;
198*7c478bd9Sstevel@tonic-gate 
199*7c478bd9Sstevel@tonic-gate 	if (updated != NULL)
200*7c478bd9Sstevel@tonic-gate 		*updated = B_FALSE;
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate 	if (stat(NFSL_CONFIG_FILE_PATH, &st) == -1) {
203*7c478bd9Sstevel@tonic-gate 		error = errno;
204*7c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
205*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
206*7c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH,
207*7c478bd9Sstevel@tonic-gate 				strerror(error));
208*7c478bd9Sstevel@tonic-gate 		} else {
209*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
210*7c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH,
211*7c478bd9Sstevel@tonic-gate 				strerror(error));
212*7c478bd9Sstevel@tonic-gate 		}
213*7c478bd9Sstevel@tonic-gate 		return (0);
214*7c478bd9Sstevel@tonic-gate 	}
215*7c478bd9Sstevel@tonic-gate 
216*7c478bd9Sstevel@tonic-gate 	if (config_last_modification.tv_sec == st.st_mtim.tv_sec &&
217*7c478bd9Sstevel@tonic-gate 	    config_last_modification.tv_nsec == st.st_mtim.tv_nsec)
218*7c478bd9Sstevel@tonic-gate 		return (0);
219*7c478bd9Sstevel@tonic-gate 
220*7c478bd9Sstevel@tonic-gate 	if (updated != NULL)
221*7c478bd9Sstevel@tonic-gate 		*updated = B_TRUE;
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	return (getconfiglist(listpp, B_TRUE));
224*7c478bd9Sstevel@tonic-gate }
225*7c478bd9Sstevel@tonic-gate 
226*7c478bd9Sstevel@tonic-gate /*
227*7c478bd9Sstevel@tonic-gate  * Does the real work. Reads the configuration file and creates the
228*7c478bd9Sstevel@tonic-gate  * list of entries. Assumes that *listpp contains at least one entry.
229*7c478bd9Sstevel@tonic-gate  * The caller is responsible for freeing any config entries added to
230*7c478bd9Sstevel@tonic-gate  * the list whether this routine returns an error or not.
231*7c478bd9Sstevel@tonic-gate  *
232*7c478bd9Sstevel@tonic-gate  * Returns 0 on success and updates the '*listpp' config list,
233*7c478bd9Sstevel@tonic-gate  * Returns non-zero error value otherwise.
234*7c478bd9Sstevel@tonic-gate  */
235*7c478bd9Sstevel@tonic-gate static int
236*7c478bd9Sstevel@tonic-gate getconfiglist(nfsl_config_t **listpp, boolean_t updating)
237*7c478bd9Sstevel@tonic-gate {
238*7c478bd9Sstevel@tonic-gate 	FILE *fp;
239*7c478bd9Sstevel@tonic-gate 	int error = 0;
240*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *listp = NULL, *tail = NULL;
241*7c478bd9Sstevel@tonic-gate 	char linebuf[MAX_LINESZ];
242*7c478bd9Sstevel@tonic-gate 	char errorbuf[ERROR_BUFSZ];
243*7c478bd9Sstevel@tonic-gate 	char *tag, *defaultdir, *bufferpath, *rpclogpath, *fhpath, *logpath;
244*7c478bd9Sstevel@tonic-gate 	int logformat;
245*7c478bd9Sstevel@tonic-gate 	flock_t flock;
246*7c478bd9Sstevel@tonic-gate 	struct stat st;
247*7c478bd9Sstevel@tonic-gate 
248*7c478bd9Sstevel@tonic-gate 	fp = fopen(NFSL_CONFIG_FILE_PATH, "r");
249*7c478bd9Sstevel@tonic-gate 	if (fp == NULL) {
250*7c478bd9Sstevel@tonic-gate 		if (updating) {
251*7c478bd9Sstevel@tonic-gate 			(void) sprintf(errorbuf, "Can't open %s",
252*7c478bd9Sstevel@tonic-gate 				NFSL_CONFIG_FILE_PATH);
253*7c478bd9Sstevel@tonic-gate 		} else {
254*7c478bd9Sstevel@tonic-gate 			(void) sprintf(errorbuf,
255*7c478bd9Sstevel@tonic-gate 				"Can't open %s - using hardwired defaults",
256*7c478bd9Sstevel@tonic-gate 				NFSL_CONFIG_FILE_PATH);
257*7c478bd9Sstevel@tonic-gate 		}
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate 		/*
260*7c478bd9Sstevel@tonic-gate 		 * Use hardwired config.
261*7c478bd9Sstevel@tonic-gate 		 */
262*7c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog)
263*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext("%s"), errorbuf);
264*7c478bd9Sstevel@tonic-gate 		else
265*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext("%s\n"), errorbuf);
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate 		return (0);
268*7c478bd9Sstevel@tonic-gate 	}
269*7c478bd9Sstevel@tonic-gate 
270*7c478bd9Sstevel@tonic-gate 	(void) memset((void *) &flock, 0, sizeof (flock));
271*7c478bd9Sstevel@tonic-gate 	flock.l_type = F_RDLCK;
272*7c478bd9Sstevel@tonic-gate 	if (fcntl(fileno(fp), F_SETLKW, &flock) == -1) {
273*7c478bd9Sstevel@tonic-gate 		error = errno;
274*7c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
275*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
276*7c478bd9Sstevel@tonic-gate 				"Can't lock %s - %s"), NFSL_CONFIG_FILE_PATH,
277*7c478bd9Sstevel@tonic-gate 				strerror(error));
278*7c478bd9Sstevel@tonic-gate 		} else {
279*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
280*7c478bd9Sstevel@tonic-gate 				"Can't lock %s - %s\n"), NFSL_CONFIG_FILE_PATH,
281*7c478bd9Sstevel@tonic-gate 				strerror(error));
282*7c478bd9Sstevel@tonic-gate 		}
283*7c478bd9Sstevel@tonic-gate 		goto done;
284*7c478bd9Sstevel@tonic-gate 	}
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate 	assert (*listpp != NULL);
287*7c478bd9Sstevel@tonic-gate 	tail = getlastconfig(*listpp);
288*7c478bd9Sstevel@tonic-gate 
289*7c478bd9Sstevel@tonic-gate 	while (getline(fp, NFSL_CONFIG_FILE_PATH, linebuf, sizeof (linebuf))) {
290*7c478bd9Sstevel@tonic-gate 		if (linebuf[0] == '\0') {
291*7c478bd9Sstevel@tonic-gate 			/*
292*7c478bd9Sstevel@tonic-gate 			 * ignore lines that exceed max size
293*7c478bd9Sstevel@tonic-gate 			 */
294*7c478bd9Sstevel@tonic-gate 			continue;
295*7c478bd9Sstevel@tonic-gate 		}
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 		if (error = get_info(linebuf, &tag, &defaultdir, &bufferpath,
298*7c478bd9Sstevel@tonic-gate 		    &rpclogpath, &fhpath, &logpath, &logformat))
299*7c478bd9Sstevel@tonic-gate 			break;
300*7c478bd9Sstevel@tonic-gate 
301*7c478bd9Sstevel@tonic-gate 		if (listp = findconfig(listpp, tag, B_FALSE, &tail)) {
302*7c478bd9Sstevel@tonic-gate 			/*
303*7c478bd9Sstevel@tonic-gate 			 * An entry with the same tag name exists,
304*7c478bd9Sstevel@tonic-gate 			 * update the fields that changed.
305*7c478bd9Sstevel@tonic-gate 			 */
306*7c478bd9Sstevel@tonic-gate 			error = update_config(listp, tag, defaultdir,
307*7c478bd9Sstevel@tonic-gate 					bufferpath, rpclogpath, fhpath, logpath,
308*7c478bd9Sstevel@tonic-gate 					logformat, B_TRUE, B_TRUE);
309*7c478bd9Sstevel@tonic-gate 			if (error)
310*7c478bd9Sstevel@tonic-gate 				break;
311*7c478bd9Sstevel@tonic-gate 		} else {
312*7c478bd9Sstevel@tonic-gate 			/*
313*7c478bd9Sstevel@tonic-gate 			 * New entry, create it.
314*7c478bd9Sstevel@tonic-gate 			 */
315*7c478bd9Sstevel@tonic-gate 			listp = create_config(tag, defaultdir,
316*7c478bd9Sstevel@tonic-gate 					bufferpath, rpclogpath, fhpath,
317*7c478bd9Sstevel@tonic-gate 					logpath, logformat, B_TRUE, &error);
318*7c478bd9Sstevel@tonic-gate 			if (listp == NULL)
319*7c478bd9Sstevel@tonic-gate 				break;
320*7c478bd9Sstevel@tonic-gate 
321*7c478bd9Sstevel@tonic-gate 			if (*listpp == NULL)
322*7c478bd9Sstevel@tonic-gate 				*listpp = listp;
323*7c478bd9Sstevel@tonic-gate 			else
324*7c478bd9Sstevel@tonic-gate 				tail->nc_next = listp;
325*7c478bd9Sstevel@tonic-gate 			tail = listp;
326*7c478bd9Sstevel@tonic-gate 		}
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 		assert(global != NULL);
329*7c478bd9Sstevel@tonic-gate 	}
330*7c478bd9Sstevel@tonic-gate 
331*7c478bd9Sstevel@tonic-gate 	if (!error) {
332*7c478bd9Sstevel@tonic-gate 		/*
333*7c478bd9Sstevel@tonic-gate 		 * Get mtime while we have file locked
334*7c478bd9Sstevel@tonic-gate 		 */
335*7c478bd9Sstevel@tonic-gate 		if (error = fstat(fileno(fp), &st)) {
336*7c478bd9Sstevel@tonic-gate 			error = errno;
337*7c478bd9Sstevel@tonic-gate 			if (nfsl_errs_to_syslog) {
338*7c478bd9Sstevel@tonic-gate 				syslog(LOG_ERR, gettext(
339*7c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH,
340*7c478bd9Sstevel@tonic-gate 				strerror(error));
341*7c478bd9Sstevel@tonic-gate 			} else {
342*7c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr, gettext(
343*7c478bd9Sstevel@tonic-gate 				"Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH,
344*7c478bd9Sstevel@tonic-gate 				strerror(error));
345*7c478bd9Sstevel@tonic-gate 			}
346*7c478bd9Sstevel@tonic-gate 		}
347*7c478bd9Sstevel@tonic-gate 		config_last_modification = st.st_mtim;
348*7c478bd9Sstevel@tonic-gate 	}
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate done:
351*7c478bd9Sstevel@tonic-gate 	(void) fclose(fp);
352*7c478bd9Sstevel@tonic-gate 	return (error);
353*7c478bd9Sstevel@tonic-gate }
354*7c478bd9Sstevel@tonic-gate 
355*7c478bd9Sstevel@tonic-gate /*
356*7c478bd9Sstevel@tonic-gate  * Creates the config structure with the values specified by the
357*7c478bd9Sstevel@tonic-gate  * parameters. If defaultdir has been specified, all relative paths
358*7c478bd9Sstevel@tonic-gate  * are prepended with this defaultdir.
359*7c478bd9Sstevel@tonic-gate  * If 'complete' is set then this must represent a complete config entry
360*7c478bd9Sstevel@tonic-gate  * as specified by is_complete_config(), otherwise no work is perfomed, and
361*7c478bd9Sstevel@tonic-gate  * NULL is returned.
362*7c478bd9Sstevel@tonic-gate  *
363*7c478bd9Sstevel@tonic-gate  * Returns the newly created config structure on success.
364*7c478bd9Sstevel@tonic-gate  * Returns NULL on failure and sets error to the appropriate error.
365*7c478bd9Sstevel@tonic-gate  */
366*7c478bd9Sstevel@tonic-gate static nfsl_config_t *
367*7c478bd9Sstevel@tonic-gate create_config(
368*7c478bd9Sstevel@tonic-gate 	char *tag,
369*7c478bd9Sstevel@tonic-gate 	char *defaultdir,
370*7c478bd9Sstevel@tonic-gate 	char *bufferpath,
371*7c478bd9Sstevel@tonic-gate 	char *rpclogpath,
372*7c478bd9Sstevel@tonic-gate 	char *fhpath,
373*7c478bd9Sstevel@tonic-gate 	char *logpath,
374*7c478bd9Sstevel@tonic-gate 	int   logformat,
375*7c478bd9Sstevel@tonic-gate 	boolean_t complete,
376*7c478bd9Sstevel@tonic-gate 	int  *error)
377*7c478bd9Sstevel@tonic-gate {
378*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *config;
379*7c478bd9Sstevel@tonic-gate 
380*7c478bd9Sstevel@tonic-gate 	if ((config = (nfsl_config_t *)malloc(sizeof (*config))) == NULL) {
381*7c478bd9Sstevel@tonic-gate 		*error = ENOMEM;
382*7c478bd9Sstevel@tonic-gate 		return (NULL);
383*7c478bd9Sstevel@tonic-gate 	}
384*7c478bd9Sstevel@tonic-gate 	(void) memset((void *)config, 0, sizeof (*config));
385*7c478bd9Sstevel@tonic-gate 
386*7c478bd9Sstevel@tonic-gate 	*error = update_config(config, tag, defaultdir, bufferpath, rpclogpath,
387*7c478bd9Sstevel@tonic-gate 			fhpath, logpath, logformat, complete, B_TRUE);
388*7c478bd9Sstevel@tonic-gate 	if (*error) {
389*7c478bd9Sstevel@tonic-gate 		free(config);
390*7c478bd9Sstevel@tonic-gate 		return (NULL);
391*7c478bd9Sstevel@tonic-gate 	}
392*7c478bd9Sstevel@tonic-gate 
393*7c478bd9Sstevel@tonic-gate 	config->nc_flags &= ~NC_UPDATED;	/* This is a new entry */
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate 	return (config);
396*7c478bd9Sstevel@tonic-gate }
397*7c478bd9Sstevel@tonic-gate 
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate /*
400*7c478bd9Sstevel@tonic-gate  * Updates the configuration entry with the new information provided,
401*7c478bd9Sstevel@tonic-gate  * sets NC_UPDATED to indicate so. The entry is left untouched if all
402*7c478bd9Sstevel@tonic-gate  * the fields are the same (except for 'nc_rpccookie', 'nc_transcookie'
403*7c478bd9Sstevel@tonic-gate  * and 'nc_next').
404*7c478bd9Sstevel@tonic-gate  * Prepends each path component with 'defauldir' if 'prepend' is set.
405*7c478bd9Sstevel@tonic-gate  *
406*7c478bd9Sstevel@tonic-gate  * Returns 0 on success, error otherwise.
407*7c478bd9Sstevel@tonic-gate  * On error, the config entry is left in an inconsistent state.
408*7c478bd9Sstevel@tonic-gate  * The only thing the caller can really do with it is free it.
409*7c478bd9Sstevel@tonic-gate  */
410*7c478bd9Sstevel@tonic-gate static int
411*7c478bd9Sstevel@tonic-gate update_config(
412*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *config,
413*7c478bd9Sstevel@tonic-gate 	char *tag,
414*7c478bd9Sstevel@tonic-gate 	char *defaultdir,
415*7c478bd9Sstevel@tonic-gate 	char *bufferpath,
416*7c478bd9Sstevel@tonic-gate 	char *rpclogpath,
417*7c478bd9Sstevel@tonic-gate 	char *fhpath,
418*7c478bd9Sstevel@tonic-gate 	char *logpath,
419*7c478bd9Sstevel@tonic-gate 	int   logformat,
420*7c478bd9Sstevel@tonic-gate 	boolean_t complete,
421*7c478bd9Sstevel@tonic-gate 	boolean_t prepend)
422*7c478bd9Sstevel@tonic-gate {
423*7c478bd9Sstevel@tonic-gate 	boolean_t updated, config_updated = B_FALSE;
424*7c478bd9Sstevel@tonic-gate 	int error = 0;
425*7c478bd9Sstevel@tonic-gate 
426*7c478bd9Sstevel@tonic-gate 	if (complete && !is_complete_config(tag, bufferpath, fhpath, logpath)) {
427*7c478bd9Sstevel@tonic-gate 		/*
428*7c478bd9Sstevel@tonic-gate 		 * Not a complete entry
429*7c478bd9Sstevel@tonic-gate 		 */
430*7c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
431*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
432*7c478bd9Sstevel@tonic-gate 			"update_config: \"%s\" not a complete config entry."),
433*7c478bd9Sstevel@tonic-gate 			tag);
434*7c478bd9Sstevel@tonic-gate 		} else {
435*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
436*7c478bd9Sstevel@tonic-gate 			"update_config: \"%s\" not a complete config entry.\n"),
437*7c478bd9Sstevel@tonic-gate 			tag);
438*7c478bd9Sstevel@tonic-gate 		}
439*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
440*7c478bd9Sstevel@tonic-gate 	}
441*7c478bd9Sstevel@tonic-gate 
442*7c478bd9Sstevel@tonic-gate 	assert(tag != NULL);
443*7c478bd9Sstevel@tonic-gate 	if (config->nc_name == NULL) {
444*7c478bd9Sstevel@tonic-gate 		/*
445*7c478bd9Sstevel@tonic-gate 		 * New entry
446*7c478bd9Sstevel@tonic-gate 		 */
447*7c478bd9Sstevel@tonic-gate 		if ((config->nc_name = strdup(tag)) == NULL) {
448*7c478bd9Sstevel@tonic-gate 			error = ENOMEM;
449*7c478bd9Sstevel@tonic-gate 			goto errout;
450*7c478bd9Sstevel@tonic-gate 		}
451*7c478bd9Sstevel@tonic-gate 	} else
452*7c478bd9Sstevel@tonic-gate 		assert(strcmp(config->nc_name, tag) == 0);
453*7c478bd9Sstevel@tonic-gate 
454*7c478bd9Sstevel@tonic-gate 	if (error = update_field(
455*7c478bd9Sstevel@tonic-gate 	    &config->nc_defaultdir, defaultdir, NULL, &updated))
456*7c478bd9Sstevel@tonic-gate 		goto errout;
457*7c478bd9Sstevel@tonic-gate 	if (!prepend) {
458*7c478bd9Sstevel@tonic-gate 		/*
459*7c478bd9Sstevel@tonic-gate 		 * Do not prepend default directory.
460*7c478bd9Sstevel@tonic-gate 		 */
461*7c478bd9Sstevel@tonic-gate 		defaultdir = NULL;
462*7c478bd9Sstevel@tonic-gate 	}
463*7c478bd9Sstevel@tonic-gate 	config_updated |= updated;
464*7c478bd9Sstevel@tonic-gate 	if (error = update_field(
465*7c478bd9Sstevel@tonic-gate 	    &config->nc_bufferpath, bufferpath, defaultdir, &updated))
466*7c478bd9Sstevel@tonic-gate 		goto errout;
467*7c478bd9Sstevel@tonic-gate 	config_updated |= updated;
468*7c478bd9Sstevel@tonic-gate 	if (error = update_field(
469*7c478bd9Sstevel@tonic-gate 	    &config->nc_rpclogpath, rpclogpath, defaultdir, &updated))
470*7c478bd9Sstevel@tonic-gate 		goto errout;
471*7c478bd9Sstevel@tonic-gate 	config_updated |= updated;
472*7c478bd9Sstevel@tonic-gate 	if (error = update_field(
473*7c478bd9Sstevel@tonic-gate 	    &config->nc_fhpath, fhpath, defaultdir, &updated))
474*7c478bd9Sstevel@tonic-gate 		goto errout;
475*7c478bd9Sstevel@tonic-gate 	config_updated |= updated;
476*7c478bd9Sstevel@tonic-gate 	if (error = update_field(
477*7c478bd9Sstevel@tonic-gate 	    &config->nc_logpath, logpath, defaultdir, &updated))
478*7c478bd9Sstevel@tonic-gate 		goto errout;
479*7c478bd9Sstevel@tonic-gate 	config_updated |= updated;
480*7c478bd9Sstevel@tonic-gate 	updated = (config->nc_logformat != logformat);
481*7c478bd9Sstevel@tonic-gate 	if (updated)
482*7c478bd9Sstevel@tonic-gate 		config->nc_logformat = logformat;
483*7c478bd9Sstevel@tonic-gate 	config_updated |= updated;
484*7c478bd9Sstevel@tonic-gate 
485*7c478bd9Sstevel@tonic-gate 	if (config_updated)
486*7c478bd9Sstevel@tonic-gate 		config->nc_flags |= NC_UPDATED;
487*7c478bd9Sstevel@tonic-gate 
488*7c478bd9Sstevel@tonic-gate 	if (strcmp(tag, DEFAULTTAG) == 0) {
489*7c478bd9Sstevel@tonic-gate 		/*
490*7c478bd9Sstevel@tonic-gate 		 * Have the default global config point to this entry.
491*7c478bd9Sstevel@tonic-gate 		 */
492*7c478bd9Sstevel@tonic-gate 		global = config;
493*7c478bd9Sstevel@tonic-gate 
494*7c478bd9Sstevel@tonic-gate 		/*
495*7c478bd9Sstevel@tonic-gate 		 * Update the global_raw configuration entry.
496*7c478bd9Sstevel@tonic-gate 		 * Make sure no expanding of paths occurs.
497*7c478bd9Sstevel@tonic-gate 		 */
498*7c478bd9Sstevel@tonic-gate 		if (error = update_config(global_raw, DEFAULTRAWTAG, defaultdir,
499*7c478bd9Sstevel@tonic-gate 			bufferpath, rpclogpath, fhpath, logpath, logformat,
500*7c478bd9Sstevel@tonic-gate 			complete, B_FALSE))
501*7c478bd9Sstevel@tonic-gate 				goto errout;
502*7c478bd9Sstevel@tonic-gate 	}
503*7c478bd9Sstevel@tonic-gate 
504*7c478bd9Sstevel@tonic-gate 	return (error);
505*7c478bd9Sstevel@tonic-gate 
506*7c478bd9Sstevel@tonic-gate errout:
507*7c478bd9Sstevel@tonic-gate 	if (nfsl_errs_to_syslog) {
508*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, gettext(
509*7c478bd9Sstevel@tonic-gate 			"update_config: Can't process \"%s\" config entry: %s"),
510*7c478bd9Sstevel@tonic-gate 			tag, strerror(error));
511*7c478bd9Sstevel@tonic-gate 	} else {
512*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
513*7c478bd9Sstevel@tonic-gate 		"update_config: Can't process \"%s\" config entry: %s\n"),
514*7c478bd9Sstevel@tonic-gate 		tag, strerror(error));
515*7c478bd9Sstevel@tonic-gate 	}
516*7c478bd9Sstevel@tonic-gate 	return (error);
517*7c478bd9Sstevel@tonic-gate }
518*7c478bd9Sstevel@tonic-gate 
519*7c478bd9Sstevel@tonic-gate /*
520*7c478bd9Sstevel@tonic-gate  * Prepends 'prependir' to 'new' if 'prependir' is defined.
521*7c478bd9Sstevel@tonic-gate  * Compares the value of '*old' with 'new', if it has changed,
522*7c478bd9Sstevel@tonic-gate  * then sets whatever 'old' references equal to 'new'.
523*7c478bd9Sstevel@tonic-gate  * Returns 0 on success, error otherwise.
524*7c478bd9Sstevel@tonic-gate  * Sets '*updated' to B_TRUE if field was modified.
525*7c478bd9Sstevel@tonic-gate  * The value of '*updated' is undefined on error.
526*7c478bd9Sstevel@tonic-gate  */
527*7c478bd9Sstevel@tonic-gate static int
528*7c478bd9Sstevel@tonic-gate update_field(
529*7c478bd9Sstevel@tonic-gate 	char **old,		/* pointer to config field */
530*7c478bd9Sstevel@tonic-gate 	char *new,		/* updated value */
531*7c478bd9Sstevel@tonic-gate 	char *prependdir,	/* prepend this directory to new */
532*7c478bd9Sstevel@tonic-gate 	boolean_t *updated)	/* field was modified */
533*7c478bd9Sstevel@tonic-gate {
534*7c478bd9Sstevel@tonic-gate 	char *tmp_new = NULL;
535*7c478bd9Sstevel@tonic-gate 	int need_update = 0;
536*7c478bd9Sstevel@tonic-gate 
537*7c478bd9Sstevel@tonic-gate 	if (new != NULL) {
538*7c478bd9Sstevel@tonic-gate 		if (prependdir != NULL && new[0] != '/') {
539*7c478bd9Sstevel@tonic-gate 			tmp_new = malloc(strlen(prependdir) + strlen(new) + 2);
540*7c478bd9Sstevel@tonic-gate 			if (tmp_new == NULL)
541*7c478bd9Sstevel@tonic-gate 				return (ENOMEM);
542*7c478bd9Sstevel@tonic-gate 			(void) sprintf(tmp_new, "%s/%s", prependdir, new);
543*7c478bd9Sstevel@tonic-gate 		} else {
544*7c478bd9Sstevel@tonic-gate 			if ((tmp_new = strdup(new)) == NULL)
545*7c478bd9Sstevel@tonic-gate 				return (ENOMEM);
546*7c478bd9Sstevel@tonic-gate 		}
547*7c478bd9Sstevel@tonic-gate 	}
548*7c478bd9Sstevel@tonic-gate 
549*7c478bd9Sstevel@tonic-gate 	if (tmp_new != NULL) {
550*7c478bd9Sstevel@tonic-gate 		if (*old == NULL)
551*7c478bd9Sstevel@tonic-gate 			need_update++;
552*7c478bd9Sstevel@tonic-gate 		else if (strcmp(tmp_new, *old) != 0) {
553*7c478bd9Sstevel@tonic-gate 			free(*old);
554*7c478bd9Sstevel@tonic-gate 			need_update++;
555*7c478bd9Sstevel@tonic-gate 		}
556*7c478bd9Sstevel@tonic-gate 		if (need_update)
557*7c478bd9Sstevel@tonic-gate 			*old = tmp_new;
558*7c478bd9Sstevel@tonic-gate 	} else if (*old != NULL) {
559*7c478bd9Sstevel@tonic-gate 		need_update++;
560*7c478bd9Sstevel@tonic-gate 		free(*old);
561*7c478bd9Sstevel@tonic-gate 		*old = NULL;
562*7c478bd9Sstevel@tonic-gate 	}
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 	*updated = need_update != 0;
565*7c478bd9Sstevel@tonic-gate 	return (0);
566*7c478bd9Sstevel@tonic-gate }
567*7c478bd9Sstevel@tonic-gate 
568*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
569*7c478bd9Sstevel@tonic-gate /*
570*7c478bd9Sstevel@tonic-gate  * Removes and frees the 'config' entry from the list
571*7c478bd9Sstevel@tonic-gate  * pointed to by '*listpp'.
572*7c478bd9Sstevel@tonic-gate  * No error is reported if the entry does not exist.
573*7c478bd9Sstevel@tonic-gate  * Updates '*tail' to point to the last item in the list.
574*7c478bd9Sstevel@tonic-gate  */
575*7c478bd9Sstevel@tonic-gate static void
576*7c478bd9Sstevel@tonic-gate remove_config(
577*7c478bd9Sstevel@tonic-gate 	nfsl_config_t **listpp,
578*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *config,
579*7c478bd9Sstevel@tonic-gate 	nfsl_config_t **tail)
580*7c478bd9Sstevel@tonic-gate {
581*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *p, *prev;
582*7c478bd9Sstevel@tonic-gate 
583*7c478bd9Sstevel@tonic-gate 	prev = *listpp;
584*7c478bd9Sstevel@tonic-gate 	for (p = *listpp; p != NULL; p = p->nc_next) {
585*7c478bd9Sstevel@tonic-gate 		if (p == config) {
586*7c478bd9Sstevel@tonic-gate 			if (p == prev) {
587*7c478bd9Sstevel@tonic-gate 				/*
588*7c478bd9Sstevel@tonic-gate 				 * first element of the list
589*7c478bd9Sstevel@tonic-gate 				 */
590*7c478bd9Sstevel@tonic-gate 				*listpp = prev->nc_next;
591*7c478bd9Sstevel@tonic-gate 			} else
592*7c478bd9Sstevel@tonic-gate 				prev->nc_next = p->nc_next;
593*7c478bd9Sstevel@tonic-gate 			free_config(p);
594*7c478bd9Sstevel@tonic-gate 			break;
595*7c478bd9Sstevel@tonic-gate 		}
596*7c478bd9Sstevel@tonic-gate 		prev = p;
597*7c478bd9Sstevel@tonic-gate 	}
598*7c478bd9Sstevel@tonic-gate 
599*7c478bd9Sstevel@tonic-gate 	/*
600*7c478bd9Sstevel@tonic-gate 	 * Find tail of the list.
601*7c478bd9Sstevel@tonic-gate 	 */
602*7c478bd9Sstevel@tonic-gate 	for (*tail = prev; (*tail)->nc_next != NULL; *tail = (*tail)->nc_next)
603*7c478bd9Sstevel@tonic-gate 		;
604*7c478bd9Sstevel@tonic-gate }
605*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */
606*7c478bd9Sstevel@tonic-gate 
607*7c478bd9Sstevel@tonic-gate static void
608*7c478bd9Sstevel@tonic-gate free_config(nfsl_config_t *config)
609*7c478bd9Sstevel@tonic-gate {
610*7c478bd9Sstevel@tonic-gate 	if (config == NULL)
611*7c478bd9Sstevel@tonic-gate 		return;
612*7c478bd9Sstevel@tonic-gate 	if (config->nc_name)
613*7c478bd9Sstevel@tonic-gate 		free(config->nc_name);
614*7c478bd9Sstevel@tonic-gate 	if (config->nc_defaultdir)
615*7c478bd9Sstevel@tonic-gate 		free(config->nc_defaultdir);
616*7c478bd9Sstevel@tonic-gate 	if (config->nc_bufferpath)
617*7c478bd9Sstevel@tonic-gate 		free(config->nc_bufferpath);
618*7c478bd9Sstevel@tonic-gate 	if (config->nc_rpclogpath)
619*7c478bd9Sstevel@tonic-gate 		free(config->nc_rpclogpath);
620*7c478bd9Sstevel@tonic-gate 	if (config->nc_fhpath)
621*7c478bd9Sstevel@tonic-gate 		free(config->nc_fhpath);
622*7c478bd9Sstevel@tonic-gate 	if (config->nc_logpath)
623*7c478bd9Sstevel@tonic-gate 		free(config->nc_logpath);
624*7c478bd9Sstevel@tonic-gate 	if (config == global)
625*7c478bd9Sstevel@tonic-gate 		global = NULL;
626*7c478bd9Sstevel@tonic-gate 	if (config == global_raw)
627*7c478bd9Sstevel@tonic-gate 		global_raw = NULL;
628*7c478bd9Sstevel@tonic-gate 	free(config);
629*7c478bd9Sstevel@tonic-gate }
630*7c478bd9Sstevel@tonic-gate 
631*7c478bd9Sstevel@tonic-gate void
632*7c478bd9Sstevel@tonic-gate nfsl_freeconfig_list(nfsl_config_t **listpp)
633*7c478bd9Sstevel@tonic-gate {
634*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *next;
635*7c478bd9Sstevel@tonic-gate 
636*7c478bd9Sstevel@tonic-gate 	if (*listpp == NULL)
637*7c478bd9Sstevel@tonic-gate 		return;
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 	do {
640*7c478bd9Sstevel@tonic-gate 		next = (*listpp)->nc_next;
641*7c478bd9Sstevel@tonic-gate 		free_config(*listpp);
642*7c478bd9Sstevel@tonic-gate 		*listpp = next;
643*7c478bd9Sstevel@tonic-gate 	} while (*listpp);
644*7c478bd9Sstevel@tonic-gate 
645*7c478bd9Sstevel@tonic-gate 	free_config(global_raw);
646*7c478bd9Sstevel@tonic-gate }
647*7c478bd9Sstevel@tonic-gate 
648*7c478bd9Sstevel@tonic-gate /*
649*7c478bd9Sstevel@tonic-gate  * Returns a pointer to the first instance of 'tag' in the list.
650*7c478bd9Sstevel@tonic-gate  * If 'remove' is true, then the entry is removed from the list and
651*7c478bd9Sstevel@tonic-gate  * a pointer to it is returned.
652*7c478bd9Sstevel@tonic-gate  * If '*tail' is not NULL, then it will point to the last element of
653*7c478bd9Sstevel@tonic-gate  * the list. Note that this function assumes that *tail already
654*7c478bd9Sstevel@tonic-gate  * points at the last element of the list.
655*7c478bd9Sstevel@tonic-gate  * Returns NULL if the entry does not exist.
656*7c478bd9Sstevel@tonic-gate  */
657*7c478bd9Sstevel@tonic-gate static nfsl_config_t *
658*7c478bd9Sstevel@tonic-gate findconfig(
659*7c478bd9Sstevel@tonic-gate 	nfsl_config_t **listpp,
660*7c478bd9Sstevel@tonic-gate 	char *tag, boolean_t remove,
661*7c478bd9Sstevel@tonic-gate 	nfsl_config_t **tail)
662*7c478bd9Sstevel@tonic-gate {
663*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *p, *prev;
664*7c478bd9Sstevel@tonic-gate 
665*7c478bd9Sstevel@tonic-gate 	prev = *listpp;
666*7c478bd9Sstevel@tonic-gate 	for (p = *listpp; p != NULL; p = p->nc_next) {
667*7c478bd9Sstevel@tonic-gate 		if (strcmp(p->nc_name, tag) == 0) {
668*7c478bd9Sstevel@tonic-gate 			if (remove) {
669*7c478bd9Sstevel@tonic-gate 				if (p == prev) {
670*7c478bd9Sstevel@tonic-gate 					/*
671*7c478bd9Sstevel@tonic-gate 					 * first element of the list
672*7c478bd9Sstevel@tonic-gate 					 */
673*7c478bd9Sstevel@tonic-gate 					*listpp = prev->nc_next;
674*7c478bd9Sstevel@tonic-gate 				} else
675*7c478bd9Sstevel@tonic-gate 					prev->nc_next = p->nc_next;
676*7c478bd9Sstevel@tonic-gate 
677*7c478bd9Sstevel@tonic-gate 				if (tail != NULL && p == *tail) {
678*7c478bd9Sstevel@tonic-gate 					/*
679*7c478bd9Sstevel@tonic-gate 					 * Only update *tail if we removed
680*7c478bd9Sstevel@tonic-gate 					 * the last element of the list, and we
681*7c478bd9Sstevel@tonic-gate 					 * requested *tail to be updated.
682*7c478bd9Sstevel@tonic-gate 					 */
683*7c478bd9Sstevel@tonic-gate 					*tail = prev;
684*7c478bd9Sstevel@tonic-gate 				}
685*7c478bd9Sstevel@tonic-gate 			}
686*7c478bd9Sstevel@tonic-gate 			return (p);
687*7c478bd9Sstevel@tonic-gate 		}
688*7c478bd9Sstevel@tonic-gate 		prev = p;
689*7c478bd9Sstevel@tonic-gate 	}
690*7c478bd9Sstevel@tonic-gate 
691*7c478bd9Sstevel@tonic-gate 	return (NULL);
692*7c478bd9Sstevel@tonic-gate }
693*7c478bd9Sstevel@tonic-gate 
694*7c478bd9Sstevel@tonic-gate static nfsl_config_t *
695*7c478bd9Sstevel@tonic-gate getlastconfig(nfsl_config_t *listp)
696*7c478bd9Sstevel@tonic-gate {
697*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *lastp = NULL;
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 	for (; listp != NULL; listp = listp->nc_next)
700*7c478bd9Sstevel@tonic-gate 		lastp = listp;
701*7c478bd9Sstevel@tonic-gate 
702*7c478bd9Sstevel@tonic-gate 	return (lastp);
703*7c478bd9Sstevel@tonic-gate }
704*7c478bd9Sstevel@tonic-gate 
705*7c478bd9Sstevel@tonic-gate /*
706*7c478bd9Sstevel@tonic-gate  * Returns a pointer to the first instance of 'tag' in the list.
707*7c478bd9Sstevel@tonic-gate  * Returns NULL if the entry does not exist.
708*7c478bd9Sstevel@tonic-gate  * Sets 'error' if the update of the list failed if necessary, and
709*7c478bd9Sstevel@tonic-gate  * returns NULL.
710*7c478bd9Sstevel@tonic-gate  */
711*7c478bd9Sstevel@tonic-gate nfsl_config_t *
712*7c478bd9Sstevel@tonic-gate nfsl_findconfig(nfsl_config_t *listp, char *tag, int *error)
713*7c478bd9Sstevel@tonic-gate {
714*7c478bd9Sstevel@tonic-gate 	nfsl_config_t *config;
715*7c478bd9Sstevel@tonic-gate 	boolean_t updated;
716*7c478bd9Sstevel@tonic-gate 
717*7c478bd9Sstevel@tonic-gate 	*error = 0;
718*7c478bd9Sstevel@tonic-gate 	config = findconfig(&listp, tag, B_FALSE, (nfsl_config_t **)NULL);
719*7c478bd9Sstevel@tonic-gate 	if (config == NULL) {
720*7c478bd9Sstevel@tonic-gate 		/*
721*7c478bd9Sstevel@tonic-gate 		 * Rebuild our list if the file has changed.
722*7c478bd9Sstevel@tonic-gate 		 */
723*7c478bd9Sstevel@tonic-gate 		if (*error = nfsl_checkconfig_list(&listp, &updated)) {
724*7c478bd9Sstevel@tonic-gate 			/*
725*7c478bd9Sstevel@tonic-gate 			 * List may be corrupted, notify caller.
726*7c478bd9Sstevel@tonic-gate 			 */
727*7c478bd9Sstevel@tonic-gate 			return (NULL);
728*7c478bd9Sstevel@tonic-gate 		}
729*7c478bd9Sstevel@tonic-gate 		if (updated) {
730*7c478bd9Sstevel@tonic-gate 			/*
731*7c478bd9Sstevel@tonic-gate 			 * Search for tag again.
732*7c478bd9Sstevel@tonic-gate 			 */
733*7c478bd9Sstevel@tonic-gate 			config = findconfig(&listp, tag, B_FALSE,
734*7c478bd9Sstevel@tonic-gate 				(nfsl_config_t **)NULL);
735*7c478bd9Sstevel@tonic-gate 		}
736*7c478bd9Sstevel@tonic-gate 	}
737*7c478bd9Sstevel@tonic-gate 
738*7c478bd9Sstevel@tonic-gate 	return (config);
739*7c478bd9Sstevel@tonic-gate }
740*7c478bd9Sstevel@tonic-gate 
741*7c478bd9Sstevel@tonic-gate /*
742*7c478bd9Sstevel@tonic-gate  * Use the raw global values if any of the parameters is not defined.
743*7c478bd9Sstevel@tonic-gate  */
744*7c478bd9Sstevel@tonic-gate static void
745*7c478bd9Sstevel@tonic-gate complete_with_global(
746*7c478bd9Sstevel@tonic-gate 	char **defaultdir,
747*7c478bd9Sstevel@tonic-gate 	char **bufferpath,
748*7c478bd9Sstevel@tonic-gate 	char **rpclogpath,
749*7c478bd9Sstevel@tonic-gate 	char **fhpath,
750*7c478bd9Sstevel@tonic-gate 	char **logpath,
751*7c478bd9Sstevel@tonic-gate 	int  *logformat)
752*7c478bd9Sstevel@tonic-gate {
753*7c478bd9Sstevel@tonic-gate 	if (*defaultdir == NULL)
754*7c478bd9Sstevel@tonic-gate 		*defaultdir = global_raw->nc_defaultdir;
755*7c478bd9Sstevel@tonic-gate 	if (*bufferpath == NULL)
756*7c478bd9Sstevel@tonic-gate 		*bufferpath = global_raw->nc_bufferpath;
757*7c478bd9Sstevel@tonic-gate 	if (*rpclogpath == NULL)
758*7c478bd9Sstevel@tonic-gate 		*rpclogpath = global_raw->nc_rpclogpath;
759*7c478bd9Sstevel@tonic-gate 	if (*fhpath == NULL)
760*7c478bd9Sstevel@tonic-gate 		*fhpath = global_raw->nc_fhpath;
761*7c478bd9Sstevel@tonic-gate 	if (*logpath == NULL)
762*7c478bd9Sstevel@tonic-gate 		*logpath = global_raw->nc_logpath;
763*7c478bd9Sstevel@tonic-gate 	if (*logformat == 0)
764*7c478bd9Sstevel@tonic-gate 		*logformat = global_raw->nc_logformat;
765*7c478bd9Sstevel@tonic-gate }
766*7c478bd9Sstevel@tonic-gate 
767*7c478bd9Sstevel@tonic-gate /*
768*7c478bd9Sstevel@tonic-gate  * Parses 'linebuf'. Returns 0 if a valid tag is found, otherwise non-zero.
769*7c478bd9Sstevel@tonic-gate  * Unknown tokens are silently ignored.
770*7c478bd9Sstevel@tonic-gate  * It is the responsibility of the caller to make a copy of the non-NULL
771*7c478bd9Sstevel@tonic-gate  * parameters if they need to be used before linebuf is freed.
772*7c478bd9Sstevel@tonic-gate  */
773*7c478bd9Sstevel@tonic-gate static int
774*7c478bd9Sstevel@tonic-gate get_info(
775*7c478bd9Sstevel@tonic-gate 	char *linebuf,
776*7c478bd9Sstevel@tonic-gate 	char **tag,
777*7c478bd9Sstevel@tonic-gate 	char **defaultdir,
778*7c478bd9Sstevel@tonic-gate 	char **bufferpath,
779*7c478bd9Sstevel@tonic-gate 	char **rpclogpath,
780*7c478bd9Sstevel@tonic-gate 	char **fhpath,
781*7c478bd9Sstevel@tonic-gate 	char **logpath,
782*7c478bd9Sstevel@tonic-gate 	int  *logformat)
783*7c478bd9Sstevel@tonic-gate {
784*7c478bd9Sstevel@tonic-gate 	char *tok;
785*7c478bd9Sstevel@tonic-gate 	char *tmp;
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate 	/* tag */
788*7c478bd9Sstevel@tonic-gate 	*tag = NULL;
789*7c478bd9Sstevel@tonic-gate 	tok = strtok(linebuf, whitespace);
790*7c478bd9Sstevel@tonic-gate 	if (tok == NULL)
791*7c478bd9Sstevel@tonic-gate 		goto badtag;
792*7c478bd9Sstevel@tonic-gate 	if (!is_legal_tag(tok))
793*7c478bd9Sstevel@tonic-gate 		goto badtag;
794*7c478bd9Sstevel@tonic-gate 	*tag = tok;
795*7c478bd9Sstevel@tonic-gate 
796*7c478bd9Sstevel@tonic-gate 	*defaultdir = *bufferpath = *rpclogpath = NULL;
797*7c478bd9Sstevel@tonic-gate 	*fhpath = *logpath = NULL;
798*7c478bd9Sstevel@tonic-gate 	*logformat = 0;
799*7c478bd9Sstevel@tonic-gate 
800*7c478bd9Sstevel@tonic-gate 	while (tok = strtok(NULL, whitespace)) {
801*7c478bd9Sstevel@tonic-gate 		if (strncmp(tok, "defaultdir=", strlen("defaultdir=")) == 0) {
802*7c478bd9Sstevel@tonic-gate 			*defaultdir = tok + strlen("defaultdir=");
803*7c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "buffer=", strlen("buffer=")) == 0) {
804*7c478bd9Sstevel@tonic-gate 			*bufferpath = tok + strlen("buffer=");
805*7c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "rpclog=", strlen("rpclog=")) == 0) {
806*7c478bd9Sstevel@tonic-gate 			*rpclogpath = tok + strlen("rpclog=");
807*7c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "fhtable=", strlen("fhtable=")) == 0) {
808*7c478bd9Sstevel@tonic-gate 			*fhpath = tok + strlen("fhtable=");
809*7c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "log=", strlen("log=")) == 0) {
810*7c478bd9Sstevel@tonic-gate 			*logpath = tok + strlen("log=");
811*7c478bd9Sstevel@tonic-gate 		} else if (strncmp(tok, "logformat=",
812*7c478bd9Sstevel@tonic-gate 				strlen("logformat=")) == 0) {
813*7c478bd9Sstevel@tonic-gate 			tmp = tok + strlen("logformat=");
814*7c478bd9Sstevel@tonic-gate 			if (strncmp(tmp, "extended", strlen("extended")) == 0) {
815*7c478bd9Sstevel@tonic-gate 				*logformat = TRANSLOG_EXTENDED;
816*7c478bd9Sstevel@tonic-gate 			} else {
817*7c478bd9Sstevel@tonic-gate 				/*
818*7c478bd9Sstevel@tonic-gate 				 * Use transaction log basic format if
819*7c478bd9Sstevel@tonic-gate 				 * 'extended' was not specified.
820*7c478bd9Sstevel@tonic-gate 				 */
821*7c478bd9Sstevel@tonic-gate 				*logformat = TRANSLOG_BASIC;
822*7c478bd9Sstevel@tonic-gate 			}
823*7c478bd9Sstevel@tonic-gate 		}
824*7c478bd9Sstevel@tonic-gate 	}
825*7c478bd9Sstevel@tonic-gate 
826*7c478bd9Sstevel@tonic-gate 	if (strcmp(*tag, DEFAULTTAG) != 0) {
827*7c478bd9Sstevel@tonic-gate 		/*
828*7c478bd9Sstevel@tonic-gate 		 * Use global values for fields not specified if
829*7c478bd9Sstevel@tonic-gate 		 * this tag is not the global tag.
830*7c478bd9Sstevel@tonic-gate 		 */
831*7c478bd9Sstevel@tonic-gate 		complete_with_global(defaultdir, bufferpath,
832*7c478bd9Sstevel@tonic-gate 			rpclogpath, fhpath, logpath, logformat);
833*7c478bd9Sstevel@tonic-gate 	}
834*7c478bd9Sstevel@tonic-gate 
835*7c478bd9Sstevel@tonic-gate 	return (0);
836*7c478bd9Sstevel@tonic-gate 
837*7c478bd9Sstevel@tonic-gate badtag:
838*7c478bd9Sstevel@tonic-gate 	if (nfsl_errs_to_syslog) {
839*7c478bd9Sstevel@tonic-gate 		syslog(LOG_ERR, gettext(
840*7c478bd9Sstevel@tonic-gate 			"Bad tag found in config file."));
841*7c478bd9Sstevel@tonic-gate 	} else {
842*7c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
843*7c478bd9Sstevel@tonic-gate 			"Bad tag found in config file.\n"));
844*7c478bd9Sstevel@tonic-gate 	}
845*7c478bd9Sstevel@tonic-gate 	return (-1);
846*7c478bd9Sstevel@tonic-gate }
847*7c478bd9Sstevel@tonic-gate 
848*7c478bd9Sstevel@tonic-gate /*
849*7c478bd9Sstevel@tonic-gate  * Returns True if we have all the elements of a complete configuration
850*7c478bd9Sstevel@tonic-gate  * entry. A complete configuration has tag, bufferpath, fhpath and logpath
851*7c478bd9Sstevel@tonic-gate  * defined to non-zero strings.
852*7c478bd9Sstevel@tonic-gate  */
853*7c478bd9Sstevel@tonic-gate static boolean_t
854*7c478bd9Sstevel@tonic-gate is_complete_config(
855*7c478bd9Sstevel@tonic-gate 	char *tag,
856*7c478bd9Sstevel@tonic-gate 	char *bufferpath,
857*7c478bd9Sstevel@tonic-gate 	char *fhpath,
858*7c478bd9Sstevel@tonic-gate 	char *logpath)
859*7c478bd9Sstevel@tonic-gate {
860*7c478bd9Sstevel@tonic-gate 	assert(tag != NULL);
861*7c478bd9Sstevel@tonic-gate 	assert(strlen(tag) > 0);
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 	if ((bufferpath != NULL && strlen(bufferpath) > 0) &&
864*7c478bd9Sstevel@tonic-gate 	    (fhpath != NULL && strlen(fhpath) > 0) &&
865*7c478bd9Sstevel@tonic-gate 	    (logpath != NULL && strlen(logpath) > 0))
866*7c478bd9Sstevel@tonic-gate 		return (B_TRUE);
867*7c478bd9Sstevel@tonic-gate 	return (B_FALSE);
868*7c478bd9Sstevel@tonic-gate }
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
871*7c478bd9Sstevel@tonic-gate /*
872*7c478bd9Sstevel@tonic-gate  * Prints the configuration entry to stdout.
873*7c478bd9Sstevel@tonic-gate  */
874*7c478bd9Sstevel@tonic-gate void
875*7c478bd9Sstevel@tonic-gate nfsl_printconfig(nfsl_config_t *config)
876*7c478bd9Sstevel@tonic-gate {
877*7c478bd9Sstevel@tonic-gate 	if (config->nc_name)
878*7c478bd9Sstevel@tonic-gate 		(void) printf("tag=%s\t", config->nc_name);
879*7c478bd9Sstevel@tonic-gate 	if (config->nc_defaultdir)
880*7c478bd9Sstevel@tonic-gate 		(void) printf("defaultdir=%s\t", config->nc_defaultdir);
881*7c478bd9Sstevel@tonic-gate 	if (config->nc_logpath)
882*7c478bd9Sstevel@tonic-gate 		(void) printf("logpath=%s\t", config->nc_logpath);
883*7c478bd9Sstevel@tonic-gate 	if (config->nc_fhpath)
884*7c478bd9Sstevel@tonic-gate 		(void) printf("fhpath=%s\t", config->nc_fhpath);
885*7c478bd9Sstevel@tonic-gate 	if (config->nc_bufferpath)
886*7c478bd9Sstevel@tonic-gate 		(void) printf("bufpath=%s\t", config->nc_bufferpath);
887*7c478bd9Sstevel@tonic-gate 	if (config->nc_rpclogpath)
888*7c478bd9Sstevel@tonic-gate 		(void) printf("rpclogpath=%s\t", config->nc_rpclogpath);
889*7c478bd9Sstevel@tonic-gate 	if (config->nc_logformat == TRANSLOG_BASIC)
890*7c478bd9Sstevel@tonic-gate 		(void) printf("logformat=basic");
891*7c478bd9Sstevel@tonic-gate 	else if (config->nc_logformat == TRANSLOG_EXTENDED)
892*7c478bd9Sstevel@tonic-gate 		(void) printf("logformat=extended");
893*7c478bd9Sstevel@tonic-gate 	else
894*7c478bd9Sstevel@tonic-gate 		(void) printf("config->nc_logformat=UNKNOWN");
895*7c478bd9Sstevel@tonic-gate 
896*7c478bd9Sstevel@tonic-gate 	if (config->nc_flags & NC_UPDATED)
897*7c478bd9Sstevel@tonic-gate 		(void) printf("\tflags=NC_UPDATED");
898*7c478bd9Sstevel@tonic-gate 	(void) printf("\n");
899*7c478bd9Sstevel@tonic-gate }
900*7c478bd9Sstevel@tonic-gate 
901*7c478bd9Sstevel@tonic-gate /*
902*7c478bd9Sstevel@tonic-gate  * Prints the configuration list to stdout.
903*7c478bd9Sstevel@tonic-gate  */
904*7c478bd9Sstevel@tonic-gate void
905*7c478bd9Sstevel@tonic-gate nfsl_printconfig_list(nfsl_config_t *listp)
906*7c478bd9Sstevel@tonic-gate {
907*7c478bd9Sstevel@tonic-gate 	for (; listp != NULL; listp = listp->nc_next) {
908*7c478bd9Sstevel@tonic-gate 		nfsl_printconfig(listp);
909*7c478bd9Sstevel@tonic-gate 		(void) printf("\n");
910*7c478bd9Sstevel@tonic-gate 	}
911*7c478bd9Sstevel@tonic-gate }
912*7c478bd9Sstevel@tonic-gate #endif /* DEBUG */
913*7c478bd9Sstevel@tonic-gate 
914*7c478bd9Sstevel@tonic-gate /*
915*7c478bd9Sstevel@tonic-gate  * Returns non-zero if the given string is allowable for a tag, zero if
916*7c478bd9Sstevel@tonic-gate  * not.
917*7c478bd9Sstevel@tonic-gate  */
918*7c478bd9Sstevel@tonic-gate static int
919*7c478bd9Sstevel@tonic-gate is_legal_tag(char *tag)
920*7c478bd9Sstevel@tonic-gate {
921*7c478bd9Sstevel@tonic-gate 	int i;
922*7c478bd9Sstevel@tonic-gate 	int len;
923*7c478bd9Sstevel@tonic-gate 
924*7c478bd9Sstevel@tonic-gate 	if (tag == NULL)
925*7c478bd9Sstevel@tonic-gate 		return (0);
926*7c478bd9Sstevel@tonic-gate 	len = strlen(tag);
927*7c478bd9Sstevel@tonic-gate 	if (len == 0)
928*7c478bd9Sstevel@tonic-gate 		return (0);
929*7c478bd9Sstevel@tonic-gate 
930*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < len; i++) {
931*7c478bd9Sstevel@tonic-gate 		char c;
932*7c478bd9Sstevel@tonic-gate 
933*7c478bd9Sstevel@tonic-gate 		c = tag[i];
934*7c478bd9Sstevel@tonic-gate 		if (!(isalnum((unsigned char)c) || c == '_'))
935*7c478bd9Sstevel@tonic-gate 			return (0);
936*7c478bd9Sstevel@tonic-gate 	}
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate 	return (1);
939*7c478bd9Sstevel@tonic-gate }
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate /*
942*7c478bd9Sstevel@tonic-gate  * getline attempts to get a line from the configuration file,
943*7c478bd9Sstevel@tonic-gate  * upto LINESZ. A line in the file is a concatenation of lines if the
944*7c478bd9Sstevel@tonic-gate  * continuation symbol '\' is used at the end of the line. Returns
945*7c478bd9Sstevel@tonic-gate  * line on success, a NULL on EOF, and an empty string on lines > linesz.
946*7c478bd9Sstevel@tonic-gate  */
947*7c478bd9Sstevel@tonic-gate static char *
948*7c478bd9Sstevel@tonic-gate getline(FILE *fp, char *path, char *line, int linesz) {
949*7c478bd9Sstevel@tonic-gate 	register char *p = line;
950*7c478bd9Sstevel@tonic-gate 	register int len;
951*7c478bd9Sstevel@tonic-gate 	int excess = 0;
952*7c478bd9Sstevel@tonic-gate 
953*7c478bd9Sstevel@tonic-gate 	*p = '\0';
954*7c478bd9Sstevel@tonic-gate 
955*7c478bd9Sstevel@tonic-gate 	for (;;) {
956*7c478bd9Sstevel@tonic-gate 		if (fgets(p, linesz - (p-line), fp) == NULL) {
957*7c478bd9Sstevel@tonic-gate 			return (*line ? line : NULL);   /* EOF */
958*7c478bd9Sstevel@tonic-gate 		}
959*7c478bd9Sstevel@tonic-gate 
960*7c478bd9Sstevel@tonic-gate 		len = strlen(line);
961*7c478bd9Sstevel@tonic-gate 		if (len <= 0) {
962*7c478bd9Sstevel@tonic-gate 			p = line;
963*7c478bd9Sstevel@tonic-gate 			continue;
964*7c478bd9Sstevel@tonic-gate 		}
965*7c478bd9Sstevel@tonic-gate 		p = &line[len - 1];
966*7c478bd9Sstevel@tonic-gate 
967*7c478bd9Sstevel@tonic-gate 		/*
968*7c478bd9Sstevel@tonic-gate 		 * Is input line too long?
969*7c478bd9Sstevel@tonic-gate 		 */
970*7c478bd9Sstevel@tonic-gate 		if (*p != '\n') {
971*7c478bd9Sstevel@tonic-gate 			excess = 1;
972*7c478bd9Sstevel@tonic-gate 			/*
973*7c478bd9Sstevel@tonic-gate 			 * Perhaps last char read was '\'. Reinsert it
974*7c478bd9Sstevel@tonic-gate 			 * into the stream to ease the parsing when we
975*7c478bd9Sstevel@tonic-gate 			 * read the rest of the line to discard.
976*7c478bd9Sstevel@tonic-gate 			 */
977*7c478bd9Sstevel@tonic-gate 			(void) ungetc(*p, fp);
978*7c478bd9Sstevel@tonic-gate 			break;
979*7c478bd9Sstevel@tonic-gate 		}
980*7c478bd9Sstevel@tonic-gate trim:
981*7c478bd9Sstevel@tonic-gate 
982*7c478bd9Sstevel@tonic-gate 		/* trim trailing white space */
983*7c478bd9Sstevel@tonic-gate 		while (p >= line && isspace(*(uchar_t *)p))
984*7c478bd9Sstevel@tonic-gate 		*p-- = '\0';
985*7c478bd9Sstevel@tonic-gate 		if (p < line) {			/* empty line */
986*7c478bd9Sstevel@tonic-gate 			p = line;
987*7c478bd9Sstevel@tonic-gate 			continue;
988*7c478bd9Sstevel@tonic-gate 		}
989*7c478bd9Sstevel@tonic-gate 
990*7c478bd9Sstevel@tonic-gate 		if (*p == '\\') {		/* continuation */
991*7c478bd9Sstevel@tonic-gate 			*p = '\0';
992*7c478bd9Sstevel@tonic-gate 			continue;
993*7c478bd9Sstevel@tonic-gate 		}
994*7c478bd9Sstevel@tonic-gate 
995*7c478bd9Sstevel@tonic-gate 		/*
996*7c478bd9Sstevel@tonic-gate 		 * Ignore comments. Comments start with '#'
997*7c478bd9Sstevel@tonic-gate 		 * which must be preceded by a whitespace, unless
998*7c478bd9Sstevel@tonic-gate 		 * '#' is the first character in the line.
999*7c478bd9Sstevel@tonic-gate 		 */
1000*7c478bd9Sstevel@tonic-gate 		p = line;
1001*7c478bd9Sstevel@tonic-gate 
1002*7c478bd9Sstevel@tonic-gate 		while (p = strchr(p, '#')) {
1003*7c478bd9Sstevel@tonic-gate 			if (p == line || isspace(*(p-1))) {
1004*7c478bd9Sstevel@tonic-gate 				*p-- = '\0';
1005*7c478bd9Sstevel@tonic-gate 				goto trim;
1006*7c478bd9Sstevel@tonic-gate 			}
1007*7c478bd9Sstevel@tonic-gate 			p++;
1008*7c478bd9Sstevel@tonic-gate 		}
1009*7c478bd9Sstevel@tonic-gate 
1010*7c478bd9Sstevel@tonic-gate 		break;
1011*7c478bd9Sstevel@tonic-gate 	}
1012*7c478bd9Sstevel@tonic-gate 	if (excess) {
1013*7c478bd9Sstevel@tonic-gate 		int c;
1014*7c478bd9Sstevel@tonic-gate 
1015*7c478bd9Sstevel@tonic-gate 		/*
1016*7c478bd9Sstevel@tonic-gate 		 * discard rest of line and return an empty string.
1017*7c478bd9Sstevel@tonic-gate 		 * done to set the stream to the correct place when
1018*7c478bd9Sstevel@tonic-gate 		 * we are done with this line.
1019*7c478bd9Sstevel@tonic-gate 		 */
1020*7c478bd9Sstevel@tonic-gate 		while ((c = getc(fp)) != EOF) {
1021*7c478bd9Sstevel@tonic-gate 			*p = c;
1022*7c478bd9Sstevel@tonic-gate 			if (*p == '\n')		/* end of the long line */
1023*7c478bd9Sstevel@tonic-gate 				break;
1024*7c478bd9Sstevel@tonic-gate 			else if (*p == '\\') {		/* continuation */
1025*7c478bd9Sstevel@tonic-gate 				if (getc(fp) == EOF)	/* ignore next char */
1026*7c478bd9Sstevel@tonic-gate 					break;
1027*7c478bd9Sstevel@tonic-gate 			}
1028*7c478bd9Sstevel@tonic-gate 		}
1029*7c478bd9Sstevel@tonic-gate 		if (nfsl_errs_to_syslog) {
1030*7c478bd9Sstevel@tonic-gate 			syslog(LOG_ERR, gettext(
1031*7c478bd9Sstevel@tonic-gate 				"%s: line too long - ignored (max %d chars)"),
1032*7c478bd9Sstevel@tonic-gate 				path, linesz-1);
1033*7c478bd9Sstevel@tonic-gate 		} else {
1034*7c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr, gettext(
1035*7c478bd9Sstevel@tonic-gate 				"%s: line too long - ignored (max %d chars)\n"),
1036*7c478bd9Sstevel@tonic-gate 				path, linesz-1);
1037*7c478bd9Sstevel@tonic-gate 		}
1038*7c478bd9Sstevel@tonic-gate 		*line = '\0';
1039*7c478bd9Sstevel@tonic-gate 	}
1040*7c478bd9Sstevel@tonic-gate 
1041*7c478bd9Sstevel@tonic-gate 	return (line);
1042*7c478bd9Sstevel@tonic-gate }
1043