Hi,

after all the preparation, the following diff adds support for
fork+exec in relayd; based on rzalamena@'s work for httpd.

Notes:
- proc.c is identical to the version that is already in httpd.
- The recvfd pledge in some procs is only needed once, could be
dropped later after receiving IMSG_CTL_PROCFD.
- The socket_rlimit(-1) in the parent is needed because relayd
currently opens n:m fds between all processes which slightly exceeds
the allowed 128 fds.  The problem also exists in httpd and in -current
but the relayd fork+exec diff needs a few more fds and gets over the
limit.  The problem will be reviewed and fixed independently.
- Don't forget to clean and rebuild relayctl.

All regress tests pass, but it needs some more testing.

OK?

Reyk

Index: usr.sbin/relayd/ca.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/ca.c,v
retrieving revision 1.21
diff -u -p -u -p -r1.21 ca.c
--- usr.sbin/relayd/ca.c        2 Sep 2016 14:45:51 -0000       1.21
+++ usr.sbin/relayd/ca.c        2 Sep 2016 16:49:53 -0000
@@ -73,7 +73,7 @@ ca(struct privsep *ps, struct privsep_pr
 void
 ca_init(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
-       if (pledge("stdio", NULL) == -1)
+       if (pledge("stdio recvfd", NULL) == -1)
                fatal("pledge");
 
        if (config_init(ps->ps_env) == -1)
Index: usr.sbin/relayd/hce.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/hce.c,v
retrieving revision 1.74
diff -u -p -u -p -r1.74 hce.c
--- usr.sbin/relayd/hce.c       2 Sep 2016 14:45:51 -0000       1.74
+++ usr.sbin/relayd/hce.c       2 Sep 2016 16:49:53 -0000
@@ -70,7 +70,7 @@ hce_init(struct privsep *ps, struct priv
        /* Allow maximum available sockets for TCP checks */
        socket_rlimit(-1);
 
-       if (pledge("stdio inet", NULL) == -1)
+       if (pledge("stdio recvfd inet", NULL) == -1)
                fatal("hce: pledge");
 }
 
Index: usr.sbin/relayd/relayd.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.c,v
retrieving revision 1.159
diff -u -p -u -p -r1.159 relayd.c
--- usr.sbin/relayd/relayd.c    2 Sep 2016 14:45:51 -0000       1.159
+++ usr.sbin/relayd/relayd.c    2 Sep 2016 16:49:54 -0000
@@ -121,8 +121,12 @@ main(int argc, char *argv[])
        struct relayd           *env;
        struct privsep          *ps;
        const char              *conffile = CONF_FILE;
+       enum privsep_procid      proc_id = PROC_PARENT;
+       int                      proc_instance = 0;
+       const char              *errp, *title = NULL;
+       int                      argc0 = argc;
 
-       while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
+       while ((c = getopt(argc, argv, "dD:nI:P:f:v")) != -1) {
                switch (c) {
                case 'd':
                        debug = 2;
@@ -143,6 +147,18 @@ main(int argc, char *argv[])
                        verbose++;
                        opts |= RELAYD_OPT_VERBOSE;
                        break;
+               case 'P':
+                       title = optarg;
+                       proc_id = proc_getid(procs, nitems(procs), title);
+                       if (proc_id == PROC_MAX)
+                               fatalx("invalid process name");
+                       break;
+               case 'I':
+                       proc_instance = strtonum(optarg, 0,
+                           PROC_MAX_INSTANCES, &errp);
+                       if (errp)
+                               fatalx("invalid process instance");
+                       break;
                default:
                        usage();
                }
@@ -189,19 +205,29 @@ main(int argc, char *argv[])
        log_init(debug, LOG_DAEMON);
        log_verbose(verbose);
 
-       if (!debug && daemon(1, 0) == -1)
-               err(1, "failed to daemonize");
-
        if (env->sc_conf.opts & RELAYD_OPT_NOACTION)
                ps->ps_noaction = 1;
-       else
-               log_info("startup");
 
        ps->ps_instances[PROC_RELAY] = env->sc_conf.prefork_relay;
        ps->ps_instances[PROC_CA] = env->sc_conf.prefork_relay;
+       ps->ps_instance = proc_instance;
+       if (title != NULL)
+               ps->ps_title[proc_id] = title;
+
+       if (proc_id == PROC_PARENT) {
+               /* XXX the parent opens too many fds in proc_open() */
+               socket_rlimit(-1);
+       }
+
+       /* only the parent returns */
+       proc_init(ps, procs, nitems(procs), argc0, argv, proc_id);
 
-       proc_init(ps, procs, nitems(procs));
        log_procinit("parent");
+       if (!debug && daemon(1, 0) == -1)
+               err(1, "failed to daemonize");
+
+       if (ps->ps_noaction == 0)
+               log_info("startup");
 
        event_init();
 
@@ -217,7 +243,7 @@ main(int argc, char *argv[])
        signal_add(&ps->ps_evsigpipe, NULL);
        signal_add(&ps->ps_evsigusr1, NULL);
 
-       proc_listen(ps, procs, nitems(procs));
+       proc_connect(ps);
 
        if (load_config(env->sc_conffile, env) == -1) {
                proc_kill(env->sc_ps);
Index: usr.sbin/relayd/relayd.h
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/relayd.h,v
retrieving revision 1.231
diff -u -p -u -p -r1.231 relayd.h
--- usr.sbin/relayd/relayd.h    2 Sep 2016 16:14:09 -0000       1.231
+++ usr.sbin/relayd/relayd.h    2 Sep 2016 16:49:54 -0000
@@ -915,6 +915,7 @@ enum imsg_type {
        IMSG_CTL_OK,            /* answer to relayctl requests */
        IMSG_CTL_FAIL,
        IMSG_CTL_VERBOSE,
+       IMSG_CTL_PROCFD,
        IMSG_CTL_END,
        IMSG_CTL_RDR,
        IMSG_CTL_TABLE,
@@ -987,6 +988,11 @@ enum privsep_procid {
 /* Attach the control socket to the following process */
 #define PROC_CONTROL   PROC_PFE
 
+/* Define default parent socket number */
+#define PARENT_SOCK_FILENO     3
+
+#define PROC_MAX_INSTANCES     128
+
 struct privsep_pipes {
        int                             *pp_pipes[PROC_MAX];
 };
@@ -1031,6 +1037,11 @@ struct privsep_proc {
        struct relayd           *p_env;
 };
 
+struct privsep_fd {
+       enum privsep_procid              pf_procid;
+       unsigned int                     pf_instance;
+};
+
 struct relayd_config {
        char                     tls_sid[SSL_MAX_SID_CTX_LENGTH];
        char                     snmp_path[PATH_MAX];
@@ -1363,12 +1374,15 @@ __dead void fatalx(const char *, ...)
            __attribute__((__format__ (printf, 1, 2)));
 
 /* proc.c */
-void    proc_init(struct privsep *, struct privsep_proc *, u_int);
+enum privsep_procid
+           proc_getid(struct privsep_proc *, unsigned int, const char *);
+void    proc_init(struct privsep *, struct privsep_proc *, unsigned int,
+           int, char **, enum privsep_procid);
 void    proc_kill(struct privsep *);
-void    proc_listen(struct privsep *, struct privsep_proc *, size_t);
+void    proc_connect(struct privsep *);
 void    proc_dispatch(int, short event, void *);
 void    proc_run(struct privsep *, struct privsep_proc *,
-           struct privsep_proc *, u_int,
+           struct privsep_proc *, unsigned int,
            void (*)(struct privsep *, struct privsep_proc *, void *), void *);
 void    proc_range(struct privsep *, enum privsep_procid, int *, int *);
 int     proc_compose_imsg(struct privsep *, enum privsep_procid, int,
@@ -1377,18 +1391,18 @@ int      proc_compose(struct privsep *, enum
            uint16_t, void *, uint16_t);
 int     proc_composev_imsg(struct privsep *, enum privsep_procid, int,
            u_int16_t, u_int32_t, int, const struct iovec *, int);
-int     proc_forward_imsg(struct privsep *, struct imsg *,
-           enum privsep_procid, int);
 int     proc_composev(struct privsep *, enum privsep_procid,
            uint16_t, const struct iovec *, int);
+int     proc_forward_imsg(struct privsep *, struct imsg *,
+           enum privsep_procid, int);
 struct imsgbuf *
         proc_ibuf(struct privsep *, enum privsep_procid, int);
 struct imsgev *
         proc_iev(struct privsep *, enum privsep_procid, int);
 void    imsg_event_add(struct imsgev *);
-int     imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
-           pid_t, int, void *, u_int16_t);
-int     imsg_composev_event(struct imsgev *, u_int16_t, u_int32_t,
+int     imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
+           pid_t, int, void *, uint16_t);
+int     imsg_composev_event(struct imsgev *, uint16_t, uint32_t,
            pid_t, int, const struct iovec *, int);
 
 /* config.c */
Index: usr.sbin/relayd/proc.c
===================================================================
RCS file: /cvs/src/usr.sbin/relayd/proc.c,v
retrieving revision 1.30
diff -u -p -u -p -r1.30 proc.c
--- usr.sbin/relayd/proc.c      2 Sep 2016 12:14:08 -0000       1.30
+++ usr.sbin/relayd/proc.c      2 Sep 2016 16:49:54 -0000
@@ -1,4 +1,4 @@
-/*     $OpenBSD: proc.c,v 1.30 2016/09/02 12:14:08 reyk Exp $  */
+/*     $OpenBSD: proc.c,v 1.24 2016/09/02 11:25:14 reyk Exp $  */
 
 /*
  * Copyright (c) 2010 - 2014 Reyk Floeter <r...@openbsd.org>
@@ -34,8 +34,14 @@
 
 #include "relayd.h"
 
-void    proc_open(struct privsep *, struct privsep_proc *,
-           struct privsep_proc *, size_t);
+void    proc_exec(struct privsep *, struct privsep_proc *, unsigned int,
+           int, char **);
+void    proc_connectpeer(struct privsep *, enum privsep_procid, int,
+           struct privsep_pipes *);
+void    proc_setup(struct privsep *, struct privsep_proc *, unsigned int);
+void    proc_open(struct privsep *, int, int);
+void    proc_accept(struct privsep *, int, enum privsep_procid,
+           unsigned int);
 void    proc_close(struct privsep *);
 int     proc_ispeer(struct privsep_proc *, unsigned int, enum privsep_procid);
 void    proc_shutdown(struct privsep_proc *);
@@ -55,17 +61,264 @@ proc_ispeer(struct privsep_proc *procs, 
        return (0);
 }
 
+enum privsep_procid
+proc_getid(struct privsep_proc *procs, unsigned int nproc,
+    const char *proc_name)
+{
+       struct privsep_proc     *p;
+       unsigned int             proc;
+
+       for (proc = 0; proc < nproc; proc++) {
+               p = &procs[proc];
+               if (strcmp(p->p_title, proc_name))
+                       continue;
+
+               return (p->p_id);
+       }
+
+       return (PROC_MAX);
+}
+
+void
+proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
+    int argc, char **argv)
+{
+       unsigned int             proc, nargc, i, proc_i;
+       char                    **nargv;
+       struct privsep_proc     *p;
+       char                     num[32];
+       int                      fd;
+
+       /* Prepare the new process argv. */
+       nargv = calloc(argc + 5, sizeof(char *));
+       if (nargv == NULL)
+               fatal("%s: calloc", __func__);
+
+       /* Copy call argument first. */
+       nargc = 0;
+       nargv[nargc++] = argv[0];
+
+       /* Set process name argument and save the position. */
+       nargv[nargc++] = "-P";
+       proc_i = nargc;
+       nargc++;
+
+       /* Point process instance arg to stack and copy the original args. */
+       nargv[nargc++] = "-I";
+       nargv[nargc++] = num;
+       for (i = 1; i < (unsigned int) argc; i++)
+               nargv[nargc++] = argv[i];
+
+       nargv[nargc] = NULL;
+
+       for (proc = 0; proc < nproc; proc++) {
+               p = &procs[proc];
+
+               /* Update args with process title. */
+               nargv[proc_i] = (char *) p->p_title;
+
+               /* Fire children processes. */
+               for (i = 0; i < ps->ps_instances[p->p_id]; i++) {
+                       /* Update the process instance number. */
+                       snprintf(num, sizeof(num), "%u", i);
+
+                       fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0];
+                       ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1;
+
+                       switch (fork()) {
+                       case -1:
+                               fatal("%s: fork", __func__);
+                               break;
+                       case 0:
+                               /* Prepare parent socket. */
+                               dup2(fd, PARENT_SOCK_FILENO);
+
+                               execvp(argv[0], nargv);
+                               fatal("%s: execvp", __func__);
+                               break;
+                       default:
+                               /* Close child end. */
+                               close(fd);
+                               break;
+                       }
+               }
+       }
+       free(nargv);
+}
+
+void
+proc_connectpeer(struct privsep *ps, enum privsep_procid id, int inst,
+    struct privsep_pipes *pp)
+{
+       unsigned int             i, j;
+       struct privsep_fd        pf;
+
+       for (i = 0; i < PROC_MAX; i++) {
+               /* Parent is already connected with everyone. */
+               if (i == PROC_PARENT)
+                       continue;
+
+               for (j = 0; j < ps->ps_instances[i]; j++) {
+                       /* Don't send socket to child itself. */
+                       if (i == (unsigned int)id &&
+                           j == (unsigned int)inst)
+                               continue;
+                       if (pp->pp_pipes[i][j] == -1)
+                               continue;
+
+                       pf.pf_procid = i;
+                       pf.pf_instance = j;
+                       proc_compose_imsg(ps, id, inst, IMSG_CTL_PROCFD,
+                           -1, pp->pp_pipes[i][j], &pf, sizeof(pf));
+                       pp->pp_pipes[i][j] = -1;
+               }
+       }
+}
+
+/* Inter-connect all process except with ourself. */
+void
+proc_connect(struct privsep *ps)
+{
+       unsigned int             src, i, j;
+       struct privsep_pipes    *pp;
+       struct imsgev           *iev;
+
+       /* Listen on appropriate pipes. */
+       src = privsep_process;
+       pp = &ps->ps_pipes[src][ps->ps_instance];
+
+       for (i = 0; i < PROC_MAX; i++) {
+               /* Don't listen to ourself. */
+               if (i == src)
+                       continue;
+
+               for (j = 0; j < ps->ps_instances[i]; j++) {
+                       if (pp->pp_pipes[i][j] == -1)
+                               continue;
+
+                       iev = &ps->ps_ievs[i][j];
+                       imsg_init(&iev->ibuf, pp->pp_pipes[i][j]);
+                       event_set(&iev->ev, iev->ibuf.fd, iev->events,
+                           iev->handler, iev->data);
+                       event_add(&iev->ev, NULL);
+               }
+       }
+
+       /* Exchange pipes between process. */
+       for (i = 0; i < PROC_MAX; i++) {
+               /* Parent is already connected with everyone. */
+               if (i == PROC_PARENT)
+                       continue;
+
+               for (j = 0; j < ps->ps_instances[i]; j++)
+                       proc_connectpeer(ps, i, j, &ps->ps_pipes[i][j]);
+       }
+}
+
+void
+proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
+    int argc, char **argv, enum privsep_procid proc_id)
+{
+       struct privsep_proc     *p = NULL;
+       unsigned int             proc;
+       unsigned int             src, dst;
+
+       if (proc_id == PROC_PARENT) {
+               privsep_process = PROC_PARENT;
+               proc_setup(ps, procs, nproc);
+
+               /* Open socketpair()s for everyone. */
+               for (src = 0; src < PROC_MAX; src++)
+                       for (dst = 0; dst < PROC_MAX; dst++)
+                               proc_open(ps, src, dst);
+
+               /* Engage! */
+               proc_exec(ps, procs, nproc, argc, argv);
+               return;
+       }
+
+       /* Initialize a child */
+       for (proc = 0; proc < nproc; proc++) {
+               if (procs[proc].p_id != proc_id)
+                       continue;
+               p = &procs[proc];
+               break;
+       }
+       if (p == NULL || p->p_init == NULL)
+               fatalx("%s: process %d missing process initialization",
+                   __func__, proc_id);
+
+       p->p_init(ps, p);
+
+       fatalx("failed to initiate child process");
+}
+
+void
+proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
+    unsigned int n)
+{
+       struct privsep_pipes    *pp = ps->ps_pp;
+       struct imsgev           *iev;
+
+       if (ps->ps_ievs[dst] == NULL) {
+#if DEBUG > 1
+               log_debug("%s: %s src %d %d to dst %d %d not connected",
+                   __func__, ps->ps_title[privsep_process],
+                   privsep_process, ps->ps_instance + 1,
+                   dst, n + 1);
+#endif
+               close(fd);
+               return;
+       }
+
+       if (pp->pp_pipes[dst][n] != -1) {
+               log_warnx("%s: duplicated descriptor", __func__);
+               close(fd);
+               return;
+       } else
+               pp->pp_pipes[dst][n] = fd;
+
+       iev = &ps->ps_ievs[dst][n];
+       imsg_init(&iev->ibuf, fd);
+       event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
+       event_add(&iev->ev, NULL);
+}
+
 void
-proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
+proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
 {
-       unsigned int             i, j, src, dst;
+       unsigned int             i, j, src, dst, id;
        struct privsep_pipes    *pp;
 
+       /* Initialize parent title, ps_instances and procs. */
+       ps->ps_title[PROC_PARENT] = "parent";
+
        for (src = 0; src < PROC_MAX; src++)
                /* Default to 1 process instance */
                if (ps->ps_instances[src] < 1)
                        ps->ps_instances[src] = 1;
 
+       for (src = 0; src < nproc; src++) {
+               procs[src].p_ps = ps;
+               procs[src].p_env = ps->ps_env;
+               if (procs[src].p_cb == NULL)
+                       procs[src].p_cb = proc_dispatch_null;
+
+               id = procs[src].p_id;
+               ps->ps_title[id] = procs[src].p_title;
+               if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
+                   sizeof(struct imsgev))) == NULL)
+                       fatal(__func__);
+
+               /* With this set up, we are ready to call imsg_init(). */
+               for (i = 0; i < ps->ps_instances[id]; i++) {
+                       ps->ps_ievs[id][i].handler = proc_dispatch;
+                       ps->ps_ievs[id][i].events = EV_READ;
+                       ps->ps_ievs[id][i].proc = &procs[src];
+                       ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
+               }
+       }
+
        /*
         * Allocate pipes for all process instances (incl. parent)
         *
@@ -83,7 +336,7 @@ proc_init(struct privsep *ps, struct pri
                /* Allocate destination array for each process */
                if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
                    sizeof(struct privsep_pipes))) == NULL)
-                       fatal("proc_init: calloc");
+                       fatal("%s: calloc", __func__);
 
                for (i = 0; i < ps->ps_instances[src]; i++) {
                        pp = &ps->ps_pipes[src][i];
@@ -93,7 +346,7 @@ proc_init(struct privsep *ps, struct pri
                                if ((pp->pp_pipes[dst] =
                                    calloc(ps->ps_instances[dst],
                                    sizeof(int))) == NULL)
-                                       fatal("proc_init: calloc");
+                                       fatal("%s: calloc", __func__);
 
                                /* Mark fd as unused */
                                for (j = 0; j < ps->ps_instances[dst]; j++)
@@ -102,22 +355,7 @@ proc_init(struct privsep *ps, struct pri
                }
        }
 
-       /*
-        * Setup and run the parent and its children
-        */
-       privsep_process = PROC_PARENT;
-       ps->ps_instances[PROC_PARENT] = 1;
-       ps->ps_title[PROC_PARENT] = "parent";
-       ps->ps_pp = &ps->ps_pipes[privsep_process][0];
-
-       for (i = 0; i < nproc; i++)
-               ps->ps_title[procs[i].p_id] = procs[i].p_title;
-
-       proc_open(ps, NULL, procs, nproc);
-
-       /* Engage! */
-       for (i = 0; i < nproc; i++)
-               (*procs[i].p_init)(ps, &procs[i]);
+       ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
 }
 
 void
@@ -159,115 +397,30 @@ proc_kill(struct privsep *ps)
 }
 
 void
-proc_open(struct privsep *ps, struct privsep_proc *p,
-    struct privsep_proc *procs, size_t nproc)
+proc_open(struct privsep *ps, int src, int dst)
 {
        struct privsep_pipes    *pa, *pb;
        int                      fds[2];
-       unsigned int             i, j, src, proc;
-
-       if (p == NULL)
-               src = privsep_process; /* parent */
-       else
-               src = p->p_id;
-
-       /*
-        * Open socket pairs for our peers
-        */
-       for (proc = 0; proc < nproc; proc++) {
-               procs[proc].p_ps = ps;
-               procs[proc].p_env = ps->ps_env;
-               if (procs[proc].p_cb == NULL)
-                       procs[proc].p_cb = proc_dispatch_null;
-
-               for (i = 0; i < ps->ps_instances[src]; i++) {
-                       for (j = 0; j < ps->ps_instances[procs[proc].p_id];
-                           j++) {
-                               pa = &ps->ps_pipes[src][i];
-                               pb = &ps->ps_pipes[procs[proc].p_id][j];
-
-                               /* Check if fds are already set by peer */
-                               if (pa->pp_pipes[procs[proc].p_id][j] != -1)
-                                       continue;
-
-                               if (socketpair(AF_UNIX,
-                                   SOCK_STREAM | SOCK_NONBLOCK,
-                                   PF_UNSPEC, fds) == -1)
-                                       fatal("socketpair");
-
-                               pa->pp_pipes[procs[proc].p_id][j] = fds[0];
-                               pb->pp_pipes[src][i] = fds[1];
-                       }
-               }
-       }
-}
+       unsigned int             i, j;
 
-void
-proc_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc)
-{
-       unsigned int             i, dst, src, n, m;
-       struct privsep_pipes    *pp;
-
-       /*
-        * Close unused pipes
-        */
-       for (src = 0; src < PROC_MAX; src++) {
-               for (n = 0; n < ps->ps_instances[src]; n++) {
-                       /* Ingore current process */
-                       if (src == (unsigned int)privsep_process &&
-                           n == ps->ps_instance)
+       for (i = 0; i < ps->ps_instances[src]; i++) {
+               for (j = 0; j < ps->ps_instances[dst]; j++) {
+                       /* Don't create sockets for ourself. */
+                       if (src == dst && i == j)
                                continue;
 
-                       pp = &ps->ps_pipes[src][n];
-
-                       for (dst = 0; dst < PROC_MAX; dst++) {
-                               if (src == dst)
-                                       continue;
-                               for (m = 0; m < ps->ps_instances[dst]; m++) {
-                                       if (pp->pp_pipes[dst][m] == -1)
-                                               continue;
-
-                                       /* Close and invalidate fd */
-                                       close(pp->pp_pipes[dst][m]);
-                                       pp->pp_pipes[dst][m] = -1;
-                               }
-                       }
-               }
-       }
-
-       src = privsep_process;
-       ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance];
-
-       /*
-        * Listen on appropriate pipes
-        */
-       for (i = 0; i < nproc; i++) {
-               dst = procs[i].p_id;
-
-               if (src == dst)
-                       fatal("proc_listen: cannot peer with oneself");
-
-               if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst],
-                   sizeof(struct imsgev))) == NULL)
-                       fatal("proc_open");
-
-               for (n = 0; n < ps->ps_instances[dst]; n++) {
-                       if (pp->pp_pipes[dst][n] == -1)
+                       pa = &ps->ps_pipes[src][i];
+                       pb = &ps->ps_pipes[dst][j];
+                       if (pb->pp_pipes[dst][j] != -1)
                                continue;
 
-                       imsg_init(&(ps->ps_ievs[dst][n].ibuf),
-                           pp->pp_pipes[dst][n]);
-                       ps->ps_ievs[dst][n].handler = proc_dispatch;
-                       ps->ps_ievs[dst][n].events = EV_READ;
-                       ps->ps_ievs[dst][n].proc = &procs[i];
-                       ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
-
-                       event_set(&(ps->ps_ievs[dst][n].ev),
-                           ps->ps_ievs[dst][n].ibuf.fd,
-                           ps->ps_ievs[dst][n].events,
-                           ps->ps_ievs[dst][n].handler,
-                           ps->ps_ievs[dst][n].data);
-                       event_add(&(ps->ps_ievs[dst][n].ev), NULL);
+                       if (socketpair(AF_UNIX,
+                           SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
+                           PF_UNSPEC, fds) == -1)
+                               fatal(__func__);
+
+                       pa->pp_pipes[dst][j] = fds[0];
+                       pb->pp_pipes[src][i] = fds[1];
                }
        }
 }
@@ -316,7 +469,7 @@ proc_shutdown(struct privsep_proc *p)
 
        log_info("%s exiting, pid %d", p->p_title, getpid());
 
-       _exit(0);
+       exit(0);
 }
 
 void
@@ -346,32 +499,17 @@ proc_run(struct privsep *ps, struct priv
     struct privsep_proc *procs, unsigned int nproc,
     void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
 {
-       pid_t                    pid;
-       struct passwd           *pw;
+       struct passwd           *pw = ps->ps_pw;
        const char              *root;
        struct control_sock     *rcs;
-       unsigned int             n;
 
        if (ps->ps_noaction)
-               return;
-
-       proc_open(ps, p, procs, nproc);
+               exit(0);
 
-       /* Fork child handlers */
-       switch (pid = fork()) {
-       case -1:
-               fatal("proc_run: cannot fork");
-       case 0:
-               log_procinit(p->p_title);
-
-               /* Set the process group of the current process */
-               setpgid(0, 0);
-               break;
-       default:
-               return;
-       }
+       log_procinit(p->p_title);
 
-       pw = ps->ps_pw;
+       /* Set the process group of the current process */
+       setpgid(0, 0);
 
        if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
                if (control_init(ps, &ps->ps_csock) == -1)
@@ -401,19 +539,6 @@ proc_run(struct privsep *ps, struct priv
            setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
                fatal("proc_run: cannot drop privileges");
 
-       /* Fork child handlers */
-       for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
-               if (fork() == 0) {
-                       ps->ps_instance = n;
-                       break;
-               }
-       }
-
-#ifdef DEBUG
-       log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
-           ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
-#endif
-
        event_init();
 
        signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
@@ -430,8 +555,8 @@ proc_run(struct privsep *ps, struct priv
        signal_add(&ps->ps_evsigpipe, NULL);
        signal_add(&ps->ps_evsigusr1, NULL);
 
-       proc_listen(ps, procs, nproc);
-
+       proc_setup(ps, procs, nproc);
+       proc_accept(ps, PARENT_SOCK_FILENO, PROC_PARENT, 0);
        if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
                TAILQ_INIT(&ctl_conns);
                if (control_listen(&ps->ps_csock) == -1)
@@ -441,6 +566,11 @@ proc_run(struct privsep *ps, struct priv
                                fatalx(__func__);
        }
 
+#ifdef DEBUG
+       log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
+           ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
+#endif
+
        if (run != NULL)
                run(ps, p, arg);
 
@@ -460,6 +590,7 @@ proc_dispatch(int fd, short event, void 
        ssize_t                  n;
        int                      verbose;
        const char              *title;
+       struct privsep_fd        pf;
 
        title = ps->ps_title[privsep_process];
        ibuf = &iev->ibuf;
@@ -509,6 +640,12 @@ proc_dispatch(int fd, short event, void 
                        IMSG_SIZE_CHECK(&imsg, &verbose);
                        memcpy(&verbose, imsg.data, sizeof(verbose));
                        log_verbose(verbose);
+                       break;
+               case IMSG_CTL_PROCFD:
+                       IMSG_SIZE_CHECK(&imsg, &pf);
+                       memcpy(&pf, imsg.data, sizeof(pf));
+                       proc_accept(ps, imsg.fd, pf.pf_procid,
+                           pf.pf_instance);
                        break;
                default:
                        log_warnx("%s: %s %d got invalid imsg %d peerid %d "

Reply via email to