1/*++ 2/* NAME 3/* master_ent 3 4/* SUMMARY 5/* Postfix master - config file access 6/* SYNOPSIS 7/* #include "master.h" 8/* 9/* void fset_master_ent(path) 10/* char *path; 11/* 12/* void set_master_ent() 13/* 14/* MASTER_SERV *get_master_ent() 15/* 16/* void end_master_ent() 17/* 18/* void print_master_ent(entry) 19/* MASTER_SERV *entry; 20/* 21/* void free_master_ent(entry) 22/* MASTER_SERV *entry; 23/* DESCRIPTION 24/* This module implements a simple programmatic interface 25/* for accessing Postfix master process configuration files. 26/* 27/* fset_master_ent() specifies the location of the master process 28/* configuration file. The pathname is copied. 29/* 30/* set_master_ent() opens the configuration file. It is an error 31/* to call this routine while the configuration file is still open. 32/* It is an error to open a configuration file without specifying 33/* its name to fset_master_ent(). 34/* 35/* get_master_ent() reads the next entry from an open configuration 36/* file and returns the parsed result. A null result means the end 37/* of file was reached. 38/* 39/* print_master_ent() prints the specified service entry. 40/* 41/* end_master_ent() closes an open configuration file. It is an error 42/* to call this routine when the configuration file is not open. 43/* 44/* free_master_ent() destroys the memory used for a parsed configuration 45/* file entry. 46/* DIAGNOSTICS 47/* Panics: interface violations. Fatal errors: memory allocation 48/* failure. 49/* BUGS 50/* SEE ALSO 51/* LICENSE 52/* .ad 53/* .fi 54/* The Secure Mailer license must be distributed with this software. 55/* AUTHOR(S) 56/* Wietse Venema 57/* IBM T.J. Watson Research 58/* P.O. Box 704 59/* Yorktown Heights, NY 10598, USA 60/*--*/ 61 62/* System libraries. */ 63 64#include <sys_defs.h> 65#include <netinet/in.h> 66#include <stdarg.h> 67#include <string.h> 68#include <stdlib.h> 69#include <unistd.h> 70#include <ctype.h> 71#include <fcntl.h> 72 73#ifdef STRCASECMP_IN_STRINGS_H 74#include <strings.h> 75#endif 76 77/* Utility libraries. */ 78 79#include <msg.h> 80#include <mymalloc.h> 81#include <vstring.h> 82#include <vstream.h> 83#include <argv.h> 84#include <stringops.h> 85#include <readlline.h> 86#include <inet_addr_list.h> 87#include <host_port.h> 88#include <inet_addr_host.h> 89#include <sock_addr.h> 90#include <inet_proto.h> 91 92/* Global library. */ 93 94#include <match_service.h> 95#include <mail_proto.h> 96#include <mail_params.h> 97#include <own_inet_addr.h> 98#include <wildcard_inet_addr.h> 99#include <mail_conf.h> 100 101/* Local stuff. */ 102 103#include "master_proto.h" 104#include "master.h" 105 106static char *master_path; /* config file name */ 107static VSTREAM *master_fp; /* config file pointer */ 108static int master_line; /* config file line number */ 109static ARGV *master_disable; /* disabled service patterns */ 110 111static char master_blanks[] = " \t\r\n";/* field delimiters */ 112 113static NORETURN fatal_invalid_field(char *, char *); 114static NORETURN fatal_with_context(char *,...); 115 116/* fset_master_ent - specify configuration file pathname */ 117 118void fset_master_ent(char *path) 119{ 120 if (master_path != 0) 121 myfree(master_path); 122 master_path = mystrdup(path); 123} 124 125/* set_master_ent - open configuration file */ 126 127void set_master_ent() 128{ 129 const char *myname = "set_master_ent"; 130 char *disable; 131 132 if (master_fp != 0) 133 msg_panic("%s: configuration file still open", myname); 134 if (master_path == 0) 135 msg_panic("%s: no configuration file specified", myname); 136 if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0) 137 msg_fatal("open %s: %m", master_path); 138 master_line = 0; 139 if (master_disable != 0) 140 msg_panic("%s: service disable list still exists", myname); 141 if (inet_proto_info()->ai_family_list[0] == 0) { 142 msg_warn("all network protocols are disabled (%s = %s)", 143 VAR_INET_PROTOCOLS, var_inet_protocols); 144 msg_warn("disabling all type \"inet\" services in master.cf"); 145 disable = concatenate(MASTER_XPORT_NAME_INET, ",", 146 var_master_disable, (char *) 0); 147 master_disable = match_service_init(disable); 148 myfree(disable); 149 } else 150 master_disable = match_service_init(var_master_disable); 151} 152 153/* end_master_ent - close configuration file */ 154 155void end_master_ent() 156{ 157 const char *myname = "end_master_ent"; 158 159 if (master_fp == 0) 160 msg_panic("%s: configuration file not open", myname); 161 if (vstream_fclose(master_fp) != 0) 162 msg_fatal("%s: close configuration file: %m", myname); 163 master_fp = 0; 164 if (master_disable == 0) 165 msg_panic("%s: no service disable list", myname); 166 match_service_free(master_disable); 167 master_disable = 0; 168} 169 170/* fatal_with_context - print fatal error with file/line context */ 171 172static NORETURN fatal_with_context(char *format,...) 173{ 174 const char *myname = "fatal_with_context"; 175 VSTRING *vp = vstring_alloc(100); 176 va_list ap; 177 178 if (master_path == 0) 179 msg_panic("%s: no configuration file specified", myname); 180 181 va_start(ap, format); 182 vstring_vsprintf(vp, format, ap); 183 va_end(ap); 184 msg_fatal("%s: line %d: %s", master_path, master_line, vstring_str(vp)); 185} 186 187/* fatal_invalid_field - report invalid field value */ 188 189static NORETURN fatal_invalid_field(char *name, char *value) 190{ 191 fatal_with_context("field \"%s\": bad value: \"%s\"", name, value); 192} 193 194/* get_str_ent - extract string field */ 195 196static char *get_str_ent(char **bufp, char *name, char *def_val) 197{ 198 char *value; 199 200 if ((value = mystrtok(bufp, master_blanks)) == 0) 201 fatal_with_context("missing \"%s\" field", name); 202 if (strcmp(value, "-") == 0) { 203 if (def_val == 0) 204 fatal_with_context("field \"%s\" has no default value", name); 205 return (def_val); 206 } else { 207 return (value); 208 } 209} 210 211/* get_bool_ent - extract boolean field */ 212 213static int get_bool_ent(char **bufp, char *name, char *def_val) 214{ 215 char *value; 216 217 value = get_str_ent(bufp, name, def_val); 218 if (strcmp("y", value) == 0) { 219 return (1); 220 } else if (strcmp("n", value) == 0) { 221 return (0); 222 } else { 223 fatal_invalid_field(name, value); 224 } 225 /* NOTREACHED */ 226} 227 228/* get_int_ent - extract integer field */ 229 230static int get_int_ent(char **bufp, char *name, char *def_val, int min_val) 231{ 232 char *value; 233 int n; 234 235 value = get_str_ent(bufp, name, def_val); 236 if (!ISDIGIT(*value) || (n = atoi(value)) < min_val) 237 fatal_invalid_field(name, value); 238 return (n); 239} 240 241/* get_master_ent - read entry from configuration file */ 242 243MASTER_SERV *get_master_ent() 244{ 245 VSTRING *buf = vstring_alloc(100); 246 VSTRING *junk = vstring_alloc(100); 247 MASTER_SERV *serv; 248 char *cp; 249 char *name; 250 char *host = 0; 251 char *port = 0; 252 char *transport; 253 int private; 254 int unprivileged; /* passed on to child */ 255 int chroot; /* passed on to child */ 256 char *command; 257 int n; 258 char *bufp; 259 char *atmp; 260 const char *parse_err; 261 static char *saved_interfaces = 0; 262 263 if (master_fp == 0) 264 msg_panic("get_master_ent: config file not open"); 265 if (master_disable == 0) 266 msg_panic("get_master_ent: no service disable list"); 267 268 /* 269 * XXX We cannot change the inet_interfaces setting for a running master 270 * process. Listening sockets are inherited by child processes so that 271 * closing and reopening those sockets in the master does not work. 272 * 273 * Another problem is that library routines still cache results that are 274 * based on the old inet_interfaces setting. It is too much trouble to 275 * recompute everything. 276 * 277 * In order to keep our data structures consistent we ignore changes in 278 * inet_interfaces settings, and issue a warning instead. 279 */ 280 if (saved_interfaces == 0) 281 saved_interfaces = mystrdup(var_inet_interfaces); 282 283 /* 284 * Skip blank lines and comment lines. 285 */ 286 for (;;) { 287 if (readlline(buf, master_fp, &master_line) == 0) { 288 vstring_free(buf); 289 vstring_free(junk); 290 return (0); 291 } 292 bufp = vstring_str(buf); 293 if ((cp = mystrtok(&bufp, master_blanks)) == 0) 294 continue; 295 name = cp; 296 transport = get_str_ent(&bufp, "transport type", (char *) 0); 297 vstring_sprintf(junk, "%s/%s", name, transport); 298 if (match_service_match(master_disable, vstring_str(junk)) == 0) 299 break; 300 } 301 302 /* 303 * Parse one logical line from the configuration file. Initialize service 304 * structure members in order. 305 */ 306 serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV)); 307 serv->next = 0; 308 309 /* 310 * Flags member. 311 */ 312 serv->flags = 0; 313 314 /* 315 * All servers busy warning timer. 316 */ 317 serv->busy_warn_time = 0; 318 319 /* 320 * Service name. Syntax is transport-specific. 321 */ 322 serv->ext_name = mystrdup(name); 323 324 /* 325 * Transport type: inet (wild-card listen or virtual) or unix. 326 */ 327#define STR_SAME !strcmp 328 329 if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) { 330 if (!STR_SAME(saved_interfaces, var_inet_interfaces)) { 331 msg_warn("service %s: ignoring %s change", 332 serv->ext_name, VAR_INET_INTERFACES); 333 msg_warn("to change %s, stop and start Postfix", 334 VAR_INET_INTERFACES); 335 } 336 serv->type = MASTER_SERV_TYPE_INET; 337 atmp = mystrdup(name); 338 if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0) 339 msg_fatal("%s: line %d: %s in \"%s\"", 340 VSTREAM_PATH(master_fp), master_line, 341 parse_err, name); 342 if (*host) { 343 serv->flags |= MASTER_FLAG_INETHOST;/* host:port */ 344 MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *) 345 mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv))); 346 inet_addr_list_init(MASTER_INET_ADDRLIST(serv)); 347 if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0) 348 msg_fatal("%s: line %d: bad hostname or network address: %s", 349 VSTREAM_PATH(master_fp), master_line, name); 350 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); 351 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; 352 } else { 353 MASTER_INET_ADDRLIST(serv) = 354 strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ? 355 own_inet_addr_list() : /* virtual */ 356 wildcard_inet_addr_list(); /* wild-card */ 357 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); 358 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; 359 } 360 MASTER_INET_PORT(serv) = mystrdup(port); 361 for (n = 0; /* see below */ ; n++) { 362 if (n >= MASTER_INET_ADDRLIST(serv)->used) { 363 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 364 break; 365 } 366 if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n))) 367 break; 368 } 369 } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) { 370 serv->type = MASTER_SERV_TYPE_UNIX; 371 serv->listen_fd_count = 1; 372 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 373 } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) { 374 serv->type = MASTER_SERV_TYPE_FIFO; 375 serv->listen_fd_count = 1; 376 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 377#ifdef MASTER_SERV_TYPE_PASS 378 } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) { 379 serv->type = MASTER_SERV_TYPE_PASS; 380 serv->listen_fd_count = 1; 381 /* If this is a connection screener, remote clients are likely. */ 382#endif 383 } else { 384 fatal_with_context("bad transport type: %s", transport); 385 } 386 387 /* 388 * Service class: public or private. 389 */ 390 private = get_bool_ent(&bufp, "private", "y"); 391 392 /* 393 * Derive an internal service name. The name may depend on service 394 * attributes such as privacy. 395 */ 396 if (serv->type == MASTER_SERV_TYPE_INET) { 397 MAI_HOSTADDR_STR host_addr; 398 MAI_SERVPORT_STR serv_port; 399 struct addrinfo *res0; 400 401 if (private) 402 fatal_with_context("inet service cannot be private"); 403 404 /* 405 * Canonicalize endpoint names so that we correctly handle "reload" 406 * requests after someone changes "25" into "smtp" or vice versa. 407 */ 408 if (*host == 0) 409 host = 0; 410 /* Canonicalize numeric host and numeric or symbolic service. */ 411 if (hostaddr_to_sockaddr(host, port, 0, &res0) == 0) { 412 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen, 413 host ? &host_addr : (MAI_HOSTADDR_STR *) 0, 414 &serv_port, 0); 415 serv->name = (host ? concatenate("[", host_addr.buf, "]:", 416 serv_port.buf, (char *) 0) : 417 mystrdup(serv_port.buf)); 418 freeaddrinfo(res0); 419 } 420 /* Canonicalize numeric or symbolic service. */ 421 else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) { 422 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen, 423 (MAI_HOSTADDR_STR *) 0, &serv_port, 0); 424 serv->name = (host ? concatenate("[", host, "]:", 425 serv_port.buf, (char *) 0) : 426 mystrdup(serv_port.buf)); 427 freeaddrinfo(res0); 428 } 429 /* Bad service name? */ 430 else 431 serv->name = mystrdup(name); 432 myfree(atmp); 433 } else if (serv->type == MASTER_SERV_TYPE_UNIX) { 434 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 435 MAIL_CLASS_PUBLIC, name); 436 } else if (serv->type == MASTER_SERV_TYPE_FIFO) { 437 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 438 MAIL_CLASS_PUBLIC, name); 439#ifdef MASTER_SERV_TYPE_PASS 440 } else if (serv->type == MASTER_SERV_TYPE_PASS) { 441 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 442 MAIL_CLASS_PUBLIC, name); 443#endif 444 } else { 445 msg_panic("bad transport type: %d", serv->type); 446 } 447 448 /* 449 * Listen socket(s). XXX We pre-allocate storage because the number of 450 * sockets is frozen anyway once we build the command-line vector below. 451 */ 452 if (serv->listen_fd_count == 0) { 453 msg_fatal("%s: line %d: no valid IP address found: %s", 454 VSTREAM_PATH(master_fp), master_line, name); 455 } 456 serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count); 457 for (n = 0; n < serv->listen_fd_count; n++) 458 serv->listen_fd[n] = -1; 459 460 /* 461 * Privilege level. Default is to restrict process privileges to those of 462 * the mail owner. 463 */ 464 unprivileged = get_bool_ent(&bufp, "unprivileged", "y"); 465 466 /* 467 * Chroot. Default is to restrict file system access to the mail queue. 468 * XXX Chroot cannot imply unprivileged service (for example, the pickup 469 * service runs chrooted but needs privileges to open files as the user). 470 */ 471 chroot = get_bool_ent(&bufp, "chroot", "y"); 472 473 /* 474 * Wakeup timer. XXX should we require that var_proc_limit == 1? Right 475 * now, the only services that have a wakeup timer also happen to be the 476 * services that have at most one running instance: local pickup and 477 * local delivery. 478 */ 479 serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0); 480 481 /* 482 * Find out if the wakeup time is conditional, i.e., wakeup triggers 483 * should not be sent until the service has actually been used. 484 */ 485 if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1] == '?') 486 serv->flags |= MASTER_FLAG_CONDWAKE; 487 488 /* 489 * Concurrency limit. Zero means no limit. 490 */ 491 vstring_sprintf(junk, "%d", var_proc_limit); 492 serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0); 493 494 /* 495 * Path to command, 496 */ 497 command = get_str_ent(&bufp, "command", (char *) 0); 498 serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0); 499 500 /* 501 * Idle and total process count. 502 */ 503 serv->avail_proc = 0; 504 serv->total_proc = 0; 505 506 /* 507 * Backoff time in case a service is broken. 508 */ 509 serv->throttle_delay = var_throttle_time; 510 511 /* 512 * Shared channel for child status updates. 513 */ 514 serv->status_fd[0] = serv->status_fd[1] = -1; 515 516 /* 517 * Child process structures. 518 */ 519 serv->children = 0; 520 521 /* 522 * Command-line vector. Add "-n service_name" when the process name 523 * basename differs from the service name. Always add the transport. 524 */ 525 serv->args = argv_alloc(0); 526 argv_add(serv->args, command, (char *) 0); 527 if (serv->max_proc == 1) 528 argv_add(serv->args, "-l", (char *) 0); 529 if (serv->max_proc == 0) 530 argv_add(serv->args, "-z", (char *) 0); 531 if (strcmp(basename(command), name) != 0) 532 argv_add(serv->args, "-n", name, (char *) 0); 533 argv_add(serv->args, "-t", transport, (char *) 0); 534 if (master_detach == 0) 535 argv_add(serv->args, "-d", (char *) 0); 536 if (msg_verbose) 537 argv_add(serv->args, "-v", (char *) 0); 538 if (unprivileged) 539 argv_add(serv->args, "-u", (char *) 0); 540 if (chroot) 541 argv_add(serv->args, "-c", (char *) 0); 542 if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0 && serv->max_proc > 1) { 543 argv_add(serv->args, "-o", "stress=" CONFIG_BOOL_YES, (char *) 0); 544 serv->stress_param_val = 545 serv->args->argv[serv->args->argc - 1] + sizeof("stress=") - 1; 546 serv->stress_param_val[0] = 0; 547 } else 548 serv->stress_param_val = 0; 549 serv->stress_expire_time = 0; 550 if (serv->listen_fd_count > 1) 551 argv_add(serv->args, "-s", 552 vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)), 553 (char *) 0); 554 while ((cp = mystrtok(&bufp, master_blanks)) != 0) 555 argv_add(serv->args, cp, (char *) 0); 556 argv_terminate(serv->args); 557 558 /* 559 * Cleanup. 560 */ 561 vstring_free(buf); 562 vstring_free(junk); 563 return (serv); 564} 565 566/* print_master_ent - show service entry contents */ 567 568void print_master_ent(MASTER_SERV *serv) 569{ 570 char **cpp; 571 572 msg_info("====start service entry"); 573 msg_info("flags: %d", serv->flags); 574 msg_info("name: %s", serv->name); 575 msg_info("type: %s", 576 serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX : 577 serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO : 578 serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET : 579#ifdef MASTER_SERV_TYPE_PASS 580 serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS : 581#endif 582 "unknown transport type"); 583 msg_info("listen_fd_count: %d", serv->listen_fd_count); 584 msg_info("wakeup: %d", serv->wakeup_time); 585 msg_info("max_proc: %d", serv->max_proc); 586 msg_info("path: %s", serv->path); 587 for (cpp = serv->args->argv; *cpp; cpp++) 588 msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp); 589 msg_info("avail_proc: %d", serv->avail_proc); 590 msg_info("total_proc: %d", serv->total_proc); 591 msg_info("throttle_delay: %d", serv->throttle_delay); 592 msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]); 593 msg_info("children: 0x%lx", (long) serv->children); 594 msg_info("next: 0x%lx", (long) serv->next); 595 msg_info("====end service entry"); 596} 597 598/* free_master_ent - destroy process entry */ 599 600void free_master_ent(MASTER_SERV *serv) 601{ 602 603 /* 604 * Undo what get_master_ent() created. 605 */ 606 if (serv->flags & MASTER_FLAG_INETHOST) { 607 inet_addr_list_free(MASTER_INET_ADDRLIST(serv)); 608 myfree((char *) MASTER_INET_ADDRLIST(serv)); 609 } 610 if (serv->type == MASTER_SERV_TYPE_INET) 611 myfree(MASTER_INET_PORT(serv)); 612 myfree(serv->ext_name); 613 myfree(serv->name); 614 myfree(serv->path); 615 argv_free(serv->args); 616 myfree((char *) serv->listen_fd); 617 myfree((char *) serv); 618} 619