Logo Search packages:      
Sourcecode: fdm version File versions  Download package

fdm.c

/* $Id: fdm.c,v 1.163 2007/09/28 18:17:31 nicm Exp $ */

/*
 * Copyright (c) 2006 Nicholas Marriott <nicm@users.sourceforge.net>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/utsname.h>
#include <sys/wait.h>

#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <grp.h>
#include <limits.h>
#include <paths.h>
#include <pwd.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include "fdm.h"

#if defined(__OpenBSD__) && defined(DEBUG)
const char        *malloc_options = "AFGJPRX";
#endif

void               sighandler(int);
void               usage(void);

struct conf        conf;

volatile sig_atomic_t    sigint;
volatile sig_atomic_t    sigterm;

void
sighandler(int sig)
{
      switch (sig) {
      case SIGINT:
            sigint = 1;
            break;
      case SIGTERM:
            sigterm = 1;
            break;
      }
}

double
get_time(void)
{
      struct timeval     tv;

      if (gettimeofday(&tv, NULL) != 0)
            fatal("gettimeofday failed");
      return (tv.tv_sec + tv.tv_usec / 1000000.0);
}

void
fill_info(const char *home)
{
      struct passwd     *pw;
      uid_t        uid;
      char         host[MAXHOSTNAMELEN];

      uid = getuid();
      if (conf.info.valid && conf.info.last_uid == uid)
            return;
      conf.info.valid = 1;
      conf.info.last_uid = uid;

      if (conf.info.uid != NULL) {
            xfree(conf.info.uid);
            conf.info.uid = NULL;
      }
      if (conf.info.user != NULL) {
            xfree(conf.info.user);
            conf.info.user = NULL;
      }
      if (conf.info.home != NULL) {
            xfree(conf.info.home);
            conf.info.home = NULL;
      }

      if (conf.info.host == NULL) {
            if (gethostname(host, sizeof host) != 0)
                  fatal("gethostname failed");
            conf.info.host = xstrdup(host);

            getaddrs(host, &conf.info.fqdn, &conf.info.addr);
      }

      if (home != NULL && *home != '\0')
            conf.info.home = xstrdup(home);

      xasprintf(&conf.info.uid, "%lu", (u_long) uid);
      pw = getpwuid(uid);
      if (pw != NULL) {
            if (conf.info.home == NULL) {
                  if (pw->pw_dir != NULL && *pw->pw_dir != '\0')
                        conf.info.home = xstrdup(pw->pw_dir);
                  else
                        conf.info.home = xstrdup(".");
            }
            if (pw->pw_name != NULL && *pw->pw_name != '\0')
                  conf.info.user = xstrdup(pw->pw_name);
      }
      endpwent();
      if (conf.info.user == NULL) {
            conf.info.user = xstrdup(conf.info.uid);
            log_warnx("can't find name for user %lu", (u_long) uid);
      }
}

void
dropto(uid_t uid)
{
      struct passwd     *pw;
      gid_t        gid;

      if (uid == (uid_t) -1 || uid == 0)
            return;

      pw = getpwuid(uid);
      if (pw == NULL) {
            errno = ESRCH;
            fatal("getpwuid failed");
      }
      gid = pw->pw_gid;
      endpwent();

      if (setgroups(1, &gid) != 0)
            fatal("setgroups failed");
      if (setresgid(gid, gid, gid) != 0)
            fatal("setresgid failed");
      if (setresuid(uid, uid, uid) != 0)
            fatal("setresuid failed");
}

int
check_incl(const char *name)
{
      u_int i;

      if (ARRAY_EMPTY(&conf.incl))
            return (1);

      for (i = 0; i < ARRAY_LENGTH(&conf.incl); i++) {
            if (account_match(ARRAY_ITEM(&conf.incl, i), name))
                  return (1);
      }

      return (0);
}

int
check_excl(const char *name)
{
      u_int i;

      if (ARRAY_EMPTY(&conf.excl))
            return (0);

      for (i = 0; i < ARRAY_LENGTH(&conf.excl); i++) {
            if (account_match(ARRAY_ITEM(&conf.excl, i), name))
                  return (1);
      }

      return (0);
}

int
use_account(struct account *a, char **cause)
{
      if (!check_incl(a->name)) {
            if (cause != NULL)
                  xasprintf(cause, "account %s is not included", a->name);
            return (0);
      }
      if (check_excl(a->name)) {
            if (cause != NULL)
                  xasprintf(cause, "account %s is excluded", a->name);
            return (0);
      }

      /*
       * If the account is disabled and no accounts are specified on the
       * command line (whether or not it is included if there are is already
       * confirmed), then skip it.
       */
      if (a->disabled && ARRAY_EMPTY(&conf.incl)) {
            if (cause != NULL)
                  xasprintf(cause, "account %s is disabled", a->name);
            return (0);
      }

      return (1);
}

__dead void
usage(void)
{
      fprintf(stderr,
          "usage: %s [-klmnqv] [-a name] [-D name=value] [-f conffile]"
          " [-u user] [-x name] [fetch|poll]\n", __progname);
        exit(1);
}

int
main(int argc, char **argv)
{
        int        opt, lockfd, status, res;
      u_int        i;
      enum fdmop       op = FDMOP_NONE;
      const char  *errstr, *proxy = NULL, *s;
      char         tmp[BUFSIZ], *ptr, *strs, *user = NULL, *lock = NULL;
      long         n;
      struct utsname     un;
      struct passwd     *pw;
      struct stat  sb;
      time_t             tt;
      struct account    *a;
      pid_t        pid;
      struct children    children, dead_children;
      struct child      *child;
      struct io       *rio;
      struct iolist      iol;
      double             tim;
      struct sigaction act;
      struct msg   msg;
      struct msgbuf      msgbuf;
      size_t             off;
      struct strings     macros;
      struct child_fetch_data *cfd;
#ifdef DEBUG
      struct rule *r;
      struct action     *t;
      struct cache      *cache;
#endif

      log_open(stderr, LOG_MAIL, 0);

      memset(&conf, 0, sizeof conf);
      TAILQ_INIT(&conf.accounts);
      TAILQ_INIT(&conf.rules);
      TAILQ_INIT(&conf.actions);
      TAILQ_INIT(&conf.caches);
      conf.max_size = DEFMAILSIZE;
      conf.timeout = DEFTIMEOUT;
      conf.lock_types = LOCK_FLOCK;
      conf.impl_act = DECISION_NONE;
      conf.purge_after = 0;
      conf.file_umask = DEFUMASK;
      conf.file_group = -1;
      conf.queue_high = -1;
      conf.queue_low = -1;
      conf.def_user = -1;
      conf.strip_chars = xstrdup(DEFSTRIPCHARS);

      ARRAY_INIT(&conf.incl);
      ARRAY_INIT(&conf.excl);

      ARRAY_INIT(&macros);
        while ((opt = getopt(argc, argv, "a:D:f:klmnqu:vx:")) != EOF) {
                switch (opt) {
            case 'a':
                  ARRAY_ADD(&conf.incl, xstrdup(optarg));
                  break;
            case 'D':
                  ARRAY_ADD(&macros, optarg);
                  break;
                case 'f':
                        conf.conf_file = xstrdup(optarg);
                        break;
            case 'k':
                  conf.keep_all = 1;
                  break;
            case 'l':
                  conf.syslog = 1;
                  break;
            case 'm':
                  conf.allow_many = 1;
                  break;
            case 'n':
                  conf.check_only = 1;
                  break;
            case 'u':
                  user = optarg;
                  break;
                case 'v':
                  if (conf.debug != -1)
                        conf.debug++;
                        break;
            case 'q':
                  conf.debug = -1;
                  break;
            case 'x':
                  ARRAY_ADD(&conf.excl, xstrdup(optarg));
                  break;
                default:
                        usage();
                }
        }
      argc -= optind;
      argv += optind;
      if (conf.check_only) {
            if (argc != 0)
                  usage();
      } else {
            if (argc != 1)
                  usage();
            if (strncmp(argv[0], "poll", strlen(argv[0])) == 0)
                  op = FDMOP_POLL;
            else if (strncmp(argv[0], "fetch", strlen(argv[0])) == 0)
                  op = FDMOP_FETCH;
            else
                  usage();
      }

      /* Check the user. */
      if (user != NULL) {
            pw = getpwnam(user);
            if (pw == NULL) {
                  endpwent();
                  n = strtonum(user, 0, UID_MAX, &errstr);
                  if (errstr != NULL) {
                        if (errno == ERANGE) {
                              log_warnx("invalid uid: %s", user);
                              exit(1);
                        }
                  } else
                        pw = getpwuid((uid_t) n);
                  if (pw == NULL) {
                        log_warnx("unknown user: %s", user);
                        exit(1);
                  }
            }
            conf.def_user = pw->pw_uid;
            endpwent();
      }

      /* Set debug level and start logging to syslog if necessary. */
      if (conf.syslog)
            log_open(NULL, LOG_MAIL, conf.debug);
      else
            log_open(stderr, LOG_MAIL, conf.debug);
      tt = time(NULL);
      log_debug("version is: %s " BUILD ", started at: %.24s", __progname,
          ctime(&tt));

      /* And the OS version. */
      if (uname(&un) == 0) {
            log_debug2("running on: %s %s %s %s", un.sysname, un.release,
                un.version, un.machine);
      } else
            log_debug2("uname: %s", strerror(errno));

      /* Save the home dir and misc user info. */
      fill_info(getenv("HOME"));
      log_debug2("user is: %s, home is: %s", conf.info.user, conf.info.home);

      /* Find the config file. */
      if (conf.conf_file == NULL) {
            /* If no file specified, try ~ then /etc. */
            xasprintf(&conf.conf_file, "%s/%s", conf.info.home, CONFFILE);
            if (access(conf.conf_file, R_OK) != 0) {
                  xfree(conf.conf_file);
                  conf.conf_file = xstrdup(SYSCONFFILE);
            }
      }
      log_debug2("loading configuration from %s", conf.conf_file);
      if (stat(conf.conf_file, &sb) == -1) {
                log_warn("%s", conf.conf_file);
            exit(1);
      }
      if (geteuid() != 0 && (sb.st_mode & (S_IROTH|S_IWOTH)) != 0)
            log_warnx("%s: world readable or writable", conf.conf_file);
        if (parse_conf(conf.conf_file, &macros) != 0) {
                log_warn("%s", conf.conf_file);
            exit(1);
      }
      ARRAY_FREE(&macros);
      log_debug2("configuration loaded");

      /* Sort out queue limits. */
      if (conf.queue_high == -1)
            conf.queue_high = DEFMAILQUEUE;
      if (conf.queue_low == -1) {
            conf.queue_low = conf.queue_high * 3 / 4;
            if (conf.queue_low >= conf.queue_high)
                  conf.queue_low = conf.queue_high - 1;
      }

      /* Set the umask. */
      umask(conf.file_umask);

      /* Figure out default user. */
      if (conf.def_user == (uid_t) -1) {
            conf.def_user = geteuid();
            if (conf.def_user == 0) {
                  log_warnx("no default user specified");
                  exit(1);
            }
      }

      /* Print proxy info. */
      if (conf.proxy != NULL) {
            switch (conf.proxy->type) {
            case PROXY_HTTP:
                  proxy = "HTTP";
                  break;
            case PROXY_HTTPS:
                  proxy = "HTTPS";
                  break;
            case PROXY_SOCKS5:
                  proxy = "SOCKS5";
                  break;
            }
            log_debug2("using proxy: %s on %s:%s", proxy,
                conf.proxy->server.host, conf.proxy->server.port);
      }

      /* Print some locking info. */
      *tmp = '\0';
      if (conf.lock_types == 0)
            strlcpy(tmp, "none", sizeof tmp);
      else {
            if (conf.lock_types & LOCK_FCNTL)
                  strlcat(tmp, "fcntl ", sizeof tmp);
            if (conf.lock_types & LOCK_FLOCK)
                  strlcat(tmp, "flock ", sizeof tmp);
            if (conf.lock_types & LOCK_DOTLOCK)
                  strlcat(tmp, "dotlock ", sizeof tmp);
      }
      log_debug2("locking using: %s", tmp);

      /* Initialise and print headers and domains. */
      if (conf.headers == NULL) {
            conf.headers = xmalloc(sizeof *conf.headers);
            ARRAY_INIT(conf.headers);
            ARRAY_ADD(conf.headers, xstrdup("to"));
            ARRAY_ADD(conf.headers, xstrdup("cc"));
      }
      strs = fmt_strings("", conf.headers);
      log_debug2("headers are: %s", strs);
      xfree(strs);
      if (conf.domains == NULL) {
            conf.domains = xmalloc(sizeof *conf.domains);
            ARRAY_INIT(conf.domains);
            ARRAY_ADD(conf.domains, xstrdup(conf.info.host));
            if (conf.info.fqdn != NULL) {
                  ptr = xstrdup(conf.info.fqdn);
                  ARRAY_ADD(conf.domains, ptr);
            }
            if (conf.info.addr != NULL) {
                  xasprintf(&ptr, "\\[%s\\]", conf.info.addr);
                  ARRAY_ADD(conf.domains, ptr);
            }
      }
      strs = fmt_strings("", conf.domains);
      log_debug2("domains are: %s", strs);
      xfree(strs);

      /* Print the other settings. */
      *tmp = '\0';
      off = 0;
      if (conf.allow_many)
            off = strlcat(tmp, "allow-multiple, ", sizeof tmp);
      if (conf.no_received)
            off = strlcat(tmp, "no-received, ", sizeof tmp);
      if (conf.keep_all)
            off = strlcat(tmp, "keep-all, ", sizeof tmp);
      if (conf.del_big)
            off = strlcat(tmp, "delete-oversized, ", sizeof tmp);
      if (conf.verify_certs)
            off = strlcat(tmp, "verify-certificates, ", sizeof tmp);
      if (sizeof tmp > off && conf.purge_after > 0) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "purge-after=%u, ", conf.purge_after);
      }
      if (sizeof tmp > off) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "maximum-size=%zu, ", conf.max_size);
      }
      if (sizeof tmp > off) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "timeout=%d, ", conf.timeout / 1000);
      }
      if (sizeof tmp > off) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "default-user=%lu, ", (u_long) conf.def_user);
      }
      if (sizeof tmp > off && conf.impl_act != DECISION_NONE) {
            if (conf.impl_act == DECISION_DROP)
                  s = "drop";
            else if (conf.impl_act == DECISION_KEEP)
                  s = "keep";
            else
                  s = "none";
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "unmatched-mail=%s, ", s);
      }
      if (sizeof tmp > off) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "file-umask=%o%o%o, ", MODE(conf.file_umask));
      }
      if (sizeof tmp > off && conf.file_group != (gid_t) -1) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "file-group=%lu, ", (u_long) conf.file_group);
      }
      if (sizeof tmp > off) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "queue-high=%u, queue-low=%u, ", conf.queue_high,
                conf.queue_low);
      }
      if (sizeof tmp > off && conf.lock_file != NULL) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "lock-file=\"%s\", ", conf.lock_file);
      }
      if (sizeof tmp > off) {
            off += xsnprintf(tmp + off, (sizeof tmp) - off,
                "strip-characters=\"%s\", ", conf.strip_chars);
      }
      if (off >= 2) {
            tmp[off - 2] = '\0';
            log_debug2("options are: %s", tmp);
      }

      /* Save and print tmp dir. */
      s = getenv("TMPDIR");
      if (s == NULL || *s == '\0')
            s = _PATH_TMP;
      else {
            if (stat(s, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
                  log_warn("%s", s);
                  s = _PATH_TMP;
            }
      }
      conf.tmp_dir = xstrdup(s);
      while ((ptr = strrchr(conf.tmp_dir, '/')) != NULL) {
            if (ptr == conf.tmp_dir || ptr[1] != '\0')
                  break;
            *ptr = '\0';
      }
      log_debug2("using tmp directory: %s", conf.tmp_dir);

      /* If -n, bail now, otherwise check there is something to work with. */
      if (conf.check_only)
            exit(0);
        if (TAILQ_EMPTY(&conf.accounts)) {
                log_warnx("no accounts specified");
            exit(1);
      }
        if (op == FDMOP_FETCH && TAILQ_EMPTY(&conf.rules)) {
                log_warnx("no rules specified");
            exit(1);
      }

      /* Check for child user if root. */
      if (geteuid() == 0) {
            pw = getpwnam(CHILDUSER);
            if (pw == NULL) {
                  log_warnx("can't find user: %s", CHILDUSER);
                  exit(1);
            }
            conf.child_uid = pw->pw_uid;
            conf.child_gid = pw->pw_gid;
            endpwent();
      }

      /* Set up signal handlers. */
      memset(&act, 0, sizeof act);
      sigemptyset(&act.sa_mask);
      sigaddset(&act.sa_mask, SIGINT);
      sigaddset(&act.sa_mask, SIGTERM);
      act.sa_flags = SA_RESTART;

      act.sa_handler = SIG_IGN;
      if (sigaction(SIGPIPE, &act, NULL) < 0)
            fatal("sigaction failed");
      if (sigaction(SIGUSR1, &act, NULL) < 0)
            fatal("sigaction failed");
      if (sigaction(SIGUSR2, &act, NULL) < 0)
            fatal("sigaction failed");

      act.sa_handler = sighandler;
      if (sigaction(SIGINT, &act, NULL) < 0)
            fatal("sigaction failed");
      if (sigaction(SIGTERM, &act, NULL) < 0)
            fatal("sigaction failed");

      /* Check lock file. */
      lock = conf.lock_file;
      if (lock == NULL) {
            if (geteuid() == 0)
                  lock = xstrdup(SYSLOCKFILE);
            else
                  xasprintf(&lock, "%s/%s", conf.info.home, LOCKFILE);
      }
      if (*lock != '\0' && !conf.allow_many) {
            lockfd = xcreate(lock, O_WRONLY, -1, -1, S_IRUSR|S_IWUSR);
            if (lockfd == -1 && errno == EEXIST) {
                  log_warnx("already running (%s exists)", lock);
                  exit(1);
            } else if (lockfd == -1) {
                  log_warn("%s: open", lock);
                  exit(1);
            }
            close(lockfd);
      }
      conf.lock_file = lock;

        SSL_library_init();
        SSL_load_error_strings();

#ifdef DEBUG
      COUNTFDS("parent");
#endif

      /* Start the children and build the array. */
      ARRAY_INIT(&children);
      ARRAY_INIT(&dead_children);

      child = NULL;
      TAILQ_FOREACH(a, &conf.accounts, entry) {
            if (!use_account(a, NULL))
                  continue;

            cfd = xmalloc(sizeof *cfd);
            cfd->account = a;
            cfd->op = op;
            cfd->children = &children;
            child = child_start(&children, conf.child_uid, child_fetch,
                parent_fetch, cfd);

            log_debug2("parent: child %ld (%s) started", (long) child->pid,
                a->name);
      }

      if (ARRAY_EMPTY(&children)) {
                log_warnx("no accounts found");
            res = 1;
            goto out;
      }

#ifndef NO_SETPROCTITLE
      setproctitle("parent");
#endif
      log_debug2("parent: started, pid is %ld", (long) getpid());
      tim = get_time();

      res = 0;
      ARRAY_INIT(&iol);
      while (!ARRAY_EMPTY(&children)) {
            if (sigint || sigterm)
                  break;

            /* Fill the io list. */
            ARRAY_CLEAR(&iol);
            for (i = 0; i < ARRAY_LENGTH(&children); i++) {
                  child = ARRAY_ITEM(&children, i);
                  ARRAY_ADD(&iol, child->io);
            }

            /* Poll the io list. */
            n = io_polln(
                ARRAY_DATA(&iol), ARRAY_LENGTH(&iol), &rio, INFTIM, NULL);
            switch (n) {
            case -1:
                  fatalx("child socket error");
            case 0:
                  fatalx("child socket closed");
            }

            while (!ARRAY_EMPTY(&children)) {
                  /* Check all children for pending privsep messages. */
                  for (i = 0; i < ARRAY_LENGTH(&children); i++) {
                        child = ARRAY_ITEM(&children, i);
                        if (privsep_check(child->io))
                              break;
                  }
                  if (i == ARRAY_LENGTH(&children))
                        break;

                  /* And handle them if necessary. */
                  if (privsep_recv(child->io, &msg, &msgbuf) != 0)
                        fatalx("privsep_recv error");
                  log_debug3("parent: got message type %d, id %u from "
                      "child %ld", msg.type, msg.id, (long) child->pid);

                  if (child->msg(child, &msg, &msgbuf) == 0)
                        continue;

                  /* Child has said it is ready to exit, tell it to. */
                  memset(&msg, 0, sizeof msg);
                  msg.type = MSG_EXIT;
                  if (privsep_send(child->io, &msg, NULL) != 0)
                        fatalx("privsep_send error");

                  /* Wait for the child. */
                  if (waitpid(child->pid, &status, 0) == -1)
                        fatal("waitpid failed");
                  if (WIFSIGNALED(status)) {
                        res = 1;
                        log_debug2("parent: child %ld got signal %d",
                            (long) child->pid, WTERMSIG(status));
                  } else if (!WIFEXITED(status)) {
                        res = 1;
                        log_debug2("parent: child %ld didn't exit"
                            "normally", (long) child->pid);
                  } else {
                        if (WEXITSTATUS(status) != 0)
                              res = 1;
                        log_debug2("parent: child %ld returned %d",
                            (long) child->pid, WEXITSTATUS(status));
                  }

                  io_close(child->io);
                  io_free(child->io);
                  child->io = NULL;

                  ARRAY_REMOVE(&children, i);
                  ARRAY_ADD(&dead_children, child);
            }
      }
      ARRAY_FREE(&iol);

      /* Free the dead children. */
      for (i = 0; i < ARRAY_LENGTH(&dead_children); i++) {
            child = ARRAY_ITEM(&dead_children, i);
            if (child->data != NULL)
                  xfree(child->data);
            xfree(child);
      }
      ARRAY_FREE(&dead_children);

      if (sigint || sigterm) {
            act.sa_handler = SIG_IGN;
            if (sigaction(SIGINT, &act, NULL) < 0)
                  fatal("sigaction failed");
            if (sigaction(SIGTERM, &act, NULL) < 0)
                  fatal("sigaction failed");

            if (sigint)
                  log_warnx("parent: caught SIGINT. stopping");
            else if (sigterm)
                  log_warnx("parent: caught SIGTERM. stopping");

            /* Kill the children. */
            for (i = 0; i < ARRAY_LENGTH(&children); i++) {
                  child = ARRAY_ITEM(&children, i);
                  kill(child->pid, SIGTERM);

                  io_close(child->io);
                  io_free(child->io);
                  xfree(child);
            }
            ARRAY_FREE(&children);

            /* And wait for them. */
            for (;;) {
                  if ((pid = wait(&status)) == -1) {
                        if (errno == ECHILD)
                              break;
                        fatal("wait failed");
                  }
                  log_debug2("parent: child %ld killed", (long) pid);
            }

            res = 1;
      }

      tim = get_time() - tim;
      log_debug2("parent: finished, total time %.3f seconds", tim);

out:
      if (!conf.allow_many && *conf.lock_file != '\0')
            unlink(conf.lock_file);

#ifdef DEBUG
      COUNTFDS("parent");

      /* Free everything. */
      while (!TAILQ_EMPTY(&conf.caches)) {
            cache = TAILQ_FIRST(&conf.caches);
            TAILQ_REMOVE(&conf.caches, cache, entry);
            free_cache(cache);
      }
      while (!TAILQ_EMPTY(&conf.accounts)) {
            a = TAILQ_FIRST(&conf.accounts);
            TAILQ_REMOVE(&conf.accounts, a, entry);
            free_account(a);
      }
      while (!TAILQ_EMPTY(&conf.rules)) {
            r = TAILQ_FIRST(&conf.rules);
            TAILQ_REMOVE(&conf.rules, r, entry);
            free_rule(r);
      }
      while (!TAILQ_EMPTY(&conf.actions)) {
            t = TAILQ_FIRST(&conf.actions);
            TAILQ_REMOVE(&conf.actions, t, entry);
            free_action(t);
      }
      xfree(conf.info.home);
      xfree(conf.info.user);
      xfree(conf.info.uid);
      xfree(conf.info.host);
      if (conf.info.fqdn != NULL)
            xfree(conf.info.fqdn);
      if (conf.info.addr != NULL)
            xfree(conf.info.addr);
      xfree(conf.conf_file);
      xfree(conf.lock_file);
      xfree(conf.tmp_dir);
      xfree(conf.strip_chars);
      free_strings(conf.domains);
      ARRAY_FREEALL(conf.domains);
      free_strings(conf.headers);
      ARRAY_FREEALL(conf.headers);
      free_strings(&conf.incl);
      free_strings(&conf.excl);

      xmalloc_report(getpid(), "parent");
#endif

      exit(res);
}

Generated by  Doxygen 1.6.0   Back to index