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 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 * 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 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 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 * 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 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 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 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 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 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 * 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 * 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 * 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 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 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 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 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 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 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 * 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