From: Stefan Sperling Subject: Re: gotd: fork repo children on demand To: gameoftrees@openbsd.org Date: Tue, 27 Dec 2022 09:21:31 +0100 On Mon, Dec 26, 2022 at 06:22:06PM +0100, Stefan Sperling wrote: > Fork gotd repo children on demand, instead of just once at startup. > This is another step on the road towards better privsep. > > For now, we need to add the "exec" pledge promise back to the parent process. > It will be removed again in a later step. > > ok? The previous version of this patch had two problems: - The parent did not wait for repo children after sending them a SIGTERM, which could lead to zombie processes accumulating. - The parent forgot to free its struct proc during disconnect(), resulting in a memory leak. Both issues fixed here. diff 9d0feb8b5d4a20276efaf3f29df59ade82cd38aa 34d54e2690024fb12b89c1c160170efaa286c2df commit - 9d0feb8b5d4a20276efaf3f29df59ade82cd38aa commit + 34d54e2690024fb12b89c1c160170efaa286c2df blob - 97731f1e9b245bfea3cec151f3d56b6c81205390 blob + 3040877532eed07c6a4bd0d310bf4b9564ceab06 --- gotd/gotd.c +++ gotd/gotd.c @@ -96,6 +96,9 @@ static void gotd_shutdown(void); void gotd_sighdlr(int sig, short event, void *arg); static void gotd_shutdown(void); +static const struct got_error *start_repo_child(struct gotd_client *, + enum gotd_procid, struct gotd_repo *, char *, const char *, int, int); +static void kill_proc(struct gotd_child_proc *, int); __dead static void usage() @@ -234,6 +237,23 @@ static int return NULL; } +static struct gotd_client * +find_client_by_proc_fd(int fd) +{ + uint64_t slot; + + for (slot = 0; slot < nitems(gotd_clients); slot++) { + struct gotd_client *c; + STAILQ_FOREACH(c, &gotd_clients[slot], entry) { + struct gotd_child_proc *proc = get_client_proc(c); + if (proc && proc->iev.ibuf.fd == fd) + return c; + } + } + + return NULL; +} + static int client_is_reading(struct gotd_client *client) { @@ -295,11 +315,35 @@ disconnect(struct gotd_client *client) } static void +wait_for_children(pid_t child_pid) +{ + pid_t pid; + int status; + + if (child_pid == 0) + log_debug("waiting for children to terminate"); + else + log_debug("waiting for child PID %ld to terminate", + (long)child_pid); + + do { + pid = wait(&status); + if (pid == -1) { + if (errno != EINTR && errno != ECHILD) + fatal("wait"); + } else if (WIFSIGNALED(status)) { + log_warnx("child PID %ld terminated; signal %d", + (long)pid, WTERMSIG(status)); + } + } while (pid != -1 || (pid == -1 && errno == EINTR)); +} + +static void disconnect(struct gotd_client *client) { struct gotd_imsg_disconnect idisconnect; struct gotd_child_proc *proc = get_client_proc(client); - struct gotd_child_proc *listen_proc = &gotd.procs[0]; + struct gotd_child_proc *listen_proc = &gotd.listen_proc; uint64_t slot; log_debug("uid %d: disconnecting", client->euid); @@ -310,6 +354,13 @@ disconnect(struct gotd_client *client) GOTD_IMSG_DISCONNECT, PROC_GOTD, -1, &idisconnect, sizeof(idisconnect)) == -1) log_warn("imsg compose DISCONNECT"); + + msgbuf_clear(&proc->iev.ibuf.w); + close(proc->iev.ibuf.fd); + kill_proc(proc, 0); + wait_for_children(proc->pid); + free(proc); + proc = NULL; } if (gotd_imsg_compose_event(&listen_proc->iev, @@ -533,53 +584,13 @@ static struct gotd_child_proc * return NULL; } -static struct gotd_child_proc * -find_proc_by_repo_name(enum gotd_procid proc_id, const char *repo_name) -{ - struct gotd_child_proc *proc; - int i; - size_t namelen; - - for (i = 0; i < gotd.nprocs; i++) { - proc = &gotd.procs[i]; - if (proc->type != proc_id) - continue; - namelen = strlen(proc->repo_name); - if (strncmp(proc->repo_name, repo_name, namelen) != 0) - continue; - if (repo_name[namelen] == '\0' || - strcmp(&repo_name[namelen], ".git") == 0) - return proc; - } - - return NULL; -} - -static struct gotd_child_proc * -find_proc_by_fd(int fd) -{ - struct gotd_child_proc *proc; - int i; - - for (i = 0; i < gotd.nprocs; i++) { - proc = &gotd.procs[i]; - if (proc->iev.ibuf.fd == fd) - return proc; - } - - return NULL; -} - static const struct got_error * -forward_list_refs_request(struct gotd_client *client, struct imsg *imsg) +start_client_session(struct gotd_client *client, struct imsg *imsg) { const struct got_error *err; struct gotd_imsg_list_refs ireq; - struct gotd_imsg_list_refs_internal ilref; struct gotd_repo *repo = NULL; - struct gotd_child_proc *proc = NULL; size_t datalen; - int fd = -1; log_debug("list-refs request from uid %d", client->euid); @@ -589,9 +600,6 @@ forward_list_refs_request(struct gotd_client *client, memcpy(&ireq, imsg->data, datalen); - memset(&ilref, 0, sizeof(ilref)); - ilref.client_id = client->id; - if (ireq.client_is_reading) { err = ensure_client_is_not_writing(client); if (err) @@ -603,10 +611,11 @@ forward_list_refs_request(struct gotd_client *client, client->euid, client->egid, GOTD_AUTH_READ); if (err) return err; - client->repo_read = find_proc_by_repo_name(PROC_REPO_READ, - ireq.repo_name); - if (client->repo_read == NULL) - return got_error(GOT_ERR_NOT_GIT_REPO); + err = start_repo_child(client, PROC_REPO_READ, repo, + gotd.argv0, gotd.confpath, gotd.daemonize, + gotd.verbosity); + if (err) + return err; } else { err = ensure_client_is_not_reading(client); if (err) @@ -618,27 +627,14 @@ forward_list_refs_request(struct gotd_client *client, client->egid, GOTD_AUTH_READ | GOTD_AUTH_WRITE); if (err) return err; - client->repo_write = find_proc_by_repo_name(PROC_REPO_WRITE, - ireq.repo_name); - if (client->repo_write == NULL) - return got_error(GOT_ERR_NOT_GIT_REPO); + err = start_repo_child(client, PROC_REPO_WRITE, repo, + gotd.argv0, gotd.confpath, gotd.daemonize, + gotd.verbosity); + if (err) + return err; } - fd = dup(client->fd); - if (fd == -1) - return got_error_from_errno("dup"); - - proc = get_client_proc(client); - if (proc == NULL) - fatalx("no process found for uid %d", client->euid); - if (gotd_imsg_compose_event(&proc->iev, - GOTD_IMSG_LIST_REFS_INTERNAL, PROC_GOTD, fd, - &ilref, sizeof(ilref)) == -1) { - err = got_error_from_errno("imsg compose WANT"); - close(fd); - return err; - } - + /* List-refs request will be forwarded once the child is ready. */ return NULL; } @@ -1040,12 +1036,9 @@ gotd_request(int fd, short events, void *arg) "unexpected list-refs request received"); break; } - err = forward_list_refs_request(client, &imsg); + err = start_client_session(client, &imsg); if (err) break; - client->state = GOTD_STATE_EXPECT_CAPABILITIES; - log_debug("uid %d: expecting capabilities", - client->euid); break; case GOTD_IMSG_CAPABILITIES: if (client->state != GOTD_STATE_EXPECT_CAPABILITIES) { @@ -1269,7 +1262,7 @@ done: client->euid, client->fd); done: if (err) { - struct gotd_child_proc *listen_proc = &gotd.procs[0]; + struct gotd_child_proc *listen_proc = &gotd.listen_proc; struct gotd_imsg_disconnect idisconnect; idisconnect.client_id = client->id; @@ -1292,21 +1285,6 @@ static struct gotd_child_proc * "repo_write" }; -static struct gotd_child_proc * -get_proc_for_pid(pid_t pid) -{ - struct gotd_child_proc *proc; - int i; - - for (i = 0; i < gotd.nprocs; i++) { - proc = &gotd.procs[i]; - if (proc->pid == pid) - return proc; - } - - return NULL; -} - static void kill_proc(struct gotd_child_proc *proc, int fatal) { @@ -1320,30 +1298,20 @@ gotd_shutdown(void) static void gotd_shutdown(void) { - pid_t pid; - int status, i; struct gotd_child_proc *proc; + uint64_t slot; - for (i = 0; i < gotd.nprocs; i++) { - proc = &gotd.procs[i]; - msgbuf_clear(&proc->iev.ibuf.w); - close(proc->iev.ibuf.fd); - kill_proc(proc, 0); + for (slot = 0; slot < nitems(gotd_clients); slot++) { + struct gotd_client *c, *tmp; + STAILQ_FOREACH_SAFE(c, &gotd_clients[slot], entry, tmp) + disconnect(c); } - log_debug("waiting for children to terminate"); - do { - pid = wait(&status); - if (pid == -1) { - if (errno != EINTR && errno != ECHILD) - fatal("wait"); - } else if (WIFSIGNALED(status)) { - proc = get_proc_for_pid(pid); - log_warnx("%s %s child process terminated; signal %d", - proc ? gotd_proc_names[proc->type] : "", - proc ? proc->repo_path : "", WTERMSIG(status)); - } - } while (pid != -1 || (pid == -1 && errno == EINTR)); + proc = &gotd.listen_proc; + msgbuf_clear(&proc->iev.ibuf.w); + close(proc->iev.ibuf.fd); + kill_proc(proc, 0); + wait_for_children(proc->pid); log_info("terminating"); exit(0); @@ -1439,6 +1407,15 @@ verify_imsg_src(struct gotd_client *client, struct got } else ret = 1; break; + case GOTD_IMSG_REPO_CHILD_READY: + if (proc->type != PROC_REPO_READ && + proc->type != PROC_REPO_WRITE) { + err = got_error_fmt(GOT_ERR_BAD_PACKET, + "unexpected \"ready\" signal from PID %d", + proc->pid); + } else + ret = 1; + break; case GOTD_IMSG_PACKFILE_DONE: err = ensure_proc_is_reading(client, proc); if (err) @@ -1464,6 +1441,32 @@ recv_packfile_done(uint32_t *client_id, struct imsg *i } static const struct got_error * +list_refs_request(struct gotd_client *client, struct gotd_imsgev *iev) +{ + static const struct got_error *err; + struct gotd_imsg_list_refs_internal ilref; + int fd; + + memset(&ilref, 0, sizeof(ilref)); + ilref.client_id = client->id; + + fd = dup(client->fd); + if (fd == -1) + return got_error_from_errno("dup"); + + if (gotd_imsg_compose_event(iev, GOTD_IMSG_LIST_REFS_INTERNAL, + PROC_GOTD, fd, &ilref, sizeof(ilref)) == -1) { + err = got_error_from_errno("imsg compose WANT"); + close(fd); + return err; + } + + client->state = GOTD_STATE_EXPECT_CAPABILITIES; + log_debug("uid %d: expecting capabilities", client->euid); + return NULL; +} + +static const struct got_error * recv_packfile_done(uint32_t *client_id, struct imsg *imsg) { struct gotd_imsg_packfile_done idone; @@ -1816,15 +1819,18 @@ gotd_dispatch(int fd, short event, void *arg) } static void -gotd_dispatch(int fd, short event, void *arg) +gotd_dispatch_listener(int fd, short event, void *arg) { struct gotd_imsgev *iev = arg; struct imsgbuf *ibuf = &iev->ibuf; - struct gotd_child_proc *proc = NULL; + struct gotd_child_proc *proc = &gotd.listen_proc; ssize_t n; int shut = 0; struct imsg imsg; + if (proc->iev.ibuf.fd != fd) + fatalx("%s: unexpected fd %d", __func__, fd); + if (event & EV_READ) { if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) fatal("imsg_read error"); @@ -1846,17 +1852,11 @@ gotd_dispatch(int fd, short event, void *arg) } } - proc = find_proc_by_fd(fd); - if (proc == NULL) - fatalx("cannot find child process for fd %d", fd); - for (;;) { const struct got_error *err = NULL; struct gotd_client *client = NULL; uint32_t client_id = 0; int do_disconnect = 0; - int do_ref_updates = 0, do_ref_update = 0; - int do_packfile_install = 0; if ((n = imsg_get(ibuf, &imsg)) == -1) fatal("%s: imsg_get error", __func__); @@ -1871,6 +1871,100 @@ gotd_dispatch(int fd, short event, void *arg) case GOTD_IMSG_CONNECT: err = recv_connect(&client_id, &imsg); break; + default: + log_debug("unexpected imsg %d", imsg.hdr.type); + break; + } + + client = find_client(client_id); + if (client == NULL) { + log_warnx("%s: client not found", __func__); + imsg_free(&imsg); + continue; + } + + if (err) + log_warnx("uid %d: %s", client->euid, err->msg); + + if (do_disconnect) { + if (err) + disconnect_on_error(client, err); + else + disconnect(client); + } + + imsg_free(&imsg); + } +done: + if (!shut) { + gotd_imsg_event_add(iev); + } else { + /* This pipe is dead. Remove its event handler */ + event_del(&iev->ev); + event_loopexit(NULL); + } +} + +static void +gotd_dispatch_repo_child(int fd, short event, void *arg) +{ + struct gotd_imsgev *iev = arg; + struct imsgbuf *ibuf = &iev->ibuf; + struct gotd_child_proc *proc = NULL; + struct gotd_client *client = NULL; + ssize_t n; + int shut = 0; + struct imsg imsg; + + if (event & EV_READ) { + if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN) + fatal("imsg_read error"); + if (n == 0) { + /* Connection closed. */ + shut = 1; + goto done; + } + } + + if (event & EV_WRITE) { + n = msgbuf_write(&ibuf->w); + if (n == -1 && errno != EAGAIN) + fatal("msgbuf_write"); + if (n == 0) { + /* Connection closed. */ + shut = 1; + goto done; + } + } + + client = find_client_by_proc_fd(fd); + if (client == NULL) + fatalx("cannot find client for fd %d", fd); + + proc = get_client_proc(client); + if (proc == NULL) + fatalx("cannot find child process for fd %d", fd); + + for (;;) { + const struct got_error *err = NULL; + uint32_t client_id = 0; + int do_disconnect = 0; + int do_list_refs = 0, do_ref_updates = 0, do_ref_update = 0; + int do_packfile_install = 0; + + if ((n = imsg_get(ibuf, &imsg)) == -1) + fatal("%s: imsg_get error", __func__); + if (n == 0) /* No more messages. */ + break; + + switch (imsg.hdr.type) { + case GOTD_IMSG_ERROR: + do_disconnect = 1; + err = gotd_imsg_recv_error(&client_id, &imsg); + break; + case GOTD_IMSG_REPO_CHILD_READY: + do_list_refs = 1; + break; case GOTD_IMSG_PACKFILE_DONE: do_disconnect = 1; err = recv_packfile_done(&client_id, &imsg); @@ -1895,13 +1989,6 @@ gotd_dispatch(int fd, short event, void *arg) break; } - client = find_client(client_id); - if (client == NULL) { - log_warnx("%s: client not found", __func__); - imsg_free(&imsg); - continue; - } - if (!verify_imsg_src(client, proc, &imsg)) { log_debug("dropping imsg type %d from PID %d", imsg.hdr.type, proc->pid); @@ -1917,7 +2004,9 @@ gotd_dispatch(int fd, short event, void *arg) else disconnect(client); } else { - if (do_packfile_install) + if (do_list_refs) + err = list_refs_request(client, iev); + else if (do_packfile_install) err = install_pack(client, proc->repo_path, &imsg); else if (do_ref_updates) @@ -2002,7 +2091,7 @@ start_listener(char *argv0, const char *confpath, int static void start_listener(char *argv0, const char *confpath, int daemonize, int verbosity) { - struct gotd_child_proc *proc = &gotd.procs[0]; + struct gotd_child_proc *proc = &gotd.listen_proc; proc->type = PROC_LISTEN; @@ -2013,50 +2102,55 @@ start_listener(char *argv0, const char *confpath, int proc->pid = start_child(proc->type, NULL, argv0, confpath, proc->pipe[1], daemonize, verbosity); imsg_init(&proc->iev.ibuf, proc->pipe[0]); - proc->iev.handler = gotd_dispatch; + proc->iev.handler = gotd_dispatch_listener; proc->iev.events = EV_READ; proc->iev.handler_arg = NULL; } -static void -start_repo_children(struct gotd *gotd, char *argv0, const char *confpath, +static const struct got_error * +start_repo_child(struct gotd_client *client, enum gotd_procid proc_type, + struct gotd_repo *repo, char *argv0, const char *confpath, int daemonize, int verbosity) { - struct gotd_repo *repo = NULL; struct gotd_child_proc *proc; - int i; - for (i = 1; i < gotd->nprocs; i++) { - if (repo == NULL) - repo = TAILQ_FIRST(&gotd->repos); - proc = &gotd->procs[i]; - if (i - 1 < gotd->nrepos) - proc->type = PROC_REPO_READ; - else - proc->type = PROC_REPO_WRITE; - if (strlcpy(proc->repo_name, repo->name, - sizeof(proc->repo_name)) >= sizeof(proc->repo_name)) - fatalx("repository name too long: %s", repo->name); - log_debug("adding repository %s", repo->name); - if (realpath(repo->path, proc->repo_path) == NULL) - fatal("%s", repo->path); - if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, - PF_UNSPEC, proc->pipe) == -1) - fatal("socketpair"); - proc->pid = start_child(proc->type, proc->repo_path, argv0, - confpath, proc->pipe[1], daemonize, verbosity); - imsg_init(&proc->iev.ibuf, proc->pipe[0]); - log_debug("proc %s %s is on fd %d", - gotd_proc_names[proc->type], proc->repo_path, - proc->pipe[0]); - proc->iev.handler = gotd_dispatch; - proc->iev.events = EV_READ; - proc->iev.handler_arg = NULL; - event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ, - gotd_dispatch, &proc->iev); + if (proc_type != PROC_REPO_READ && proc_type != PROC_REPO_WRITE) + return got_error_msg(GOT_ERR_NOT_IMPL, "bad process type"); + + proc = calloc(1, sizeof(*proc)); + if (proc == NULL) + return got_error_from_errno("calloc"); - repo = TAILQ_NEXT(repo, entry); - } + proc->type = proc_type; + if (strlcpy(proc->repo_name, repo->name, + sizeof(proc->repo_name)) >= sizeof(proc->repo_name)) + fatalx("repository name too long: %s", repo->name); + log_debug("starting %s for repository %s", + proc->type == PROC_REPO_READ ? "reader" : "writer", repo->name); + if (realpath(repo->path, proc->repo_path) == NULL) + fatal("%s", repo->path); + if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, + PF_UNSPEC, proc->pipe) == -1) + fatal("socketpair"); + proc->pid = start_child(proc->type, proc->repo_path, argv0, + confpath, proc->pipe[1], daemonize, verbosity); + imsg_init(&proc->iev.ibuf, proc->pipe[0]); + log_debug("proc %s %s is on fd %d", + gotd_proc_names[proc->type], proc->repo_path, + proc->pipe[0]); + proc->iev.handler = gotd_dispatch_repo_child; + proc->iev.events = EV_READ; + proc->iev.handler_arg = NULL; + event_set(&proc->iev.ev, proc->iev.ibuf.fd, EV_READ, + gotd_dispatch_repo_child, &proc->iev); + gotd_imsg_event_add(&proc->iev); + + if (proc->type == PROC_REPO_READ) + client->repo_read = proc; + else + client->repo_write = proc; + + return NULL; } static void @@ -2074,6 +2168,9 @@ apply_unveil(void) { struct gotd_repo *repo; + if (unveil(gotd.argv0, "x") == -1) + fatal("unveil %s", gotd.argv0); + TAILQ_FOREACH(repo, &gotd.repos, entry) { if (unveil(repo->path, "rwc") == -1) fatal("unveil %s", repo->path); @@ -2145,7 +2242,11 @@ main(int argc, char **argv) if (argc != 0) usage(); - if (geteuid()) + /* Require an absolute path in argv[0] for reliable re-exec. */ + if (!got_path_is_absolute(argv0)) + fatalx("bad path \"%s\": must be an absolute path", argv0); + + if (geteuid() && (proc_id == PROC_GOTD || proc_id == PROC_LISTEN)) fatalx("need root privileges"); log_init(daemonize ? 0 : 1, LOG_DAEMON); @@ -2154,6 +2255,11 @@ main(int argc, char **argv) if (parse_config(confpath, proc_id, &gotd) != 0) return 1; + gotd.argv0 = argv0; + gotd.daemonize = daemonize; + gotd.verbosity = verbosity; + gotd.confpath = confpath; + if (proc_id == PROC_GOTD && (gotd.nrepos == 0 || TAILQ_EMPTY(&gotd.repos))) fatalx("no repository defined in configuration file"); @@ -2196,18 +2302,7 @@ main(int argc, char **argv) if (proc_id == PROC_GOTD) { gotd.pid = getpid(); snprintf(title, sizeof(title), "%s", gotd_proc_names[proc_id]); - /* - * Start a listener and repository readers/writers. - * XXX For now, use one reader and one writer per repository. - * This should be changed to N readers + M writers. - */ - gotd.nprocs = 1 + gotd.nrepos * 2; - gotd.procs = calloc(gotd.nprocs, sizeof(*gotd.procs)); - if (gotd.procs == NULL) - fatal("calloc"); start_listener(argv0, confpath, daemonize, verbosity); - start_repo_children(&gotd, argv0, confpath, daemonize, - verbosity); arc4random_buf(&clients_hash_key, sizeof(clients_hash_key)); if (daemonize && daemon(1, 0) == -1) fatal("daemon"); @@ -2257,8 +2352,8 @@ main(int argc, char **argv) switch (proc_id) { case PROC_GOTD: #ifndef PROFILE - if (pledge("stdio rpath wpath cpath proc getpw sendfd recvfd " - "fattr flock unix unveil", NULL) == -1) + if (pledge("stdio rpath wpath cpath proc exec getpw " + "sendfd recvfd fattr flock unix unveil", NULL) == -1) err(1, "pledge"); #endif break; @@ -2308,7 +2403,7 @@ main(int argc, char **argv) signal_add(&evsighup, NULL); signal_add(&evsigusr1, NULL); - gotd_imsg_event_add(&gotd.procs[0].iev); + gotd_imsg_event_add(&gotd.listen_proc.iev); event_dispatch(); blob - f7524de7c95a08df6542dacc78d3295c249be091 blob + c9660ad1b2468eb23cf0f8fbd3bb1937ac6e8449 --- gotd/gotd.h +++ gotd/gotd.h @@ -114,9 +114,12 @@ struct gotd { char user_name[32]; struct gotd_repolist repos; int nrepos; + struct gotd_child_proc listen_proc; + + char *argv0; + const char *confpath; + int daemonize; int verbosity; - struct gotd_child_proc *procs; - int nprocs; }; enum gotd_imsg_type { @@ -172,6 +175,9 @@ enum gotd_imsg_type { /* Client connections. */ GOTD_IMSG_DISCONNECT, GOTD_IMSG_CONNECT, + + /* Child process management. */ + GOTD_IMSG_REPO_CHILD_READY, }; /* Structure for GOTD_IMSG_ERROR. */ blob - 4716f4da690117859980910c5c467ea48b7fa838 blob + 84ad53e727b231bc0df858b1927be8682b139f1a --- gotd/repo_read.c +++ gotd/repo_read.c @@ -892,8 +892,10 @@ repo_read_main(const char *title, const char *repo_pat iev.events = EV_READ; iev.handler_arg = NULL; event_set(&iev.ev, iev.ibuf.fd, EV_READ, repo_read_dispatch, &iev); - if (event_add(&iev.ev, NULL) == -1) { - err = got_error_from_errno("event_add"); + + if (gotd_imsg_compose_event(&iev, GOTD_IMSG_REPO_CHILD_READY, + PROC_REPO_READ, -1, NULL, 0) == -1) { + err = got_error_from_errno("imsg compose REPO_CHILD_READY"); goto done; } blob - 2b06c2f7956001962fa2df8ef028f9f1ac519b4f blob + a8b8a48b519156b15cdfed829ea3090d0552fc1a --- gotd/repo_write.c +++ gotd/repo_write.c @@ -1425,8 +1425,9 @@ repo_write_main(const char *title, const char *repo_pa iev.events = EV_READ; iev.handler_arg = NULL; event_set(&iev.ev, iev.ibuf.fd, EV_READ, repo_write_dispatch, &iev); - if (event_add(&iev.ev, NULL) == -1) { - err = got_error_from_errno("event_add"); + if (gotd_imsg_compose_event(&iev, GOTD_IMSG_REPO_CHILD_READY, + PROC_REPO_WRITE, -1, NULL, 0) == -1) { + err = got_error_from_errno("imsg compose REPO_CHILD_READY"); goto done; } blob - 7651eedffa069eddf47c6afc4e3e0c31f144ae5c blob + 0fe6926134b97333a0a4a221f988bb8f85ab7242 --- regress/gotd/Makefile +++ regress/gotd/Makefile @@ -1,3 +1,5 @@ +.include "../../got-version.mk" + REGRESS_TARGETS=test_repo_read test_repo_read_group \ test_repo_read_denied_user test_repo_read_denied_group \ test_repo_read_bad_user test_repo_read_bad_group \ @@ -26,8 +28,16 @@ GOTD_START_CMD=../../gotd/obj/gotd -vv -f $(PWD)/gotd. GOTD_GROUP?=gotsh GOTD_SOCK=${GOTD_DEVUSER_HOME}/gotd.sock -GOTD_START_CMD=../../gotd/obj/gotd -vv -f $(PWD)/gotd.conf -GOTD_STOP_CMD=../../gotctl/obj/gotctl -f $(GOTD_SOCK) stop +.if "${GOT_RELEASE}" == "Yes" +PREFIX ?= /usr/local +BINDIR ?= ${PREFIX}/bin +.else +PREFIX ?= ${GOTD_TEST_USER_HOME} +BINDIR ?= ${PREFIX}/bin +.endif + +GOTD_START_CMD?=$(BINDIR)/gotd -vv -f $(PWD)/gotd.conf +GOTD_STOP_CMD?=$(BINDIR)/gotctl -f $(GOTD_SOCK) stop GOTD_TRAP=trap "$(GOTD_STOP_CMD)" HUP INT QUIT PIPE TERM GOTD_TEST_ENV=GOTD_TEST_ROOT=$(GOTD_TEST_ROOT) \ blob - 5a81eccc64738c41542ac67a0c0695628e573319 blob + 09b2d5993cc7481df50e85fbda4705700de997bd --- regress/gotd/README +++ regress/gotd/README @@ -30,7 +30,10 @@ Tests will run the locally built gotd binary found in available in a non-standard PATH directory such as ~gotdev/bin, the gotdev user's PATH must be set appropriately in sshd_config (see below). -Tests will run the locally built gotd binary found in gotd/obj/gotd. +By default, tests will run the gotd binary found in ~/bin. +If sources were unpacked from a Got release tarball then tests will run +/usr/local/bin/gotd by default instead. + The test suite creates the corresponding gotd socket in ~gotdev/gotd.sock. To make this work, the GOTD_UNIX_SOCKET variable must be set by sshd when the gotdev user logs in. The following should be added to the file