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