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