postconf_master.c revision 1.3
1/* $NetBSD: postconf_master.c,v 1.3 2014/07/06 19:45:50 tron Exp $ */ 2 3/*++ 4/* NAME 5/* postconf_master 3 6/* SUMMARY 7/* support for master.cf 8/* SYNOPSIS 9/* #include <postconf.h> 10/* 11/* const char pcf_daemon_options_expecting_value[]; 12/* 13/* void pcf_read_master(fail_on_open) 14/* int fail_on_open; 15/* 16/* void pcf_show_master_entries(fp, mode, service_filters) 17/* VSTREAM *fp; 18/* int mode; 19/* char **service_filters; 20/* 21/* void pcf_show_master_fields(fp, mode, n_filters, field_filters) 22/* VSTREAM *fp; 23/* int mode; 24/* int n_filters; 25/* char **field_filters; 26/* 27/* void pcf_edit_master_field(masterp, field, new_value) 28/* PCF_MASTER_ENT *masterp; 29/* int field; 30/* const char *new_value; 31/* 32/* void pcf_show_master_params(fp, mode, argc, **param_filters) 33/* VSTREAM *fp; 34/* int mode; 35/* int argc; 36/* char **param_filters; 37/* 38/* void pcf_edit_master_param(masterp, mode, param_name, param_value) 39/* PCF_MASTER_ENT *masterp; 40/* int mode; 41/* const char *param_name; 42/* const char *param_value; 43/* AUXILIARY FUNCTIONS 44/* const char *pcf_parse_master_entry(masterp, buf) 45/* PCF_MASTER_ENT *masterp; 46/* const char *buf; 47/* 48/* void pcf_print_master_entry(fp, mode, masterp) 49/* VSTREAM *fp; 50/* int mode; 51/* PCF_MASTER_ENT *masterp; 52/* 53/* void pcf_free_master_entry(masterp) 54/* PCF_MASTER_ENT *masterp; 55/* DESCRIPTION 56/* pcf_read_master() reads entries from master.cf into memory. 57/* 58/* pcf_show_master_entries() writes the entries in the master.cf 59/* file to the specified stream. 60/* 61/* pcf_show_master_fields() writes name/type/field=value records 62/* to the specified stream. 63/* 64/* pcf_edit_master_field() updates the value of a single-column 65/* or multi-column attribute. 66/* 67/* pcf_show_master_params() writes name/type/parameter=value 68/* records to the specified stream. 69/* 70/* pcf_edit_master_param() updates, removes or adds the named 71/* parameter in a master.cf entry (the remove request ignores 72/* the parameter value). 73/* 74/* pcf_daemon_options_expecting_value[] is an array of master.cf 75/* daemon command-line options that expect an option value. 76/* 77/* pcf_parse_master_entry() parses a (perhaps multi-line) 78/* string that contains a complete master.cf entry, and 79/* normalizes daemon command-line options to simplify further 80/* handling. 81/* 82/* pcf_print_master_entry() prints a parsed master.cf entry. 83/* 84/* pcf_free_master_entry() returns storage to the heap that 85/* was allocated by pcf_parse_master_entry(). 86/* 87/* Arguments 88/* .IP fail_on_open 89/* Specify FAIL_ON_OPEN if open failure is a fatal error, 90/* WARN_ON_OPEN if a warning should be logged instead. 91/* .IP fp 92/* Output stream. 93/* .IP mode 94/* Bit-wise OR of flags. Flags other than the following are 95/* ignored. 96/* .RS 97/* .IP PCF_FOLD_LINE 98/* Wrap long output lines. 99/* .IP PCF_SHOW_EVAL 100/* Expand $name in parameter values. 101/* .IP PCF_EDIT_EXCL 102/* Request that pcf_edit_master_param() removes the parameter. 103/* .RE 104/* .IP n_filters 105/* The number of command-line filters. 106/* .IP field_filters 107/* A list of zero or more service field patterns (name/type/field). 108/* The output is formatted as "name/type/field = value". If 109/* no filters are specified, pcf_show_master_fields() outputs 110/* the fields of all master.cf entries in the specified order. 111/* .IP param_filters 112/* A list of zero or more service parameter patterns 113/* (name/type/parameter). The output is formatted as 114/* "name/type/parameter = value". If no filters are specified, 115/* pcf_show_master_params() outputs the parameters of all 116/* master.cf entries in sorted order. 117/* .IP service_filters 118/* A list of zero or more service patterns (name or name/type). 119/* If no filters are specified, pcf_show_master_entries() 120/* outputs all master.cf entries in the specified order. 121/* .IP field 122/* Index into parsed master.cf entry. 123/* .IP new_value 124/* Replacement value for the specified field. It is split in 125/* whitespace in case of a multi-field attribute. 126/* DIAGNOSTICS 127/* Problems are reported to the standard error stream. 128/* LICENSE 129/* .ad 130/* .fi 131/* The Secure Mailer license must be distributed with this software. 132/* AUTHOR(S) 133/* Wietse Venema 134/* IBM T.J. Watson Research 135/* P.O. Box 704 136/* Yorktown Heights, NY 10598, USA 137/*--*/ 138 139/* System library. */ 140 141#include <sys_defs.h> 142#include <string.h> 143#include <stdlib.h> 144#include <stdarg.h> 145 146/* Utility library. */ 147 148#include <msg.h> 149#include <mymalloc.h> 150#include <vstring.h> 151#include <argv.h> 152#include <vstream.h> 153#include <readlline.h> 154#include <stringops.h> 155#include <split_at.h> 156 157/* Global library. */ 158 159#include <mail_params.h> 160 161/* Master library. */ 162 163#include <master_proto.h> 164 165/* Application-specific. */ 166 167#include <postconf.h> 168 169const char pcf_daemon_options_expecting_value[] = "o"; 170 171 /* 172 * Data structure to capture a command-line service field filter. 173 */ 174typedef struct { 175 int match_count; /* hit count */ 176 const char *raw_text; /* full pattern text */ 177 ARGV *service_pattern; /* parsed service name, type, ... */ 178 int field_pattern; /* parsed field pattern */ 179 const char *param_pattern; /* parameter pattern */ 180} PCF_MASTER_FLD_REQ; 181 182 /* 183 * Valid inputs. 184 */ 185static const char *pcf_valid_master_types[] = { 186 MASTER_XPORT_NAME_UNIX, 187 MASTER_XPORT_NAME_FIFO, 188 MASTER_XPORT_NAME_INET, 189 MASTER_XPORT_NAME_PASS, 190 0, 191}; 192 193static const char pcf_valid_bool_types[] = "yn-"; 194 195#define STR(x) vstring_str(x) 196 197/* pcf_normalize_options - bring options into canonical form */ 198 199static void pcf_normalize_options(ARGV *argv) 200{ 201 int field; 202 char *arg; 203 char *cp; 204 char *junk; 205 206 /* 207 * Normalize options to simplify later processing. 208 */ 209 for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) { 210 arg = argv->argv[field]; 211 if (arg[0] != '-' || strcmp(arg, "--") == 0) 212 break; 213 for (cp = arg + 1; *cp; cp++) { 214 if (strchr(pcf_daemon_options_expecting_value, *cp) != 0 215 && cp > arg + 1) { 216 /* Split "-stuffozz" into "-stuff" and "-ozz". */ 217 junk = concatenate("-", cp, (char *) 0); 218 argv_insert_one(argv, field + 1, junk); 219 myfree(junk); 220 *cp = 0; /* XXX argv_replace_one() */ 221 break; 222 } 223 } 224 if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0) 225 /* Option requires no value. */ 226 continue; 227 if (arg[2] != 0) { 228 /* Split "-oname=value" into "-o" "name=value". */ 229 argv_insert_one(argv, field + 1, arg + 2); 230 arg[2] = 0; /* XXX argv_replace_one() */ 231 field += 1; 232 } else if (argv->argv[field + 1] != 0) { 233 /* Already in "-o" "name=value" form. */ 234 field += 1; 235 } 236 } 237} 238 239/* pcf_fix_fatal - fix multiline text before release */ 240 241static NORETURN PRINTFLIKE(1, 2) pcf_fix_fatal(const char *fmt,...) 242{ 243 VSTRING *buf = vstring_alloc(100); 244 va_list ap; 245 246 /* 247 * Replace newline with whitespace. 248 */ 249 va_start(ap, fmt); 250 vstring_vsprintf(buf, fmt, ap); 251 va_end(ap); 252 translit(STR(buf), "\n", " "); 253 msg_fatal("%s", STR(buf)); 254 /* NOTREACHED */ 255} 256 257/* pcf_check_master_entry - sanity check master.cf entry */ 258 259static void pcf_check_master_entry(ARGV *argv, const char *raw_text) 260{ 261 const char **cpp; 262 char *cp; 263 int len; 264 int field; 265 266 cp = argv->argv[PCF_MASTER_FLD_TYPE]; 267 for (cpp = pcf_valid_master_types; /* see below */ ; cpp++) { 268 if (*cpp == 0) 269 pcf_fix_fatal("invalid " PCF_MASTER_NAME_TYPE " field \"%s\" in \"%s\"", 270 cp, raw_text); 271 if (strcmp(*cpp, cp) == 0) 272 break; 273 } 274 275 for (field = PCF_MASTER_FLD_PRIVATE; field <= PCF_MASTER_FLD_CHROOT; field++) { 276 cp = argv->argv[field]; 277 if (cp[1] != 0 || strchr(pcf_valid_bool_types, *cp) == 0) 278 pcf_fix_fatal("invalid %s field \%s\" in \"%s\"", 279 pcf_str_field_pattern(field), cp, raw_text); 280 } 281 282 cp = argv->argv[PCF_MASTER_FLD_WAKEUP]; 283 len = strlen(cp); 284 if (len > 0 && cp[len - 1] == '?') 285 len--; 286 if (!(cp[0] == '-' && len == 1) && strspn(cp, "0123456789") != len) 287 pcf_fix_fatal("invalid " PCF_MASTER_NAME_WAKEUP " field \%s\" in \"%s\"", 288 cp, raw_text); 289 290 cp = argv->argv[PCF_MASTER_FLD_MAXPROC]; 291 if (strcmp("-", cp) != 0 && cp[strspn(cp, "0123456789")] != 0) 292 pcf_fix_fatal("invalid " PCF_MASTER_NAME_MAXPROC " field \%s\" in \"%s\"", 293 cp, raw_text); 294} 295 296/* pcf_free_master_entry - destroy parsed entry */ 297 298void pcf_free_master_entry(PCF_MASTER_ENT *masterp) 299{ 300 /* XX Fixme: allocation/deallocation asymmetry. */ 301 myfree(masterp->name_space); 302 argv_free(masterp->argv); 303 if (masterp->valid_names) 304 htable_free(masterp->valid_names, myfree); 305 if (masterp->all_params) 306 dict_free(masterp->all_params); 307 myfree((char *) masterp); 308} 309 310/* pcf_parse_master_entry - parse one master line */ 311 312const char *pcf_parse_master_entry(PCF_MASTER_ENT *masterp, const char *buf) 313{ 314 ARGV *argv; 315 316 /* 317 * We can't use the master daemon's master_ent routines in their current 318 * form. They convert everything to internal form, and they skip disabled 319 * services. 320 * 321 * The postconf command needs to show default fields as "-", and needs to 322 * know about all service names so that it can generate service-dependent 323 * parameter names (transport-dependent etc.). 324 * 325 * XXX Do per-field sanity checks. 326 */ 327 argv = argv_split(buf, PCF_MASTER_BLANKS); 328 if (argv->argc < PCF_MASTER_MIN_FIELDS) { 329 argv_free(argv); /* Coverity 201311 */ 330 return ("bad field count"); 331 } 332 pcf_check_master_entry(argv, buf); 333 pcf_normalize_options(argv); 334 masterp->name_space = 335 concatenate(argv->argv[0], PCF_NAMESP_SEP_STR, argv->argv[1], (char *) 0); 336 masterp->argv = argv; 337 masterp->valid_names = 0; 338 masterp->all_params = 0; 339 return (0); 340} 341 342/* pcf_read_master - read and digest the master.cf file */ 343 344void pcf_read_master(int fail_on_open_error) 345{ 346 const char *myname = "pcf_read_master"; 347 char *path; 348 VSTRING *buf; 349 VSTREAM *fp; 350 const char *err; 351 int entry_count = 0; 352 int line_count = 0; 353 354 /* 355 * Sanity check. 356 */ 357 if (pcf_master_table != 0) 358 msg_panic("%s: master table is already initialized", myname); 359 360 /* 361 * Get the location of master.cf. 362 */ 363 if (var_config_dir == 0) 364 pcf_set_config_dir(); 365 path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); 366 367 /* 368 * Initialize the in-memory master table. 369 */ 370 pcf_master_table = (PCF_MASTER_ENT *) mymalloc(sizeof(*pcf_master_table)); 371 372 /* 373 * Skip blank lines and comment lines. Degrade gracefully if master.cf is 374 * not available, and master.cf is not the primary target. 375 */ 376 if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) { 377 if (fail_on_open_error) 378 msg_fatal("open %s: %m", path); 379 msg_warn("open %s: %m", path); 380 } else { 381 buf = vstring_alloc(100); 382 while (readlline(buf, fp, &line_count) != 0) { 383 pcf_master_table = (PCF_MASTER_ENT *) myrealloc((char *) pcf_master_table, 384 (entry_count + 2) * sizeof(*pcf_master_table)); 385 if ((err = pcf_parse_master_entry(pcf_master_table + entry_count, 386 STR(buf))) != 0) 387 msg_fatal("file %s: line %d: %s", path, line_count, err); 388 entry_count += 1; 389 } 390 vstream_fclose(fp); 391 vstring_free(buf); 392 } 393 394 /* 395 * Null-terminate the master table and clean up. 396 */ 397 pcf_master_table[entry_count].argv = 0; 398 myfree(path); 399} 400 401/* pcf_print_master_entry - print one master line */ 402 403void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp) 404{ 405 char **argv = masterp->argv->argv; 406 const char *arg; 407 const char *aval; 408 int arg_len; 409 int line_len; 410 int field; 411 int in_daemon_options; 412 static int column_goal[] = { 413 0, /* service */ 414 11, /* type */ 415 17, /* private */ 416 25, /* unpriv */ 417 33, /* chroot */ 418 41, /* wakeup */ 419 49, /* maxproc */ 420 57, /* command */ 421 }; 422 423#define ADD_TEXT(text, len) do { \ 424 vstream_fputs(text, fp); line_len += len; } \ 425 while (0) 426#define ADD_SPACE ADD_TEXT(" ", 1) 427 428 /* 429 * Show the standard fields at their preferred column position. Use at 430 * least one-space column separation. 431 */ 432 for (line_len = 0, field = 0; field < PCF_MASTER_MIN_FIELDS; field++) { 433 arg = argv[field]; 434 if (line_len > 0) { 435 do { 436 ADD_SPACE; 437 } while (line_len < column_goal[field]); 438 } 439 ADD_TEXT(arg, strlen(arg)); 440 } 441 442 /* 443 * Format the daemon command-line options and non-option arguments. Here, 444 * we have no data-dependent preference for column positions, but we do 445 * have argument grouping preferences. 446 */ 447 in_daemon_options = 1; 448 for ( /* void */ ; (arg = argv[field]) != 0; field++) { 449 arg_len = strlen(arg); 450 aval = 0; 451 if (in_daemon_options) { 452 453 /* 454 * Try to show the generic options (-v -D) on the first line, and 455 * non-options on a later line. 456 */ 457 if (arg[0] != '-' || strcmp(arg, "--") == 0) { 458 in_daemon_options = 0; 459#if 0 460 if (mode & PCF_FOLD_LINE) 461 /* Force line wrap. */ 462 line_len = PCF_LINE_LIMIT; 463#endif 464 } 465 466 /* 467 * Special processing for options that require a value. 468 */ 469 else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0 470 && (aval = argv[field + 1]) != 0) { 471 472 /* Force line wrap before option with value. */ 473 line_len = PCF_LINE_LIMIT; 474 475 /* 476 * Optionally, expand $name in parameter value. 477 */ 478 if (strcmp(arg, "-o") == 0 479 && (mode & PCF_SHOW_EVAL) != 0) 480 aval = pcf_expand_parameter_value((VSTRING *) 0, mode, 481 aval, masterp); 482 483 /* 484 * Keep option and value on the same line. 485 */ 486 arg_len += strlen(aval) + 1; 487 } 488 } 489 490 /* 491 * Insert a line break when the next item won't fit. 492 */ 493 if (line_len > PCF_INDENT_LEN) { 494 if ((mode & PCF_FOLD_LINE) == 0 495 || line_len + 1 + arg_len < PCF_LINE_LIMIT) { 496 ADD_SPACE; 497 } else { 498 vstream_fputs("\n" PCF_INDENT_TEXT, fp); 499 line_len = PCF_INDENT_LEN; 500 } 501 } 502 ADD_TEXT(arg, strlen(arg)); 503 if (aval) { 504 ADD_SPACE; 505 ADD_TEXT(aval, strlen(aval)); 506 field += 1; 507 508 /* Force line wrap after option with value. */ 509 line_len = PCF_LINE_LIMIT; 510 511 } 512 } 513 vstream_fputs("\n", fp); 514 515 if (msg_verbose) 516 vstream_fflush(fp); 517} 518 519/* pcf_show_master_entries - show master.cf entries */ 520 521void pcf_show_master_entries(VSTREAM *fp, int mode, int argc, char **argv) 522{ 523 PCF_MASTER_ENT *masterp; 524 PCF_MASTER_FLD_REQ *field_reqs; 525 PCF_MASTER_FLD_REQ *req; 526 527 /* 528 * Parse the filter expressions. 529 */ 530 if (argc > 0) { 531 field_reqs = (PCF_MASTER_FLD_REQ *) 532 mymalloc(sizeof(*field_reqs) * argc); 533 for (req = field_reqs; req < field_reqs + argc; req++) { 534 req->match_count = 0; 535 req->raw_text = *argv++; 536 req->service_pattern = 537 pcf_parse_service_pattern(req->raw_text, 1, 2); 538 if (req->service_pattern == 0) 539 msg_fatal("-M option requires service_name[/type]"); 540 } 541 } 542 543 /* 544 * Iterate over the master table. 545 */ 546 for (masterp = pcf_master_table; masterp->argv != 0; masterp++) { 547 if (argc > 0) { 548 for (req = field_reqs; req < field_reqs + argc; req++) { 549 if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, 550 masterp->argv->argv[0], 551 masterp->argv->argv[1])) { 552 req->match_count++; 553 pcf_print_master_entry(fp, mode, masterp); 554 } 555 } 556 } else { 557 pcf_print_master_entry(fp, mode, masterp); 558 } 559 } 560 561 /* 562 * Cleanup. 563 */ 564 if (argc > 0) { 565 for (req = field_reqs; req < field_reqs + argc; req++) { 566 if (req->match_count == 0) 567 msg_warn("unmatched request: \"%s\"", req->raw_text); 568 argv_free(req->service_pattern); 569 } 570 myfree((char *) field_reqs); 571 } 572} 573 574/* pcf_print_master_field - scaffolding */ 575 576static void pcf_print_master_field(VSTREAM *fp, int mode, 577 PCF_MASTER_ENT *masterp, 578 int field) 579{ 580 char **argv = masterp->argv->argv; 581 const char *arg; 582 const char *aval; 583 int arg_len; 584 int line_len; 585 int in_daemon_options; 586 587 /* 588 * Show the field value, or the first value in the case of a multi-column 589 * field. 590 */ 591#define ADD_CHAR(ch) ADD_TEXT((ch), 1) 592 593 line_len = 0; 594 if ((mode & PCF_HIDE_NAME) == 0) { 595 ADD_TEXT(argv[0], strlen(argv[0])); 596 ADD_CHAR(PCF_NAMESP_SEP_STR); 597 ADD_TEXT(argv[1], strlen(argv[1])); 598 ADD_CHAR(PCF_NAMESP_SEP_STR); 599 ADD_TEXT(pcf_str_field_pattern(field), strlen(pcf_str_field_pattern(field))); 600 ADD_TEXT(" = ", 3); 601 if (line_len + strlen(argv[field]) > PCF_LINE_LIMIT) { 602 vstream_fputs("\n" PCF_INDENT_TEXT, fp); 603 line_len = PCF_INDENT_LEN; 604 } 605 } 606 ADD_TEXT(argv[field], strlen(argv[field])); 607 608 /* 609 * Format the daemon command-line options and non-option arguments. Here, 610 * we have no data-dependent preference for column positions, but we do 611 * have argument grouping preferences. 612 */ 613 if (field == PCF_MASTER_FLD_CMD) { 614 in_daemon_options = 1; 615 for (field += 1; (arg = argv[field]) != 0; field++) { 616 arg_len = strlen(arg); 617 aval = 0; 618 if (in_daemon_options) { 619 620 /* 621 * We make no special case for generic options (-v -D) 622 * options. 623 */ 624 if (arg[0] != '-' || strcmp(arg, "--") == 0) { 625 in_daemon_options = 0; 626 } else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0 627 && (aval = argv[field + 1]) != 0) { 628 629 /* Force line break before option with value. */ 630 line_len = PCF_LINE_LIMIT; 631 632 /* 633 * Optionally, expand $name in parameter value. 634 */ 635 if (strcmp(arg, "-o") == 0 636 && (mode & PCF_SHOW_EVAL) != 0) 637 aval = pcf_expand_parameter_value((VSTRING *) 0, mode, 638 aval, masterp); 639 640 /* 641 * Keep option and value on the same line. 642 */ 643 arg_len += strlen(aval) + 1; 644 } 645 } 646 647 /* 648 * Insert a line break when the next item won't fit. 649 */ 650 if (line_len > PCF_INDENT_LEN) { 651 if ((mode & PCF_FOLD_LINE) == 0 652 || line_len + 1 + arg_len < PCF_LINE_LIMIT) { 653 ADD_SPACE; 654 } else { 655 vstream_fputs("\n" PCF_INDENT_TEXT, fp); 656 line_len = PCF_INDENT_LEN; 657 } 658 } 659 ADD_TEXT(arg, strlen(arg)); 660 if (aval) { 661 ADD_SPACE; 662 ADD_TEXT(aval, strlen(aval)); 663 field += 1; 664 665 /* Force line break after option with value. */ 666 line_len = PCF_LINE_LIMIT; 667 } 668 } 669 } 670 vstream_fputs("\n", fp); 671 672 if (msg_verbose) 673 vstream_fflush(fp); 674} 675 676/* pcf_show_master_fields - show master.cf fields */ 677 678void pcf_show_master_fields(VSTREAM *fp, int mode, int argc, char **argv) 679{ 680 const char *myname = "pcf_show_master_fields"; 681 PCF_MASTER_ENT *masterp; 682 PCF_MASTER_FLD_REQ *field_reqs; 683 PCF_MASTER_FLD_REQ *req; 684 int field; 685 686 /* 687 * Parse the filter expressions. 688 */ 689 if (argc > 0) { 690 field_reqs = (PCF_MASTER_FLD_REQ *) 691 mymalloc(sizeof(*field_reqs) * argc); 692 for (req = field_reqs; req < field_reqs + argc; req++) { 693 req->match_count = 0; 694 req->raw_text = *argv++; 695 req->service_pattern = 696 pcf_parse_service_pattern(req->raw_text, 1, 3); 697 if (req->service_pattern == 0) 698 msg_fatal("-F option requires service_name[/type[/field]]"); 699 field = req->field_pattern = 700 pcf_parse_field_pattern(req->service_pattern->argv[2]); 701 if (pcf_is_magic_field_pattern(field) == 0 702 && (field < 0 || field > PCF_MASTER_FLD_CMD)) 703 msg_panic("%s: bad attribute field index: %d", 704 myname, field); 705 } 706 } 707 708 /* 709 * Iterate over the master table. 710 */ 711 for (masterp = pcf_master_table; masterp->argv != 0; masterp++) { 712 if (argc > 0) { 713 for (req = field_reqs; req < field_reqs + argc; req++) { 714 if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, 715 masterp->argv->argv[0], 716 masterp->argv->argv[1])) { 717 req->match_count++; 718 field = req->field_pattern; 719 if (pcf_is_magic_field_pattern(field)) { 720 for (field = 0; field <= PCF_MASTER_FLD_CMD; field++) 721 pcf_print_master_field(fp, mode, masterp, field); 722 } else { 723 pcf_print_master_field(fp, mode, masterp, field); 724 } 725 } 726 } 727 } else { 728 for (field = 0; field <= PCF_MASTER_FLD_CMD; field++) 729 pcf_print_master_field(fp, mode, masterp, field); 730 } 731 } 732 733 /* 734 * Cleanup. 735 */ 736 if (argc > 0) { 737 for (req = field_reqs; req < field_reqs + argc; req++) { 738 if (req->match_count == 0) 739 msg_warn("unmatched request: \"%s\"", req->raw_text); 740 argv_free(req->service_pattern); 741 } 742 myfree((char *) field_reqs); 743 } 744} 745 746/* pcf_edit_master_field - replace master.cf field value. */ 747 748void pcf_edit_master_field(PCF_MASTER_ENT *masterp, int field, 749 const char *new_value) 750{ 751 752 /* 753 * Replace multi-column attribute. 754 */ 755 if (field == PCF_MASTER_FLD_CMD) { 756 argv_truncate(masterp->argv, PCF_MASTER_FLD_CMD); 757 argv_split_append(masterp->argv, new_value, PCF_MASTER_BLANKS); 758 } 759 760 /* 761 * Replace single-column attribute. 762 */ 763 else { 764 argv_replace_one(masterp->argv, field, new_value); 765 } 766 767 /* 768 * Do per-field sanity checks. 769 */ 770 pcf_check_master_entry(masterp->argv, new_value); 771} 772 773/* pcf_print_master_param - scaffolding */ 774 775static void pcf_print_master_param(VSTREAM *fp, int mode, 776 PCF_MASTER_ENT *masterp, 777 const char *param_name, 778 const char *param_value) 779{ 780 if ((mode & PCF_SHOW_EVAL) != 0) 781 param_value = pcf_expand_parameter_value((VSTRING *) 0, mode, 782 param_value, masterp); 783 if ((mode & PCF_HIDE_NAME) == 0) { 784 pcf_print_line(fp, mode, "%s%c%s = %s\n", 785 masterp->name_space, PCF_NAMESP_SEP_CH, 786 param_name, param_value); 787 } else { 788 pcf_print_line(fp, mode, "%s\n", param_value); 789 } 790 if (msg_verbose) 791 vstream_fflush(fp); 792} 793 794/* pcf_sort_argv_cb - sort argv call-back */ 795 796static int pcf_sort_argv_cb(const void *a, const void *b) 797{ 798 return (strcmp(*(char **) a, *(char **) b)); 799} 800 801/* pcf_show_master_any_param - show any parameter in master.cf service entry */ 802 803static void pcf_show_master_any_param(VSTREAM *fp, int mode, 804 PCF_MASTER_ENT *masterp) 805{ 806 const char *myname = "pcf_show_master_any_param"; 807 ARGV *argv = argv_alloc(10); 808 DICT *dict = masterp->all_params; 809 const char *param_name; 810 const char *param_value; 811 int param_count = 0; 812 int how; 813 char **cpp; 814 815 /* 816 * Print parameters in sorted order. The number of parameters per 817 * master.cf entry is small, so we optmiize for code simplicity and don't 818 * worry about the cost of double lookup. 819 */ 820 821 /* Look up the parameter names and ignore the values. */ 822 823 for (how = DICT_SEQ_FUN_FIRST; 824 dict->sequence(dict, how, ¶m_name, ¶m_value) == 0; 825 how = DICT_SEQ_FUN_NEXT) { 826 argv_add(argv, param_name, ARGV_END); 827 param_count++; 828 } 829 830 /* Print the parameters in sorted order. */ 831 832 qsort(argv->argv, param_count, sizeof(argv->argv[0]), pcf_sort_argv_cb); 833 for (cpp = argv->argv; (param_name = *cpp) != 0; cpp++) { 834 if ((param_value = dict_get(dict, param_name)) == 0) 835 msg_panic("%s: parameter name not found: %s", myname, param_name); 836 pcf_print_master_param(fp, mode, masterp, param_name, param_value); 837 } 838 839 /* 840 * Clean up. 841 */ 842 argv_free(argv); 843} 844 845/* pcf_show_master_params - show master.cf params */ 846 847void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv) 848{ 849 PCF_MASTER_ENT *masterp; 850 PCF_MASTER_FLD_REQ *field_reqs; 851 PCF_MASTER_FLD_REQ *req; 852 DICT *dict; 853 const char *param_value; 854 855 /* 856 * Parse the filter expressions. 857 */ 858 if (argc > 0) { 859 field_reqs = (PCF_MASTER_FLD_REQ *) 860 mymalloc(sizeof(*field_reqs) * argc); 861 for (req = field_reqs; req < field_reqs + argc; req++) { 862 req->match_count = 0; 863 req->raw_text = *argv++; 864 req->service_pattern = 865 pcf_parse_service_pattern(req->raw_text, 1, 3); 866 if (req->service_pattern == 0) 867 msg_fatal("-P option requires service_name[/type[/parameter]]"); 868 req->param_pattern = req->service_pattern->argv[2]; 869 } 870 } 871 872 /* 873 * Iterate over the master table. 874 */ 875 for (masterp = pcf_master_table; masterp->argv != 0; masterp++) { 876 if ((dict = masterp->all_params) != 0) { 877 if (argc > 0) { 878 for (req = field_reqs; req < field_reqs + argc; req++) { 879 if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, 880 masterp->argv->argv[0], 881 masterp->argv->argv[1])) { 882 if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) { 883 pcf_show_master_any_param(fp, mode, masterp); 884 req->match_count += 1; 885 } else if ((param_value = dict_get(dict, 886 req->param_pattern)) != 0) { 887 pcf_print_master_param(fp, mode, masterp, 888 req->param_pattern, 889 param_value); 890 req->match_count += 1; 891 } 892 } 893 } 894 } else { 895 pcf_show_master_any_param(fp, mode, masterp); 896 } 897 } 898 } 899 900 /* 901 * Cleanup. 902 */ 903 if (argc > 0) { 904 for (req = field_reqs; req < field_reqs + argc; req++) { 905 if (req->match_count == 0) 906 msg_warn("unmatched request: \"%s\"", req->raw_text); 907 argv_free(req->service_pattern); 908 } 909 myfree((char *) field_reqs); 910 } 911} 912 913/* pcf_edit_master_param - update, add or remove -o parameter=value */ 914 915void pcf_edit_master_param(PCF_MASTER_ENT *masterp, int mode, 916 const char *param_name, 917 const char *param_value) 918{ 919 const char *myname = "pcf_edit_master_param"; 920 ARGV *argv = masterp->argv; 921 const char *arg; 922 const char *aval; 923 int param_match = 0; 924 int name_len = strlen(param_name); 925 int field; 926 927 for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) { 928 arg = argv->argv[field]; 929 930 /* 931 * Stop at the first non-option argument or end-of-list. 932 */ 933 if (arg[0] != '-' || strcmp(arg, "--") == 0) { 934 break; 935 } 936 937 /* 938 * Zoom in on command-line options with a value. 939 */ 940 else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0 941 && (aval = argv->argv[field + 1]) != 0) { 942 943 /* 944 * Zoom in on "-o parameter=value". 945 */ 946 if (strcmp(arg, "-o") == 0) { 947 if (strncmp(aval, param_name, name_len) == 0 948 && aval[name_len] == '=') { 949 param_match = 1; 950 switch (mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL)) { 951 952 /* 953 * Update parameter=value. 954 */ 955 case PCF_EDIT_CONF: 956 aval = concatenate(param_name, "=", 957 param_value, (char *) 0); 958 argv_replace_one(argv, field + 1, aval); 959 myfree((char *) aval); 960 if (masterp->all_params) 961 dict_put(masterp->all_params, param_name, param_value); 962 /* XXX Update parameter "used/defined" status. */ 963 break; 964 965 /* 966 * Delete parameter=value. 967 */ 968 case PCF_EDIT_EXCL: 969 argv_delete(argv, field, 2); 970 if (masterp->all_params) 971 dict_del(masterp->all_params, param_name); 972 /* XXX Update parameter "used/defined" status. */ 973 field -= 2; 974 break; 975 default: 976 msg_panic("%s: unexpected mode: %d", myname, mode); 977 } 978 } 979 } 980 981 /* 982 * Skip over the command-line option value. 983 */ 984 field += 1; 985 } 986 } 987 988 /* 989 * Add unmatched parameter. 990 */ 991 if ((mode & PCF_EDIT_CONF) && param_match == 0) { 992 /* XXX Generalize: argv_insert(argv, where, list...) */ 993 argv_insert_one(argv, field, "-o"); 994 aval = concatenate(param_name, "=", 995 param_value, (char *) 0); 996 argv_insert_one(argv, field + 1, aval); 997 if (masterp->all_params) 998 dict_put(masterp->all_params, param_name, param_value); 999 /* XXX May affect parameter "used/defined" status. */ 1000 myfree((char *) aval); 1001 param_match = 1; 1002 } 1003} 1004