1 /* $NetBSD: master_ent.c,v 1.1.1.2 2010/06/17 18:06:54 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 do { 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 } while (match_service_match(master_disable, vstring_str(junk)) != 0); 290 291 /* 292 * Parse one logical line from the configuration file. Initialize service 293 * structure members in order. 294 */ 295 serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV)); 296 serv->next = 0; 297 298 /* 299 * Flags member. 300 */ 301 serv->flags = 0; 302 303 /* 304 * All servers busy warning timer. 305 */ 306 serv->busy_warn_time = 0; 307 308 /* 309 * Service name. Syntax is transport-specific. 310 */ 311 serv->ext_name = mystrdup(name); 312 313 /* 314 * Transport type: inet (wild-card listen or virtual) or unix. 315 */ 316 #define STR_SAME !strcmp 317 318 if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) { 319 if (!STR_SAME(saved_interfaces, var_inet_interfaces)) { 320 msg_warn("service %s: ignoring %s change", 321 serv->ext_name, VAR_INET_INTERFACES); 322 msg_warn("to change %s, stop and start Postfix", 323 VAR_INET_INTERFACES); 324 } 325 serv->type = MASTER_SERV_TYPE_INET; 326 atmp = mystrdup(name); 327 if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0) 328 msg_fatal("%s: line %d: %s in \"%s\"", 329 VSTREAM_PATH(master_fp), master_line, 330 parse_err, name); 331 if (*host) { 332 serv->flags |= MASTER_FLAG_INETHOST;/* host:port */ 333 MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *) 334 mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv))); 335 inet_addr_list_init(MASTER_INET_ADDRLIST(serv)); 336 if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0) 337 msg_fatal("%s: line %d: bad hostname or network address: %s", 338 VSTREAM_PATH(master_fp), master_line, name); 339 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); 340 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; 341 } else { 342 MASTER_INET_ADDRLIST(serv) = 343 strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ? 344 own_inet_addr_list() : /* virtual */ 345 wildcard_inet_addr_list(); /* wild-card */ 346 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv)); 347 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used; 348 } 349 MASTER_INET_PORT(serv) = mystrdup(port); 350 for (n = 0; /* see below */ ; n++) { 351 if (n >= MASTER_INET_ADDRLIST(serv)->used) { 352 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 353 break; 354 } 355 if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n))) 356 break; 357 } 358 } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) { 359 serv->type = MASTER_SERV_TYPE_UNIX; 360 serv->listen_fd_count = 1; 361 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 362 } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) { 363 serv->type = MASTER_SERV_TYPE_FIFO; 364 serv->listen_fd_count = 1; 365 serv->flags |= MASTER_FLAG_LOCAL_ONLY; 366 #ifdef MASTER_SERV_TYPE_PASS 367 } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) { 368 serv->type = MASTER_SERV_TYPE_PASS; 369 serv->listen_fd_count = 1; 370 /* If this is a connection screener, remote clients are likely. */ 371 #endif 372 } else { 373 fatal_with_context("bad transport type: %s", transport); 374 } 375 376 /* 377 * Service class: public or private. 378 */ 379 private = get_bool_ent(&bufp, "private", "y"); 380 381 /* 382 * Derive an internal service name. The name may depend on service 383 * attributes such as privacy. 384 */ 385 if (serv->type == MASTER_SERV_TYPE_INET) { 386 MAI_HOSTADDR_STR host_addr; 387 MAI_SERVPORT_STR serv_port; 388 struct addrinfo *res0; 389 390 if (private) 391 fatal_with_context("inet service cannot be private"); 392 393 /* 394 * Canonicalize endpoint names so that we correctly handle "reload" 395 * requests after someone changes "25" into "smtp" or vice versa. 396 */ 397 if (*host == 0) 398 host = 0; 399 /* Canonicalize numeric host and numeric or symbolic service. */ 400 if (hostaddr_to_sockaddr(host, port, 0, &res0) == 0) { 401 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen, 402 host ? &host_addr : (MAI_HOSTADDR_STR *) 0, 403 &serv_port, 0); 404 serv->name = (host ? concatenate("[", host_addr.buf, "]:", 405 serv_port.buf, (char *) 0) : 406 mystrdup(serv_port.buf)); 407 freeaddrinfo(res0); 408 } 409 /* Canonicalize numeric or symbolic service. */ 410 else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) { 411 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen, 412 (MAI_HOSTADDR_STR *) 0, &serv_port, 0); 413 serv->name = (host ? concatenate("[", host, "]:", 414 serv_port.buf, (char *) 0) : 415 mystrdup(serv_port.buf)); 416 freeaddrinfo(res0); 417 } 418 /* Bad service name? */ 419 else 420 serv->name = mystrdup(name); 421 myfree(atmp); 422 } else if (serv->type == MASTER_SERV_TYPE_UNIX) { 423 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 424 MAIL_CLASS_PUBLIC, name); 425 } else if (serv->type == MASTER_SERV_TYPE_FIFO) { 426 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 427 MAIL_CLASS_PUBLIC, name); 428 #ifdef MASTER_SERV_TYPE_PASS 429 } else if (serv->type == MASTER_SERV_TYPE_PASS) { 430 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE : 431 MAIL_CLASS_PUBLIC, name); 432 #endif 433 } else { 434 msg_panic("bad transport type: %d", serv->type); 435 } 436 437 /* 438 * Listen socket(s). XXX We pre-allocate storage because the number of 439 * sockets is frozen anyway once we build the command-line vector below. 440 */ 441 if (serv->listen_fd_count == 0) { 442 msg_fatal("%s: line %d: no valid IP address found: %s", 443 VSTREAM_PATH(master_fp), master_line, name); 444 } 445 serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count); 446 for (n = 0; n < serv->listen_fd_count; n++) 447 serv->listen_fd[n] = -1; 448 449 /* 450 * Privilege level. Default is to restrict process privileges to those of 451 * the mail owner. 452 */ 453 unprivileged = get_bool_ent(&bufp, "unprivileged", "y"); 454 455 /* 456 * Chroot. Default is to restrict file system access to the mail queue. 457 * XXX Chroot cannot imply unprivileged service (for example, the pickup 458 * service runs chrooted but needs privileges to open files as the user). 459 */ 460 chroot = get_bool_ent(&bufp, "chroot", "y"); 461 462 /* 463 * Wakeup timer. XXX should we require that var_proc_limit == 1? Right 464 * now, the only services that have a wakeup timer also happen to be the 465 * services that have at most one running instance: local pickup and 466 * local delivery. 467 */ 468 serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0); 469 470 /* 471 * Find out if the wakeup time is conditional, i.e., wakeup triggers 472 * should not be sent until the service has actually been used. 473 */ 474 if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1] == '?') 475 serv->flags |= MASTER_FLAG_CONDWAKE; 476 477 /* 478 * Concurrency limit. Zero means no limit. 479 */ 480 vstring_sprintf(junk, "%d", var_proc_limit); 481 serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0); 482 483 /* 484 * Path to command, 485 */ 486 command = get_str_ent(&bufp, "command", (char *) 0); 487 serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0); 488 489 /* 490 * Idle and total process count. 491 */ 492 serv->avail_proc = 0; 493 serv->total_proc = 0; 494 495 /* 496 * Backoff time in case a service is broken. 497 */ 498 serv->throttle_delay = var_throttle_time; 499 500 /* 501 * Shared channel for child status updates. 502 */ 503 serv->status_fd[0] = serv->status_fd[1] = -1; 504 505 /* 506 * Child process structures. 507 */ 508 serv->children = 0; 509 510 /* 511 * Command-line vector. Add "-n service_name" when the process name 512 * basename differs from the service name. Always add the transport. 513 */ 514 serv->args = argv_alloc(0); 515 argv_add(serv->args, command, (char *) 0); 516 if (serv->max_proc == 1) 517 argv_add(serv->args, "-l", (char *) 0); 518 if (serv->max_proc == 0) 519 argv_add(serv->args, "-z", (char *) 0); 520 if (strcmp(basename(command), name) != 0) 521 argv_add(serv->args, "-n", name, (char *) 0); 522 argv_add(serv->args, "-t", transport, (char *) 0); 523 if (master_detach == 0) 524 argv_add(serv->args, "-d", (char *) 0); 525 if (msg_verbose) 526 argv_add(serv->args, "-v", (char *) 0); 527 if (unprivileged) 528 argv_add(serv->args, "-u", (char *) 0); 529 if (chroot) 530 argv_add(serv->args, "-c", (char *) 0); 531 if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0 && serv->max_proc > 1) { 532 argv_add(serv->args, "-o", "stress=" CONFIG_BOOL_YES, (char *) 0); 533 serv->stress_param_val = 534 serv->args->argv[serv->args->argc - 1] + sizeof("stress=") - 1; 535 serv->stress_param_val[0] = 0; 536 } else 537 serv->stress_param_val = 0; 538 serv->stress_expire_time = 0; 539 if (serv->listen_fd_count > 1) 540 argv_add(serv->args, "-s", 541 vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)), 542 (char *) 0); 543 while ((cp = mystrtok(&bufp, master_blanks)) != 0) 544 argv_add(serv->args, cp, (char *) 0); 545 argv_terminate(serv->args); 546 547 /* 548 * Cleanup. 549 */ 550 vstring_free(buf); 551 vstring_free(junk); 552 return (serv); 553 } 554 555 /* print_master_ent - show service entry contents */ 556 557 void print_master_ent(MASTER_SERV *serv) 558 { 559 char **cpp; 560 561 msg_info("====start service entry"); 562 msg_info("flags: %d", serv->flags); 563 msg_info("name: %s", serv->name); 564 msg_info("type: %s", 565 serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX : 566 serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO : 567 serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET : 568 #ifdef MASTER_SERV_TYPE_PASS 569 serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS : 570 #endif 571 "unknown transport type"); 572 msg_info("listen_fd_count: %d", serv->listen_fd_count); 573 msg_info("wakeup: %d", serv->wakeup_time); 574 msg_info("max_proc: %d", serv->max_proc); 575 msg_info("path: %s", serv->path); 576 for (cpp = serv->args->argv; *cpp; cpp++) 577 msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp); 578 msg_info("avail_proc: %d", serv->avail_proc); 579 msg_info("total_proc: %d", serv->total_proc); 580 msg_info("throttle_delay: %d", serv->throttle_delay); 581 msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]); 582 msg_info("children: 0x%lx", (long) serv->children); 583 msg_info("next: 0x%lx", (long) serv->next); 584 msg_info("====end service entry"); 585 } 586 587 /* free_master_ent - destroy process entry */ 588 589 void free_master_ent(MASTER_SERV *serv) 590 { 591 592 /* 593 * Undo what get_master_ent() created. 594 */ 595 if (serv->flags & MASTER_FLAG_INETHOST) { 596 inet_addr_list_free(MASTER_INET_ADDRLIST(serv)); 597 myfree((char *) MASTER_INET_ADDRLIST(serv)); 598 } 599 if (serv->type == MASTER_SERV_TYPE_INET) 600 myfree(MASTER_INET_PORT(serv)); 601 myfree(serv->ext_name); 602 myfree(serv->name); 603 myfree(serv->path); 604 argv_free(serv->args); 605 myfree((char *) serv->listen_fd); 606 myfree((char *) serv); 607 } 608