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 if (error = getconfiglist(listpp, B_FALSE))
145 nfsl_freeconfig_list(listpp);
146 else {
147 assert(global != NULL);
148 /*
149 * The global entry was replaced with the one in the file,
150 * clear the UPDATED flag
151 */
152 global->nc_flags &= ~NC_UPDATED;
153 }
154 return (error);
155 }
156
157 /*
158 * Allocates memory for the 'global_raw' structure.
159 * The actual allocation of values for its components happens in
160 * update_config().
161 */
162 static nfsl_config_t *
create_global_raw(int * error)163 create_global_raw(int *error)
164 {
165 nfsl_config_t *p;
166
167 *error = 0;
168 if (p = (nfsl_config_t *)malloc(sizeof (*p)))
169 (void) memset((void *)p, 0, sizeof (*p));
170 else
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 if (error = get_info(linebuf, &tag, &defaultdir, &bufferpath,
295 &rpclogpath, &fhpath, &logpath, &logformat))
296 break;
297
298 if (listp = findconfig(listpp, tag, B_FALSE, &tail)) {
299 /*
300 * An entry with the same tag name exists,
301 * update the fields that changed.
302 */
303 error = update_config(listp, tag, defaultdir,
304 bufferpath, rpclogpath, fhpath, logpath,
305 logformat, B_TRUE, B_TRUE);
306 if (error)
307 break;
308 } else {
309 /*
310 * New entry, create it.
311 */
312 listp = create_config(tag, defaultdir,
313 bufferpath, rpclogpath, fhpath,
314 logpath, logformat, B_TRUE, &error);
315 if (listp == NULL)
316 break;
317
318 if (*listpp == NULL)
319 *listpp = listp;
320 else
321 tail->nc_next = listp;
322 tail = listp;
323 }
324
325 assert(global != NULL);
326 }
327
328 if (!error) {
329 /*
330 * Get mtime while we have file locked
331 */
332 if (error = fstat(fileno(fp), &st)) {
333 error = errno;
334 if (nfsl_errs_to_syslog) {
335 syslog(LOG_ERR, gettext(
336 "Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH,
337 strerror(error));
338 } else {
339 (void) fprintf(stderr, gettext(
340 "Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH,
341 strerror(error));
342 }
343 }
344 config_last_modification = st.st_mtim;
345 }
346
347 done:
348 (void) fclose(fp);
349 return (error);
350 }
351
352 /*
353 * Creates the config structure with the values specified by the
354 * parameters. If defaultdir has been specified, all relative paths
355 * are prepended with this defaultdir.
356 * If 'complete' is set then this must represent a complete config entry
357 * as specified by is_complete_config(), otherwise no work is perfomed, and
358 * NULL is returned.
359 *
360 * Returns the newly created config structure on success.
361 * Returns NULL on failure and sets error to the appropriate error.
362 */
363 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)364 create_config(
365 char *tag,
366 char *defaultdir,
367 char *bufferpath,
368 char *rpclogpath,
369 char *fhpath,
370 char *logpath,
371 int logformat,
372 boolean_t complete,
373 int *error)
374 {
375 nfsl_config_t *config;
376
377 if ((config = (nfsl_config_t *)malloc(sizeof (*config))) == NULL) {
378 *error = ENOMEM;
379 return (NULL);
380 }
381 (void) memset((void *)config, 0, sizeof (*config));
382
383 *error = update_config(config, tag, defaultdir, bufferpath, rpclogpath,
384 fhpath, logpath, logformat, complete, B_TRUE);
385 if (*error) {
386 free(config);
387 return (NULL);
388 }
389
390 config->nc_flags &= ~NC_UPDATED; /* This is a new entry */
391
392 return (config);
393 }
394
395
396 /*
397 * Updates the configuration entry with the new information provided,
398 * sets NC_UPDATED to indicate so. The entry is left untouched if all
399 * the fields are the same (except for 'nc_rpccookie', 'nc_transcookie'
400 * and 'nc_next').
401 * Prepends each path component with 'defauldir' if 'prepend' is set.
402 *
403 * Returns 0 on success, error otherwise.
404 * On error, the config entry is left in an inconsistent state.
405 * The only thing the caller can really do with it is free it.
406 */
407 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)408 update_config(
409 nfsl_config_t *config,
410 char *tag,
411 char *defaultdir,
412 char *bufferpath,
413 char *rpclogpath,
414 char *fhpath,
415 char *logpath,
416 int logformat,
417 boolean_t complete,
418 boolean_t prepend)
419 {
420 boolean_t updated, config_updated = B_FALSE;
421 int error = 0;
422
423 if (complete && !is_complete_config(tag, bufferpath, fhpath, logpath)) {
424 /*
425 * Not a complete entry
426 */
427 if (nfsl_errs_to_syslog) {
428 syslog(LOG_ERR, gettext(
429 "update_config: \"%s\" not a complete config entry."),
430 tag);
431 } else {
432 (void) fprintf(stderr, gettext(
433 "update_config: \"%s\" not a complete config entry.\n"),
434 tag);
435 }
436 return (EINVAL);
437 }
438
439 assert(tag != NULL);
440 if (config->nc_name == NULL) {
441 /*
442 * New entry
443 */
444 if ((config->nc_name = strdup(tag)) == NULL) {
445 error = ENOMEM;
446 goto errout;
447 }
448 } else
449 assert(strcmp(config->nc_name, tag) == 0);
450
451 if (error = update_field(
452 &config->nc_defaultdir, defaultdir, NULL, &updated))
453 goto errout;
454 if (!prepend) {
455 /*
456 * Do not prepend default directory.
457 */
458 defaultdir = NULL;
459 }
460 config_updated |= updated;
461 if (error = update_field(
462 &config->nc_bufferpath, bufferpath, defaultdir, &updated))
463 goto errout;
464 config_updated |= updated;
465 if (error = update_field(
466 &config->nc_rpclogpath, rpclogpath, defaultdir, &updated))
467 goto errout;
468 config_updated |= updated;
469 if (error = update_field(
470 &config->nc_fhpath, fhpath, defaultdir, &updated))
471 goto errout;
472 config_updated |= updated;
473 if (error = update_field(
474 &config->nc_logpath, logpath, defaultdir, &updated))
475 goto errout;
476 config_updated |= updated;
477 updated = (config->nc_logformat != logformat);
478 if (updated)
479 config->nc_logformat = logformat;
480 config_updated |= updated;
481
482 if (config_updated)
483 config->nc_flags |= NC_UPDATED;
484
485 if (strcmp(tag, DEFAULTTAG) == 0) {
486 /*
487 * Have the default global config point to this entry.
488 */
489 global = config;
490
491 /*
492 * Update the global_raw configuration entry.
493 * Make sure no expanding of paths occurs.
494 */
495 if (error = update_config(global_raw, DEFAULTRAWTAG, defaultdir,
496 bufferpath, rpclogpath, fhpath, logpath, logformat,
497 complete, B_FALSE))
498 goto errout;
499 }
500
501 return (error);
502
503 errout:
504 if (nfsl_errs_to_syslog) {
505 syslog(LOG_ERR, gettext(
506 "update_config: Can't process \"%s\" config entry: %s"),
507 tag, strerror(error));
508 } else {
509 (void) fprintf(stderr, gettext(
510 "update_config: Can't process \"%s\" config entry: %s\n"),
511 tag, strerror(error));
512 }
513 return (error);
514 }
515
516 /*
517 * Prepends 'prependir' to 'new' if 'prependir' is defined.
518 * Compares the value of '*old' with 'new', if it has changed,
519 * then sets whatever 'old' references equal to 'new'.
520 * Returns 0 on success, error otherwise.
521 * Sets '*updated' to B_TRUE if field was modified.
522 * The value of '*updated' is undefined on error.
523 */
524 static int
update_field(char ** old,char * new,char * prependdir,boolean_t * updated)525 update_field(
526 char **old, /* pointer to config field */
527 char *new, /* updated value */
528 char *prependdir, /* prepend this directory to new */
529 boolean_t *updated) /* field was modified */
530 {
531 char *tmp_new = NULL;
532 int need_update = 0;
533
534 if (new != NULL) {
535 if (prependdir != NULL && new[0] != '/') {
536 tmp_new = malloc(strlen(prependdir) + strlen(new) + 2);
537 if (tmp_new == NULL)
538 return (ENOMEM);
539 (void) sprintf(tmp_new, "%s/%s", prependdir, new);
540 } else {
541 if ((tmp_new = strdup(new)) == NULL)
542 return (ENOMEM);
543 }
544 }
545
546 if (tmp_new != NULL) {
547 if (*old == NULL)
548 need_update++;
549 else if (strcmp(tmp_new, *old) != 0) {
550 free(*old);
551 need_update++;
552 }
553 if (need_update)
554 *old = tmp_new;
555 } else if (*old != NULL) {
556 need_update++;
557 free(*old);
558 *old = NULL;
559 }
560
561 *updated = need_update != 0;
562 return (0);
563 }
564
565 #ifdef DEBUG
566 /*
567 * Removes and frees the 'config' entry from the list
568 * pointed to by '*listpp'.
569 * No error is reported if the entry does not exist.
570 * Updates '*tail' to point to the last item in the list.
571 */
572 static void
remove_config(nfsl_config_t ** listpp,nfsl_config_t * config,nfsl_config_t ** tail)573 remove_config(
574 nfsl_config_t **listpp,
575 nfsl_config_t *config,
576 nfsl_config_t **tail)
577 {
578 nfsl_config_t *p, *prev;
579
580 prev = *listpp;
581 for (p = *listpp; p != NULL; p = p->nc_next) {
582 if (p == config) {
583 if (p == prev) {
584 /*
585 * first element of the list
586 */
587 *listpp = prev->nc_next;
588 } else
589 prev->nc_next = p->nc_next;
590 free_config(p);
591 break;
592 }
593 prev = p;
594 }
595
596 /*
597 * Find tail of the list.
598 */
599 for (*tail = prev; (*tail)->nc_next != NULL; *tail = (*tail)->nc_next)
600 ;
601 }
602 #endif /* DEBUG */
603
604 static void
free_config(nfsl_config_t * config)605 free_config(nfsl_config_t *config)
606 {
607 if (config == NULL)
608 return;
609 if (config->nc_name)
610 free(config->nc_name);
611 if (config->nc_defaultdir)
612 free(config->nc_defaultdir);
613 if (config->nc_bufferpath)
614 free(config->nc_bufferpath);
615 if (config->nc_rpclogpath)
616 free(config->nc_rpclogpath);
617 if (config->nc_fhpath)
618 free(config->nc_fhpath);
619 if (config->nc_logpath)
620 free(config->nc_logpath);
621 if (config == global)
622 global = NULL;
623 if (config == global_raw)
624 global_raw = NULL;
625 free(config);
626 }
627
628 void
nfsl_freeconfig_list(nfsl_config_t ** listpp)629 nfsl_freeconfig_list(nfsl_config_t **listpp)
630 {
631 nfsl_config_t *next;
632
633 if (*listpp == NULL)
634 return;
635
636 do {
637 next = (*listpp)->nc_next;
638 free_config(*listpp);
639 *listpp = next;
640 } while (*listpp);
641
642 free_config(global_raw);
643 }
644
645 /*
646 * Returns a pointer to the first instance of 'tag' in the list.
647 * If 'remove' is true, then the entry is removed from the list and
648 * a pointer to it is returned.
649 * If '*tail' is not NULL, then it will point to the last element of
650 * the list. Note that this function assumes that *tail already
651 * points at the last element of the list.
652 * Returns NULL if the entry does not exist.
653 */
654 static nfsl_config_t *
findconfig(nfsl_config_t ** listpp,char * tag,boolean_t remove,nfsl_config_t ** tail)655 findconfig(
656 nfsl_config_t **listpp,
657 char *tag, boolean_t remove,
658 nfsl_config_t **tail)
659 {
660 nfsl_config_t *p, *prev;
661
662 prev = *listpp;
663 for (p = *listpp; p != NULL; p = p->nc_next) {
664 if (strcmp(p->nc_name, tag) == 0) {
665 if (remove) {
666 if (p == prev) {
667 /*
668 * first element of the list
669 */
670 *listpp = prev->nc_next;
671 } else
672 prev->nc_next = p->nc_next;
673
674 if (tail != NULL && p == *tail) {
675 /*
676 * Only update *tail if we removed
677 * the last element of the list, and we
678 * requested *tail to be updated.
679 */
680 *tail = prev;
681 }
682 }
683 return (p);
684 }
685 prev = p;
686 }
687
688 return (NULL);
689 }
690
691 static nfsl_config_t *
getlastconfig(nfsl_config_t * listp)692 getlastconfig(nfsl_config_t *listp)
693 {
694 nfsl_config_t *lastp = NULL;
695
696 for (; listp != NULL; listp = listp->nc_next)
697 lastp = listp;
698
699 return (lastp);
700 }
701
702 /*
703 * Returns a pointer to the first instance of 'tag' in the list.
704 * Returns NULL if the entry does not exist.
705 * Sets 'error' if the update of the list failed if necessary, and
706 * returns NULL.
707 */
708 nfsl_config_t *
nfsl_findconfig(nfsl_config_t * listp,char * tag,int * error)709 nfsl_findconfig(nfsl_config_t *listp, char *tag, int *error)
710 {
711 nfsl_config_t *config;
712 boolean_t updated;
713
714 *error = 0;
715 config = findconfig(&listp, tag, B_FALSE, (nfsl_config_t **)NULL);
716 if (config == NULL) {
717 /*
718 * Rebuild our list if the file has changed.
719 */
720 if (*error = nfsl_checkconfig_list(&listp, &updated)) {
721 /*
722 * List may be corrupted, notify caller.
723 */
724 return (NULL);
725 }
726 if (updated) {
727 /*
728 * Search for tag again.
729 */
730 config = findconfig(&listp, tag, B_FALSE,
731 (nfsl_config_t **)NULL);
732 }
733 }
734
735 return (config);
736 }
737
738 /*
739 * Use the raw global values if any of the parameters is not defined.
740 */
741 static void
complete_with_global(char ** defaultdir,char ** bufferpath,char ** rpclogpath,char ** fhpath,char ** logpath,int * logformat)742 complete_with_global(
743 char **defaultdir,
744 char **bufferpath,
745 char **rpclogpath,
746 char **fhpath,
747 char **logpath,
748 int *logformat)
749 {
750 if (*defaultdir == NULL)
751 *defaultdir = global_raw->nc_defaultdir;
752 if (*bufferpath == NULL)
753 *bufferpath = global_raw->nc_bufferpath;
754 if (*rpclogpath == NULL)
755 *rpclogpath = global_raw->nc_rpclogpath;
756 if (*fhpath == NULL)
757 *fhpath = global_raw->nc_fhpath;
758 if (*logpath == NULL)
759 *logpath = global_raw->nc_logpath;
760 if (*logformat == 0)
761 *logformat = global_raw->nc_logformat;
762 }
763
764 /*
765 * Parses 'linebuf'. Returns 0 if a valid tag is found, otherwise non-zero.
766 * Unknown tokens are silently ignored.
767 * It is the responsibility of the caller to make a copy of the non-NULL
768 * parameters if they need to be used before linebuf is freed.
769 */
770 static int
get_info(char * linebuf,char ** tag,char ** defaultdir,char ** bufferpath,char ** rpclogpath,char ** fhpath,char ** logpath,int * logformat)771 get_info(
772 char *linebuf,
773 char **tag,
774 char **defaultdir,
775 char **bufferpath,
776 char **rpclogpath,
777 char **fhpath,
778 char **logpath,
779 int *logformat)
780 {
781 char *tok;
782 char *tmp;
783
784 /* tag */
785 *tag = NULL;
786 tok = strtok(linebuf, whitespace);
787 if (tok == NULL)
788 goto badtag;
789 if (!is_legal_tag(tok))
790 goto badtag;
791 *tag = tok;
792
793 *defaultdir = *bufferpath = *rpclogpath = NULL;
794 *fhpath = *logpath = NULL;
795 *logformat = 0;
796
797 while (tok = strtok(NULL, whitespace)) {
798 if (strncmp(tok, "defaultdir=", strlen("defaultdir=")) == 0) {
799 *defaultdir = tok + strlen("defaultdir=");
800 } else if (strncmp(tok, "buffer=", strlen("buffer=")) == 0) {
801 *bufferpath = tok + strlen("buffer=");
802 } else if (strncmp(tok, "rpclog=", strlen("rpclog=")) == 0) {
803 *rpclogpath = tok + strlen("rpclog=");
804 } else if (strncmp(tok, "fhtable=", strlen("fhtable=")) == 0) {
805 *fhpath = tok + strlen("fhtable=");
806 } else if (strncmp(tok, "log=", strlen("log=")) == 0) {
807 *logpath = tok + strlen("log=");
808 } else if (strncmp(tok, "logformat=",
809 strlen("logformat=")) == 0) {
810 tmp = tok + strlen("logformat=");
811 if (strncmp(tmp, "extended", strlen("extended")) == 0) {
812 *logformat = TRANSLOG_EXTENDED;
813 } else {
814 /*
815 * Use transaction log basic format if
816 * 'extended' was not specified.
817 */
818 *logformat = TRANSLOG_BASIC;
819 }
820 }
821 }
822
823 if (strcmp(*tag, DEFAULTTAG) != 0) {
824 /*
825 * Use global values for fields not specified if
826 * this tag is not the global tag.
827 */
828 complete_with_global(defaultdir, bufferpath,
829 rpclogpath, fhpath, logpath, logformat);
830 }
831
832 return (0);
833
834 badtag:
835 if (nfsl_errs_to_syslog) {
836 syslog(LOG_ERR, gettext(
837 "Bad tag found in config file."));
838 } else {
839 (void) fprintf(stderr, gettext(
840 "Bad tag found in config file.\n"));
841 }
842 return (-1);
843 }
844
845 /*
846 * Returns True if we have all the elements of a complete configuration
847 * entry. A complete configuration has tag, bufferpath, fhpath and logpath
848 * defined to non-zero strings.
849 */
850 static boolean_t
is_complete_config(char * tag,char * bufferpath,char * fhpath,char * logpath)851 is_complete_config(
852 char *tag,
853 char *bufferpath,
854 char *fhpath,
855 char *logpath)
856 {
857 assert(tag != NULL);
858 assert(strlen(tag) > 0);
859
860 if ((bufferpath != NULL && strlen(bufferpath) > 0) &&
861 (fhpath != NULL && strlen(fhpath) > 0) &&
862 (logpath != NULL && strlen(logpath) > 0))
863 return (B_TRUE);
864 return (B_FALSE);
865 }
866
867 #ifdef DEBUG
868 /*
869 * Prints the configuration entry to stdout.
870 */
871 void
nfsl_printconfig(nfsl_config_t * config)872 nfsl_printconfig(nfsl_config_t *config)
873 {
874 if (config->nc_name)
875 (void) printf("tag=%s\t", config->nc_name);
876 if (config->nc_defaultdir)
877 (void) printf("defaultdir=%s\t", config->nc_defaultdir);
878 if (config->nc_logpath)
879 (void) printf("logpath=%s\t", config->nc_logpath);
880 if (config->nc_fhpath)
881 (void) printf("fhpath=%s\t", config->nc_fhpath);
882 if (config->nc_bufferpath)
883 (void) printf("bufpath=%s\t", config->nc_bufferpath);
884 if (config->nc_rpclogpath)
885 (void) printf("rpclogpath=%s\t", config->nc_rpclogpath);
886 if (config->nc_logformat == TRANSLOG_BASIC)
887 (void) printf("logformat=basic");
888 else if (config->nc_logformat == TRANSLOG_EXTENDED)
889 (void) printf("logformat=extended");
890 else
891 (void) printf("config->nc_logformat=UNKNOWN");
892
893 if (config->nc_flags & NC_UPDATED)
894 (void) printf("\tflags=NC_UPDATED");
895 (void) printf("\n");
896 }
897
898 /*
899 * Prints the configuration list to stdout.
900 */
901 void
nfsl_printconfig_list(nfsl_config_t * listp)902 nfsl_printconfig_list(nfsl_config_t *listp)
903 {
904 for (; listp != NULL; listp = listp->nc_next) {
905 nfsl_printconfig(listp);
906 (void) printf("\n");
907 }
908 }
909 #endif /* DEBUG */
910
911 /*
912 * Returns non-zero if the given string is allowable for a tag, zero if
913 * not.
914 */
915 static int
is_legal_tag(char * tag)916 is_legal_tag(char *tag)
917 {
918 int i;
919 int len;
920
921 if (tag == NULL)
922 return (0);
923 len = strlen(tag);
924 if (len == 0)
925 return (0);
926
927 for (i = 0; i < len; i++) {
928 char c;
929
930 c = tag[i];
931 if (!(isalnum((unsigned char)c) || c == '_'))
932 return (0);
933 }
934
935 return (1);
936 }
937
938 /*
939 * gataline attempts to get a line from the configuration file,
940 * upto LINESZ. A line in the file is a concatenation of lines if the
941 * continuation symbol '\' is used at the end of the line. Returns
942 * line on success, a NULL on EOF, and an empty string on lines > linesz.
943 */
944 static char *
gataline(FILE * fp,char * path,char * line,int linesz)945 gataline(FILE *fp, char *path, char *line, int linesz) {
946 register char *p = line;
947 register int len;
948 int excess = 0;
949
950 *p = '\0';
951
952 for (;;) {
953 if (fgets(p, linesz - (p-line), fp) == NULL) {
954 return (*line ? line : NULL); /* EOF */
955 }
956
957 len = strlen(line);
958 if (len <= 0) {
959 p = line;
960 continue;
961 }
962 p = &line[len - 1];
963
964 /*
965 * Is input line too long?
966 */
967 if (*p != '\n') {
968 excess = 1;
969 /*
970 * Perhaps last char read was '\'. Reinsert it
971 * into the stream to ease the parsing when we
972 * read the rest of the line to discard.
973 */
974 (void) ungetc(*p, fp);
975 break;
976 }
977 trim:
978
979 /* trim trailing white space */
980 while (p >= line && isspace(*(uchar_t *)p))
981 *p-- = '\0';
982 if (p < line) { /* empty line */
983 p = line;
984 continue;
985 }
986
987 if (*p == '\\') { /* continuation */
988 *p = '\0';
989 continue;
990 }
991
992 /*
993 * Ignore comments. Comments start with '#'
994 * which must be preceded by a whitespace, unless
995 * '#' is the first character in the line.
996 */
997 p = line;
998
999 while (p = strchr(p, '#')) {
1000 if (p == line || isspace(*(p-1))) {
1001 *p-- = '\0';
1002 goto trim;
1003 }
1004 p++;
1005 }
1006
1007 break;
1008 }
1009 if (excess) {
1010 int c;
1011
1012 /*
1013 * discard rest of line and return an empty string.
1014 * done to set the stream to the correct place when
1015 * we are done with this line.
1016 */
1017 while ((c = getc(fp)) != EOF) {
1018 *p = c;
1019 if (*p == '\n') /* end of the long line */
1020 break;
1021 else if (*p == '\\') { /* continuation */
1022 if (getc(fp) == EOF) /* ignore next char */
1023 break;
1024 }
1025 }
1026 if (nfsl_errs_to_syslog) {
1027 syslog(LOG_ERR, gettext(
1028 "%s: line too long - ignored (max %d chars)"),
1029 path, linesz-1);
1030 } else {
1031 (void) fprintf(stderr, gettext(
1032 "%s: line too long - ignored (max %d chars)\n"),
1033 path, linesz-1);
1034 }
1035 *line = '\0';
1036 }
1037
1038 return (line);
1039 }
1040