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