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