1262266Sbapt/* 2289123Sbapt * Copyright (c) 2008-2014, Simon Schubert <2@0x2c.org>. 3262266Sbapt * Copyright (c) 2008 The DragonFly Project. All rights reserved. 4262266Sbapt * 5262266Sbapt * This code is derived from software contributed to The DragonFly Project 6289123Sbapt * by Simon Schubert <2@0x2c.org>. 7262266Sbapt * 8262266Sbapt * Redistribution and use in source and binary forms, with or without 9262266Sbapt * modification, are permitted provided that the following conditions 10262266Sbapt * are met: 11262266Sbapt * 12262266Sbapt * 1. Redistributions of source code must retain the above copyright 13262266Sbapt * notice, this list of conditions and the following disclaimer. 14262266Sbapt * 2. Redistributions in binary form must reproduce the above copyright 15262266Sbapt * notice, this list of conditions and the following disclaimer in 16262266Sbapt * the documentation and/or other materials provided with the 17262266Sbapt * distribution. 18262266Sbapt * 3. Neither the name of The DragonFly Project nor the names of its 19262266Sbapt * contributors may be used to endorse or promote products derived 20262266Sbapt * from this software without specific, prior written permission. 21262266Sbapt * 22262266Sbapt * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23262266Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24262266Sbapt * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25262266Sbapt * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26262266Sbapt * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27262266Sbapt * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28262266Sbapt * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29262266Sbapt * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30262266Sbapt * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31262266Sbapt * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32262266Sbapt * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33262266Sbapt * SUCH DAMAGE. 34262266Sbapt */ 35262266Sbapt 36262266Sbapt#include "dfcompat.h" 37262266Sbapt 38262266Sbapt#include <sys/param.h> 39262266Sbapt#include <sys/types.h> 40262266Sbapt#include <sys/queue.h> 41262266Sbapt#include <sys/stat.h> 42262266Sbapt#include <sys/time.h> 43262266Sbapt#include <sys/wait.h> 44262266Sbapt 45262266Sbapt#include <dirent.h> 46262266Sbapt#include <err.h> 47262266Sbapt#include <errno.h> 48262266Sbapt#include <fcntl.h> 49262266Sbapt#include <inttypes.h> 50315995Sbapt#include <libgen.h> 51262266Sbapt#include <paths.h> 52262266Sbapt#include <pwd.h> 53262266Sbapt#include <signal.h> 54262266Sbapt#include <stdarg.h> 55262266Sbapt#include <stdio.h> 56262266Sbapt#include <stdlib.h> 57262266Sbapt#include <string.h> 58262266Sbapt#include <syslog.h> 59262266Sbapt#include <unistd.h> 60262266Sbapt 61262266Sbapt#include "dma.h" 62262266Sbapt 63262266Sbapt 64262266Sbaptstatic void deliver(struct qitem *); 65262266Sbapt 66262266Sbaptstruct aliases aliases = LIST_HEAD_INITIALIZER(aliases); 67262266Sbaptstruct strlist tmpfs = SLIST_HEAD_INITIALIZER(tmpfs); 68262266Sbaptstruct authusers authusers = LIST_HEAD_INITIALIZER(authusers); 69262266Sbaptchar username[USERNAME_SIZE]; 70262266Sbaptuid_t useruid; 71262266Sbaptconst char *logident_base; 72262266Sbaptchar errmsg[ERRMSG_SIZE]; 73262266Sbapt 74262266Sbaptstatic int daemonize = 1; 75262266Sbaptstatic int doqueue = 0; 76262266Sbapt 77262266Sbaptstruct config config = { 78262266Sbapt .smarthost = NULL, 79262266Sbapt .port = 25, 80262266Sbapt .aliases = "/etc/aliases", 81262266Sbapt .spooldir = "/var/spool/dma", 82262266Sbapt .authpath = NULL, 83262266Sbapt .certfile = NULL, 84262266Sbapt .features = 0, 85262266Sbapt .mailname = NULL, 86262266Sbapt .masquerade_host = NULL, 87262266Sbapt .masquerade_user = NULL, 88262266Sbapt}; 89262266Sbapt 90262266Sbapt 91262266Sbaptstatic void 92262266Sbaptsighup_handler(int signo) 93262266Sbapt{ 94262266Sbapt (void)signo; /* so that gcc doesn't complain */ 95262266Sbapt} 96262266Sbapt 97262266Sbaptstatic char * 98262266Sbaptset_from(struct queue *queue, const char *osender) 99262266Sbapt{ 100262266Sbapt const char *addr; 101262266Sbapt char *sender; 102262266Sbapt 103262266Sbapt if (osender) { 104262266Sbapt addr = osender; 105262266Sbapt } else if (getenv("EMAIL") != NULL) { 106262266Sbapt addr = getenv("EMAIL"); 107262266Sbapt } else { 108262266Sbapt if (config.masquerade_user) 109262266Sbapt addr = config.masquerade_user; 110262266Sbapt else 111262266Sbapt addr = username; 112262266Sbapt } 113262266Sbapt 114262266Sbapt if (!strchr(addr, '@')) { 115262266Sbapt const char *from_host = hostname(); 116262266Sbapt 117262266Sbapt if (config.masquerade_host) 118262266Sbapt from_host = config.masquerade_host; 119262266Sbapt 120262266Sbapt if (asprintf(&sender, "%s@%s", addr, from_host) <= 0) 121262266Sbapt return (NULL); 122262266Sbapt } else { 123262266Sbapt sender = strdup(addr); 124262266Sbapt if (sender == NULL) 125262266Sbapt return (NULL); 126262266Sbapt } 127262266Sbapt 128262266Sbapt if (strchr(sender, '\n') != NULL) { 129262266Sbapt errno = EINVAL; 130262266Sbapt return (NULL); 131262266Sbapt } 132262266Sbapt 133262266Sbapt queue->sender = sender; 134262266Sbapt return (sender); 135262266Sbapt} 136262266Sbapt 137262266Sbaptstatic int 138262266Sbaptread_aliases(void) 139262266Sbapt{ 140262266Sbapt yyin = fopen(config.aliases, "r"); 141262266Sbapt if (yyin == NULL) { 142262266Sbapt /* 143262266Sbapt * Non-existing aliases file is not a fatal error 144262266Sbapt */ 145262266Sbapt if (errno == ENOENT) 146262266Sbapt return (0); 147262266Sbapt /* Other problems are. */ 148262266Sbapt return (-1); 149262266Sbapt } 150262266Sbapt if (yyparse()) 151262266Sbapt return (-1); /* fatal error, probably malloc() */ 152262266Sbapt fclose(yyin); 153262266Sbapt return (0); 154262266Sbapt} 155262266Sbapt 156262266Sbaptstatic int 157262266Sbaptdo_alias(struct queue *queue, const char *addr) 158262266Sbapt{ 159262266Sbapt struct alias *al; 160262266Sbapt struct stritem *sit; 161262266Sbapt int aliased = 0; 162262266Sbapt 163262266Sbapt LIST_FOREACH(al, &aliases, next) { 164262266Sbapt if (strcmp(al->alias, addr) != 0) 165262266Sbapt continue; 166262266Sbapt SLIST_FOREACH(sit, &al->dests, next) { 167262266Sbapt if (add_recp(queue, sit->str, EXPAND_ADDR) != 0) 168262266Sbapt return (-1); 169262266Sbapt } 170262266Sbapt aliased = 1; 171262266Sbapt } 172262266Sbapt 173262266Sbapt return (aliased); 174262266Sbapt} 175262266Sbapt 176262266Sbaptint 177262266Sbaptadd_recp(struct queue *queue, const char *str, int expand) 178262266Sbapt{ 179262266Sbapt struct qitem *it, *tit; 180262266Sbapt struct passwd *pw; 181262266Sbapt char *host; 182262266Sbapt int aliased = 0; 183262266Sbapt 184262266Sbapt it = calloc(1, sizeof(*it)); 185262266Sbapt if (it == NULL) 186262266Sbapt return (-1); 187262266Sbapt it->addr = strdup(str); 188262266Sbapt if (it->addr == NULL) 189262266Sbapt return (-1); 190262266Sbapt 191262266Sbapt it->sender = queue->sender; 192262266Sbapt host = strrchr(it->addr, '@'); 193262266Sbapt if (host != NULL && 194262266Sbapt (strcmp(host + 1, hostname()) == 0 || 195262266Sbapt strcmp(host + 1, "localhost") == 0)) { 196262266Sbapt *host = 0; 197262266Sbapt } 198262266Sbapt LIST_FOREACH(tit, &queue->queue, next) { 199262266Sbapt /* weed out duplicate dests */ 200262266Sbapt if (strcmp(tit->addr, it->addr) == 0) { 201262266Sbapt free(it->addr); 202262266Sbapt free(it); 203262266Sbapt return (0); 204262266Sbapt } 205262266Sbapt } 206262266Sbapt LIST_INSERT_HEAD(&queue->queue, it, next); 207262266Sbapt 208262266Sbapt /** 209262266Sbapt * Do local delivery if there is no @. 210262266Sbapt * Do not do local delivery when NULLCLIENT is set. 211262266Sbapt */ 212262266Sbapt if (strrchr(it->addr, '@') == NULL && (config.features & NULLCLIENT) == 0) { 213262266Sbapt it->remote = 0; 214262266Sbapt if (expand) { 215262266Sbapt aliased = do_alias(queue, it->addr); 216262266Sbapt if (!aliased && expand == EXPAND_WILDCARD) 217262266Sbapt aliased = do_alias(queue, "*"); 218262266Sbapt if (aliased < 0) 219262266Sbapt return (-1); 220262266Sbapt if (aliased) { 221262266Sbapt LIST_REMOVE(it, next); 222262266Sbapt } else { 223262266Sbapt /* Local destination, check */ 224262266Sbapt pw = getpwnam(it->addr); 225262266Sbapt if (pw == NULL) 226262266Sbapt goto out; 227262266Sbapt /* XXX read .forward */ 228262266Sbapt endpwent(); 229262266Sbapt } 230262266Sbapt } 231262266Sbapt } else { 232262266Sbapt it->remote = 1; 233262266Sbapt } 234262266Sbapt 235262266Sbapt return (0); 236262266Sbapt 237262266Sbaptout: 238262266Sbapt free(it->addr); 239262266Sbapt free(it); 240262266Sbapt return (-1); 241262266Sbapt} 242262266Sbapt 243262266Sbaptstatic struct qitem * 244262266Sbaptgo_background(struct queue *queue) 245262266Sbapt{ 246262266Sbapt struct sigaction sa; 247262266Sbapt struct qitem *it; 248262266Sbapt pid_t pid; 249262266Sbapt 250262266Sbapt if (daemonize && daemon(0, 0) != 0) { 251262266Sbapt syslog(LOG_ERR, "can not daemonize: %m"); 252289123Sbapt exit(EX_OSERR); 253262266Sbapt } 254262266Sbapt daemonize = 0; 255262266Sbapt 256262266Sbapt bzero(&sa, sizeof(sa)); 257262266Sbapt sa.sa_handler = SIG_IGN; 258262266Sbapt sigaction(SIGCHLD, &sa, NULL); 259262266Sbapt 260262266Sbapt LIST_FOREACH(it, &queue->queue, next) { 261262266Sbapt /* No need to fork for the last dest */ 262262266Sbapt if (LIST_NEXT(it, next) == NULL) 263262266Sbapt goto retit; 264262266Sbapt 265262266Sbapt pid = fork(); 266262266Sbapt switch (pid) { 267262266Sbapt case -1: 268262266Sbapt syslog(LOG_ERR, "can not fork: %m"); 269289123Sbapt exit(EX_OSERR); 270262266Sbapt break; 271262266Sbapt 272262266Sbapt case 0: 273262266Sbapt /* 274262266Sbapt * Child: 275262266Sbapt * 276262266Sbapt * return and deliver mail 277262266Sbapt */ 278262266Sbaptretit: 279262266Sbapt /* 280262266Sbapt * If necessary, acquire the queue and * mail files. 281262266Sbapt * If this fails, we probably were raced by another 282262266Sbapt * process. It is okay to be raced if we're supposed 283262266Sbapt * to flush the queue. 284262266Sbapt */ 285262266Sbapt setlogident("%s", it->queueid); 286262266Sbapt switch (acquirespool(it)) { 287262266Sbapt case 0: 288262266Sbapt break; 289262266Sbapt case 1: 290262266Sbapt if (doqueue) 291289123Sbapt exit(EX_OK); 292262266Sbapt syslog(LOG_WARNING, "could not lock queue file"); 293289123Sbapt exit(EX_SOFTWARE); 294262266Sbapt default: 295289123Sbapt exit(EX_SOFTWARE); 296262266Sbapt } 297262266Sbapt dropspool(queue, it); 298262266Sbapt return (it); 299262266Sbapt 300262266Sbapt default: 301262266Sbapt /* 302262266Sbapt * Parent: 303262266Sbapt * 304262266Sbapt * fork next child 305262266Sbapt */ 306262266Sbapt break; 307262266Sbapt } 308262266Sbapt } 309262266Sbapt 310262266Sbapt syslog(LOG_CRIT, "reached dead code"); 311289123Sbapt exit(EX_SOFTWARE); 312262266Sbapt} 313262266Sbapt 314262266Sbaptstatic void 315262266Sbaptdeliver(struct qitem *it) 316262266Sbapt{ 317262266Sbapt int error; 318262266Sbapt unsigned int backoff = MIN_RETRY, slept; 319262266Sbapt struct timeval now; 320262266Sbapt struct stat st; 321262266Sbapt 322262266Sbapt snprintf(errmsg, sizeof(errmsg), "unknown bounce reason"); 323262266Sbapt 324262266Sbaptretry: 325304587Sbapt syslog(LOG_INFO, "<%s> trying delivery", it->addr); 326262266Sbapt 327262266Sbapt if (it->remote) 328262266Sbapt error = deliver_remote(it); 329262266Sbapt else 330262266Sbapt error = deliver_local(it); 331262266Sbapt 332262266Sbapt switch (error) { 333262266Sbapt case 0: 334331350Semaste syslog(LOG_INFO, "<%s> delivery successful", it->addr); 335262266Sbapt delqueue(it); 336289123Sbapt exit(EX_OK); 337262266Sbapt 338262266Sbapt case 1: 339262266Sbapt if (stat(it->queuefn, &st) != 0) { 340262266Sbapt syslog(LOG_ERR, "lost queue file `%s'", it->queuefn); 341289123Sbapt exit(EX_SOFTWARE); 342262266Sbapt } 343262266Sbapt if (gettimeofday(&now, NULL) == 0 && 344262266Sbapt (now.tv_sec - st.st_mtim.tv_sec > MAX_TIMEOUT)) { 345262266Sbapt snprintf(errmsg, sizeof(errmsg), 346262266Sbapt "Could not deliver for the last %d seconds. Giving up.", 347262266Sbapt MAX_TIMEOUT); 348262266Sbapt goto bounce; 349262266Sbapt } 350262266Sbapt for (slept = 0; slept < backoff;) { 351262266Sbapt slept += SLEEP_TIMEOUT - sleep(SLEEP_TIMEOUT); 352262266Sbapt if (flushqueue_since(slept)) { 353262266Sbapt backoff = MIN_RETRY; 354262266Sbapt goto retry; 355262266Sbapt } 356262266Sbapt } 357262266Sbapt if (slept >= backoff) { 358262266Sbapt /* pick the next backoff between [1.5, 2.5) times backoff */ 359262266Sbapt backoff = backoff + backoff / 2 + random() % backoff; 360262266Sbapt if (backoff > MAX_RETRY) 361262266Sbapt backoff = MAX_RETRY; 362262266Sbapt } 363262266Sbapt goto retry; 364262266Sbapt 365262266Sbapt case -1: 366262266Sbapt default: 367262266Sbapt break; 368262266Sbapt } 369262266Sbapt 370262266Sbaptbounce: 371262266Sbapt bounce(it, errmsg); 372262266Sbapt /* NOTREACHED */ 373262266Sbapt} 374262266Sbapt 375262266Sbaptvoid 376262266Sbaptrun_queue(struct queue *queue) 377262266Sbapt{ 378262266Sbapt struct qitem *it; 379262266Sbapt 380262266Sbapt if (LIST_EMPTY(&queue->queue)) 381262266Sbapt return; 382262266Sbapt 383262266Sbapt it = go_background(queue); 384262266Sbapt deliver(it); 385262266Sbapt /* NOTREACHED */ 386262266Sbapt} 387262266Sbapt 388262266Sbaptstatic void 389262266Sbaptshow_queue(struct queue *queue) 390262266Sbapt{ 391262266Sbapt struct qitem *it; 392262266Sbapt int locked = 0; /* XXX */ 393262266Sbapt 394262266Sbapt if (LIST_EMPTY(&queue->queue)) { 395262266Sbapt printf("Mail queue is empty\n"); 396262266Sbapt return; 397262266Sbapt } 398262266Sbapt 399262266Sbapt LIST_FOREACH(it, &queue->queue, next) { 400262266Sbapt printf("ID\t: %s%s\n" 401262266Sbapt "From\t: %s\n" 402262266Sbapt "To\t: %s\n", 403262266Sbapt it->queueid, 404262266Sbapt locked ? "*" : "", 405262266Sbapt it->sender, it->addr); 406262266Sbapt 407262266Sbapt if (LIST_NEXT(it, next) != NULL) 408262266Sbapt printf("--\n"); 409262266Sbapt } 410262266Sbapt} 411262266Sbapt 412262266Sbapt/* 413262266Sbapt * TODO: 414262266Sbapt * 415262266Sbapt * - alias processing 416262266Sbapt * - use group permissions 417262266Sbapt * - proper sysexit codes 418262266Sbapt */ 419262266Sbapt 420262266Sbaptint 421262266Sbaptmain(int argc, char **argv) 422262266Sbapt{ 423262266Sbapt struct sigaction act; 424262266Sbapt char *sender = NULL; 425262266Sbapt struct queue queue; 426262266Sbapt int i, ch; 427262266Sbapt int nodot = 0, showq = 0, queue_only = 0; 428262266Sbapt int recp_from_header = 0; 429262266Sbapt 430262266Sbapt set_username(); 431262266Sbapt 432262266Sbapt /* 433262266Sbapt * We never run as root. If called by root, drop permissions 434262266Sbapt * to the mail user. 435262266Sbapt */ 436262266Sbapt if (geteuid() == 0 || getuid() == 0) { 437262266Sbapt struct passwd *pw; 438262266Sbapt 439262266Sbapt errno = 0; 440262266Sbapt pw = getpwnam(DMA_ROOT_USER); 441262266Sbapt if (pw == NULL) { 442262266Sbapt if (errno == 0) 443289123Sbapt errx(EX_CONFIG, "user '%s' not found", DMA_ROOT_USER); 444262266Sbapt else 445289123Sbapt err(EX_OSERR, "cannot drop root privileges"); 446262266Sbapt } 447262266Sbapt 448262266Sbapt if (setuid(pw->pw_uid) != 0) 449289123Sbapt err(EX_OSERR, "cannot drop root privileges"); 450262266Sbapt 451262266Sbapt if (geteuid() == 0 || getuid() == 0) 452289123Sbapt errx(EX_OSERR, "cannot drop root privileges"); 453262266Sbapt } 454262266Sbapt 455262266Sbapt atexit(deltmp); 456262266Sbapt init_random(); 457262266Sbapt 458262266Sbapt bzero(&queue, sizeof(queue)); 459262266Sbapt LIST_INIT(&queue.queue); 460262266Sbapt 461315995Sbapt if (strcmp(basename(argv[0]), "mailq") == 0) { 462262266Sbapt argv++; argc--; 463262266Sbapt showq = 1; 464262266Sbapt if (argc != 0) 465289123Sbapt errx(EX_USAGE, "invalid arguments"); 466262266Sbapt goto skipopts; 467262266Sbapt } else if (strcmp(argv[0], "newaliases") == 0) { 468262266Sbapt logident_base = "dma"; 469262295Sbapt setlogident("%s", logident_base); 470262266Sbapt 471262266Sbapt if (read_aliases() != 0) 472289123Sbapt errx(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases); 473289123Sbapt exit(EX_OK); 474262266Sbapt } 475262266Sbapt 476262266Sbapt opterr = 0; 477262266Sbapt while ((ch = getopt(argc, argv, ":A:b:B:C:d:Df:F:h:iL:N:no:O:q:r:R:tUV:vX:")) != -1) { 478262266Sbapt switch (ch) { 479262266Sbapt case 'A': 480262266Sbapt /* -AX is being ignored, except for -A{c,m} */ 481262266Sbapt if (optarg[0] == 'c' || optarg[0] == 'm') { 482262266Sbapt break; 483262266Sbapt } 484262266Sbapt /* else FALLTRHOUGH */ 485262266Sbapt case 'b': 486262266Sbapt /* -bX is being ignored, except for -bp */ 487262266Sbapt if (optarg[0] == 'p') { 488262266Sbapt showq = 1; 489262266Sbapt break; 490262266Sbapt } else if (optarg[0] == 'q') { 491262266Sbapt queue_only = 1; 492262266Sbapt break; 493262266Sbapt } 494262266Sbapt /* else FALLTRHOUGH */ 495262266Sbapt case 'D': 496262266Sbapt daemonize = 0; 497262266Sbapt break; 498262266Sbapt case 'L': 499262266Sbapt logident_base = optarg; 500262266Sbapt break; 501262266Sbapt case 'f': 502262266Sbapt case 'r': 503262266Sbapt sender = optarg; 504262266Sbapt break; 505262266Sbapt 506262266Sbapt case 't': 507262266Sbapt recp_from_header = 1; 508262266Sbapt break; 509262266Sbapt 510262266Sbapt case 'o': 511262266Sbapt /* -oX is being ignored, except for -oi */ 512262266Sbapt if (optarg[0] != 'i') 513262266Sbapt break; 514262266Sbapt /* else FALLTRHOUGH */ 515262266Sbapt case 'O': 516262266Sbapt break; 517262266Sbapt case 'i': 518262266Sbapt nodot = 1; 519262266Sbapt break; 520262266Sbapt 521262266Sbapt case 'q': 522262266Sbapt /* Don't let getopt slup up other arguments */ 523262266Sbapt if (optarg && *optarg == '-') 524262266Sbapt optind--; 525262266Sbapt doqueue = 1; 526262266Sbapt break; 527262266Sbapt 528262266Sbapt /* Ignored options */ 529262266Sbapt case 'B': 530262266Sbapt case 'C': 531262266Sbapt case 'd': 532262266Sbapt case 'F': 533262266Sbapt case 'h': 534262266Sbapt case 'N': 535262266Sbapt case 'n': 536262266Sbapt case 'R': 537262266Sbapt case 'U': 538262266Sbapt case 'V': 539262266Sbapt case 'v': 540262266Sbapt case 'X': 541262266Sbapt break; 542262266Sbapt 543262266Sbapt case ':': 544262266Sbapt if (optopt == 'q') { 545262266Sbapt doqueue = 1; 546262266Sbapt break; 547262266Sbapt } 548262266Sbapt /* FALLTHROUGH */ 549262266Sbapt 550262266Sbapt default: 551262266Sbapt fprintf(stderr, "invalid argument: `-%c'\n", optopt); 552289123Sbapt exit(EX_USAGE); 553262266Sbapt } 554262266Sbapt } 555262266Sbapt argc -= optind; 556262266Sbapt argv += optind; 557262266Sbapt opterr = 1; 558262266Sbapt 559262266Sbapt if (argc != 0 && (showq || doqueue)) 560289123Sbapt errx(EX_USAGE, "sending mail and queue operations are mutually exclusive"); 561262266Sbapt 562262266Sbapt if (showq + doqueue > 1) 563289123Sbapt errx(EX_USAGE, "conflicting queue operations"); 564262266Sbapt 565262266Sbaptskipopts: 566262266Sbapt if (logident_base == NULL) 567262266Sbapt logident_base = "dma"; 568262295Sbapt setlogident("%s", logident_base); 569262266Sbapt 570262266Sbapt act.sa_handler = sighup_handler; 571262266Sbapt act.sa_flags = 0; 572262266Sbapt sigemptyset(&act.sa_mask); 573262266Sbapt if (sigaction(SIGHUP, &act, NULL) != 0) 574262266Sbapt syslog(LOG_WARNING, "can not set signal handler: %m"); 575262266Sbapt 576262266Sbapt parse_conf(CONF_PATH "/dma.conf"); 577262266Sbapt 578262266Sbapt if (config.authpath != NULL) 579262266Sbapt parse_authfile(config.authpath); 580262266Sbapt 581262266Sbapt if (showq) { 582262266Sbapt if (load_queue(&queue) < 0) 583289123Sbapt errlog(EX_NOINPUT, "can not load queue"); 584262266Sbapt show_queue(&queue); 585262266Sbapt return (0); 586262266Sbapt } 587262266Sbapt 588262266Sbapt if (doqueue) { 589262266Sbapt flushqueue_signal(); 590262266Sbapt if (load_queue(&queue) < 0) 591289123Sbapt errlog(EX_NOINPUT, "can not load queue"); 592262266Sbapt run_queue(&queue); 593262266Sbapt return (0); 594262266Sbapt } 595262266Sbapt 596262266Sbapt if (read_aliases() != 0) 597289123Sbapt errlog(EX_SOFTWARE, "could not parse aliases file `%s'", config.aliases); 598262266Sbapt 599262266Sbapt if ((sender = set_from(&queue, sender)) == NULL) 600289144Sbapt errlog(EX_SOFTWARE, "set_from()"); 601262266Sbapt 602262266Sbapt if (newspoolf(&queue) != 0) 603289123Sbapt errlog(EX_CANTCREAT, "can not create temp file in `%s'", config.spooldir); 604262266Sbapt 605262266Sbapt setlogident("%s", queue.id); 606262266Sbapt 607262266Sbapt for (i = 0; i < argc; i++) { 608262266Sbapt if (add_recp(&queue, argv[i], EXPAND_WILDCARD) != 0) 609289123Sbapt errlogx(EX_DATAERR, "invalid recipient `%s'", argv[i]); 610262266Sbapt } 611262266Sbapt 612262266Sbapt if (LIST_EMPTY(&queue.queue) && !recp_from_header) 613289123Sbapt errlogx(EX_NOINPUT, "no recipients"); 614262266Sbapt 615262266Sbapt if (readmail(&queue, nodot, recp_from_header) != 0) 616289123Sbapt errlog(EX_NOINPUT, "can not read mail"); 617262266Sbapt 618262266Sbapt if (LIST_EMPTY(&queue.queue)) 619289123Sbapt errlogx(EX_NOINPUT, "no recipients"); 620262266Sbapt 621262266Sbapt if (linkspool(&queue) != 0) 622289123Sbapt errlog(EX_CANTCREAT, "can not create spools"); 623262266Sbapt 624262266Sbapt /* From here on the mail is safe. */ 625262266Sbapt 626262266Sbapt if (config.features & DEFER || queue_only) 627262266Sbapt return (0); 628262266Sbapt 629262266Sbapt run_queue(&queue); 630262266Sbapt 631262266Sbapt /* NOTREACHED */ 632262266Sbapt return (0); 633262266Sbapt} 634