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