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.
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.
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]
23 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
37 #include <sys/types.h>
41 #include "nfslog_config.h"
43 #define ERROR_BUFSZ 100
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.
50 boolean_t nfsl_errs_to_syslog
;
53 * Pointer to the global entry in the list
55 static nfsl_config_t
*global
= NULL
;
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.
62 static nfsl_config_t
*global_raw
= NULL
;
65 * Last modification time to config file.
67 static timestruc_t config_last_modification
= { 0 };
70 * Whitespace characters to delimit fields in a line.
72 static const char *whitespace
= " \t";
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
,
83 static nfsl_config_t
*getlastconfig(nfsl_config_t
*);
84 static void complete_with_global(char **, char **, char **, char **,
87 static void remove_config(nfsl_config_t
**, nfsl_config_t
*, nfsl_config_t
**);
88 void nfsl_printconfig(nfsl_config_t
*);
90 static char *gataline(FILE *, char *, char *, int);
91 static int get_info(char *, char **, char **, char **, char **, char **,
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 *);
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().
103 * If the configuration file does not exist, *listpp points to a config entry
104 * containing the hardwired defaults.
107 nfsl_getconfig_list(nfsl_config_t
**listpp
)
113 * Set the locale correctly so that we can correctly identify
114 * alphabetic characters.
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
);
124 * Allocate 'global_raw' structure, its contents are
125 * indirectly allocated by create_config().
127 assert(global_raw
== NULL
);
128 global_raw
= create_global_raw(&error
);
129 if (global_raw
== NULL
)
133 * Build global entry with hardwired defaults first.
135 assert(global
== NULL
);
136 global
= create_config(DEFAULTTAG
, DEFAULTDIR
, BUFFERPATH
, NULL
,
137 FHPATH
, LOGPATH
, TRANSLOG_BASIC
, B_TRUE
, &error
);
139 if (global
== NULL
) {
140 free_config(global_raw
);
144 if (error
= getconfiglist(listpp
, B_FALSE
))
145 nfsl_freeconfig_list(listpp
);
147 assert(global
!= NULL
);
149 * The global entry was replaced with the one in the file,
150 * clear the UPDATED flag
152 global
->nc_flags
&= ~NC_UPDATED
;
158 * Allocates memory for the 'global_raw' structure.
159 * The actual allocation of values for its components happens in
162 static nfsl_config_t
*
163 create_global_raw(int *error
)
168 if (p
= (nfsl_config_t
*)malloc(sizeof (*p
)))
169 (void) memset((void *)p
, 0, sizeof (*p
));
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
183 * if 'updated' is defined then it is set to TRUE if the list was modified.
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.
191 nfsl_checkconfig_list(nfsl_config_t
**listpp
, boolean_t
*updated
)
199 if (stat(NFSL_CONFIG_FILE_PATH
, &st
) == -1) {
201 if (nfsl_errs_to_syslog
) {
202 syslog(LOG_ERR
, gettext(
203 "Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH
,
206 (void) fprintf(stderr
, gettext(
207 "Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH
,
213 if (config_last_modification
.tv_sec
== st
.st_mtim
.tv_sec
&&
214 config_last_modification
.tv_nsec
== st
.st_mtim
.tv_nsec
)
220 return (getconfiglist(listpp
, B_TRUE
));
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.
229 * Returns 0 on success and updates the '*listpp' config list,
230 * Returns non-zero error value otherwise.
233 getconfiglist(nfsl_config_t
**listpp
, boolean_t updating
)
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
;
245 fp
= fopen(NFSL_CONFIG_FILE_PATH
, "r");
248 (void) sprintf(errorbuf
, "Can't open %s",
249 NFSL_CONFIG_FILE_PATH
);
251 (void) sprintf(errorbuf
,
252 "Can't open %s - using hardwired defaults",
253 NFSL_CONFIG_FILE_PATH
);
257 * Use hardwired config.
259 if (nfsl_errs_to_syslog
)
260 syslog(LOG_ERR
, gettext("%s"), errorbuf
);
262 (void) fprintf(stderr
, gettext("%s\n"), errorbuf
);
267 (void) memset((void *) &flock
, 0, sizeof (flock
));
268 flock
.l_type
= F_RDLCK
;
269 if (fcntl(fileno(fp
), F_SETLKW
, &flock
) == -1) {
271 if (nfsl_errs_to_syslog
) {
272 syslog(LOG_ERR
, gettext(
273 "Can't lock %s - %s"), NFSL_CONFIG_FILE_PATH
,
276 (void) fprintf(stderr
, gettext(
277 "Can't lock %s - %s\n"), NFSL_CONFIG_FILE_PATH
,
283 assert (*listpp
!= NULL
);
284 tail
= getlastconfig(*listpp
);
286 while (gataline(fp
, NFSL_CONFIG_FILE_PATH
, linebuf
, sizeof (linebuf
))) {
287 if (linebuf
[0] == '\0') {
289 * ignore lines that exceed max size
294 if (error
= get_info(linebuf
, &tag
, &defaultdir
, &bufferpath
,
295 &rpclogpath
, &fhpath
, &logpath
, &logformat
))
298 if (listp
= findconfig(listpp
, tag
, B_FALSE
, &tail
)) {
300 * An entry with the same tag name exists,
301 * update the fields that changed.
303 error
= update_config(listp
, tag
, defaultdir
,
304 bufferpath
, rpclogpath
, fhpath
, logpath
,
305 logformat
, B_TRUE
, B_TRUE
);
310 * New entry, create it.
312 listp
= create_config(tag
, defaultdir
,
313 bufferpath
, rpclogpath
, fhpath
,
314 logpath
, logformat
, B_TRUE
, &error
);
321 tail
->nc_next
= listp
;
325 assert(global
!= NULL
);
330 * Get mtime while we have file locked
332 if (error
= fstat(fileno(fp
), &st
)) {
334 if (nfsl_errs_to_syslog
) {
335 syslog(LOG_ERR
, gettext(
336 "Can't stat %s - %s"), NFSL_CONFIG_FILE_PATH
,
339 (void) fprintf(stderr
, gettext(
340 "Can't stat %s - %s\n"), NFSL_CONFIG_FILE_PATH
,
344 config_last_modification
= st
.st_mtim
;
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
360 * Returns the newly created config structure on success.
361 * Returns NULL on failure and sets error to the appropriate error.
363 static nfsl_config_t
*
375 nfsl_config_t
*config
;
377 if ((config
= (nfsl_config_t
*)malloc(sizeof (*config
))) == NULL
) {
381 (void) memset((void *)config
, 0, sizeof (*config
));
383 *error
= update_config(config
, tag
, defaultdir
, bufferpath
, rpclogpath
,
384 fhpath
, logpath
, logformat
, complete
, B_TRUE
);
390 config
->nc_flags
&= ~NC_UPDATED
; /* This is a new entry */
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'
401 * Prepends each path component with 'defauldir' if 'prepend' is set.
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.
409 nfsl_config_t
*config
,
420 boolean_t updated
, config_updated
= B_FALSE
;
423 if (complete
&& !is_complete_config(tag
, bufferpath
, fhpath
, logpath
)) {
425 * Not a complete entry
427 if (nfsl_errs_to_syslog
) {
428 syslog(LOG_ERR
, gettext(
429 "update_config: \"%s\" not a complete config entry."),
432 (void) fprintf(stderr
, gettext(
433 "update_config: \"%s\" not a complete config entry.\n"),
440 if (config
->nc_name
== NULL
) {
444 if ((config
->nc_name
= strdup(tag
)) == NULL
) {
449 assert(strcmp(config
->nc_name
, tag
) == 0);
451 if (error
= update_field(
452 &config
->nc_defaultdir
, defaultdir
, NULL
, &updated
))
456 * Do not prepend default directory.
460 config_updated
|= updated
;
461 if (error
= update_field(
462 &config
->nc_bufferpath
, bufferpath
, defaultdir
, &updated
))
464 config_updated
|= updated
;
465 if (error
= update_field(
466 &config
->nc_rpclogpath
, rpclogpath
, defaultdir
, &updated
))
468 config_updated
|= updated
;
469 if (error
= update_field(
470 &config
->nc_fhpath
, fhpath
, defaultdir
, &updated
))
472 config_updated
|= updated
;
473 if (error
= update_field(
474 &config
->nc_logpath
, logpath
, defaultdir
, &updated
))
476 config_updated
|= updated
;
477 updated
= (config
->nc_logformat
!= logformat
);
479 config
->nc_logformat
= logformat
;
480 config_updated
|= updated
;
483 config
->nc_flags
|= NC_UPDATED
;
485 if (strcmp(tag
, DEFAULTTAG
) == 0) {
487 * Have the default global config point to this entry.
492 * Update the global_raw configuration entry.
493 * Make sure no expanding of paths occurs.
495 if (error
= update_config(global_raw
, DEFAULTRAWTAG
, defaultdir
,
496 bufferpath
, rpclogpath
, fhpath
, logpath
, logformat
,
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
));
509 (void) fprintf(stderr
, gettext(
510 "update_config: Can't process \"%s\" config entry: %s\n"),
511 tag
, strerror(error
));
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.
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 */
531 char *tmp_new
= NULL
;
535 if (prependdir
!= NULL
&& new[0] != '/') {
536 tmp_new
= malloc(strlen(prependdir
) + strlen(new) + 2);
539 (void) sprintf(tmp_new
, "%s/%s", prependdir
, new);
541 if ((tmp_new
= strdup(new)) == NULL
)
546 if (tmp_new
!= NULL
) {
549 else if (strcmp(tmp_new
, *old
) != 0) {
555 } else if (*old
!= NULL
) {
561 *updated
= need_update
!= 0;
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.
574 nfsl_config_t
**listpp
,
575 nfsl_config_t
*config
,
576 nfsl_config_t
**tail
)
578 nfsl_config_t
*p
, *prev
;
581 for (p
= *listpp
; p
!= NULL
; p
= p
->nc_next
) {
585 * first element of the list
587 *listpp
= prev
->nc_next
;
589 prev
->nc_next
= p
->nc_next
;
597 * Find tail of the list.
599 for (*tail
= prev
; (*tail
)->nc_next
!= NULL
; *tail
= (*tail
)->nc_next
)
605 free_config(nfsl_config_t
*config
)
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
)
623 if (config
== global_raw
)
629 nfsl_freeconfig_list(nfsl_config_t
**listpp
)
637 next
= (*listpp
)->nc_next
;
638 free_config(*listpp
);
642 free_config(global_raw
);
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.
654 static nfsl_config_t
*
656 nfsl_config_t
**listpp
,
657 char *tag
, boolean_t remove
,
658 nfsl_config_t
**tail
)
660 nfsl_config_t
*p
, *prev
;
663 for (p
= *listpp
; p
!= NULL
; p
= p
->nc_next
) {
664 if (strcmp(p
->nc_name
, tag
) == 0) {
668 * first element of the list
670 *listpp
= prev
->nc_next
;
672 prev
->nc_next
= p
->nc_next
;
674 if (tail
!= NULL
&& p
== *tail
) {
676 * Only update *tail if we removed
677 * the last element of the list, and we
678 * requested *tail to be updated.
691 static nfsl_config_t
*
692 getlastconfig(nfsl_config_t
*listp
)
694 nfsl_config_t
*lastp
= NULL
;
696 for (; listp
!= NULL
; listp
= listp
->nc_next
)
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
709 nfsl_findconfig(nfsl_config_t
*listp
, char *tag
, int *error
)
711 nfsl_config_t
*config
;
715 config
= findconfig(&listp
, tag
, B_FALSE
, (nfsl_config_t
**)NULL
);
716 if (config
== NULL
) {
718 * Rebuild our list if the file has changed.
720 if (*error
= nfsl_checkconfig_list(&listp
, &updated
)) {
722 * List may be corrupted, notify caller.
728 * Search for tag again.
730 config
= findconfig(&listp
, tag
, B_FALSE
,
731 (nfsl_config_t
**)NULL
);
739 * Use the raw global values if any of the parameters is not defined.
742 complete_with_global(
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
;
757 *fhpath
= global_raw
->nc_fhpath
;
758 if (*logpath
== NULL
)
759 *logpath
= global_raw
->nc_logpath
;
761 *logformat
= global_raw
->nc_logformat
;
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.
786 tok
= strtok(linebuf
, whitespace
);
789 if (!is_legal_tag(tok
))
793 *defaultdir
= *bufferpath
= *rpclogpath
= NULL
;
794 *fhpath
= *logpath
= NULL
;
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
;
815 * Use transaction log basic format if
816 * 'extended' was not specified.
818 *logformat
= TRANSLOG_BASIC
;
823 if (strcmp(*tag
, DEFAULTTAG
) != 0) {
825 * Use global values for fields not specified if
826 * this tag is not the global tag.
828 complete_with_global(defaultdir
, bufferpath
,
829 rpclogpath
, fhpath
, logpath
, logformat
);
835 if (nfsl_errs_to_syslog
) {
836 syslog(LOG_ERR
, gettext(
837 "Bad tag found in config file."));
839 (void) fprintf(stderr
, gettext(
840 "Bad tag found in config file.\n"));
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.
858 assert(strlen(tag
) > 0);
860 if ((bufferpath
!= NULL
&& strlen(bufferpath
) > 0) &&
861 (fhpath
!= NULL
&& strlen(fhpath
) > 0) &&
862 (logpath
!= NULL
&& strlen(logpath
) > 0))
869 * Prints the configuration entry to stdout.
872 nfsl_printconfig(nfsl_config_t
*config
)
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");
891 (void) printf("config->nc_logformat=UNKNOWN");
893 if (config
->nc_flags
& NC_UPDATED
)
894 (void) printf("\tflags=NC_UPDATED");
899 * Prints the configuration list to stdout.
902 nfsl_printconfig_list(nfsl_config_t
*listp
)
904 for (; listp
!= NULL
; listp
= listp
->nc_next
) {
905 nfsl_printconfig(listp
);
912 * Returns non-zero if the given string is allowable for a tag, zero if
916 is_legal_tag(char *tag
)
927 for (i
= 0; i
< len
; i
++) {
931 if (!(isalnum((unsigned char)c
) || c
== '_'))
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.
945 gataline(FILE *fp
, char *path
, char *line
, int linesz
) {
946 register char *p
= line
;
953 if (fgets(p
, linesz
- (p
-line
), fp
) == NULL
) {
954 return (*line
? line
: NULL
); /* EOF */
965 * Is input line too long?
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.
974 (void) ungetc(*p
, fp
);
979 /* trim trailing white space */
980 while (p
>= line
&& isspace(*(uchar_t
*)p
))
982 if (p
< line
) { /* empty line */
987 if (*p
== '\\') { /* continuation */
993 * Ignore comments. Comments start with '#'
994 * which must be preceded by a whitespace, unless
995 * '#' is the first character in the line.
999 while (p
= strchr(p
, '#')) {
1000 if (p
== line
|| isspace(*(p
-1))) {
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.
1017 while ((c
= getc(fp
)) != EOF
) {
1019 if (*p
== '\n') /* end of the long line */
1021 else if (*p
== '\\') { /* continuation */
1022 if (getc(fp
) == EOF
) /* ignore next char */
1026 if (nfsl_errs_to_syslog
) {
1027 syslog(LOG_ERR
, gettext(
1028 "%s: line too long - ignored (max %d chars)"),
1031 (void) fprintf(stderr
, gettext(
1032 "%s: line too long - ignored (max %d chars)\n"),