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