commit - 898c8f8fd003d730d0490c1a5a807e4c11e30f4b
commit + b50a2b4639132c68255480f1c5c8785fd7cd8094
blob - 32ce54f037478054277fa8c9a5df079d8f8aa325
blob + a128e4ee286c5728743f77a63585f71ec9b483a6
--- gotd/gotd.c
+++ gotd/gotd.c
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()
return client->repo_read;
else if (client->repo_write)
return client->repo_write;
+
+ 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;
}
}
return NULL;
+}
+
+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
{
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);
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,
if (repo_name[namelen] == '\0' ||
strcmp(&repo_name[namelen], ".git") == 0)
return repo;
- }
-
- 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);
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)
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)
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);
- }
-
- 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;
+ err = start_repo_child(client, PROC_REPO_WRITE, repo,
+ gotd.argv0, gotd.confpath, gotd.daemonize,
+ gotd.verbosity);
+ if (err)
+ return err;
}
+ /* List-refs request will be forwarded once the child is ready. */
return NULL;
}
"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) {
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;
"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)
{
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);
} 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)
}
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;
}
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 = &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");
+ 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;
+ }
+ }
+
+ for (;;) {
+ const struct got_error *err = NULL;
+ struct gotd_client *client = NULL;
+ uint32_t client_id = 0;
+ int do_disconnect = 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_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;
}
}
- proc = find_proc_by_fd(fd);
+ 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;
- 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_list_refs = 0, do_ref_updates = 0, do_ref_update = 0;
int do_packfile_install = 0;
if ((n = imsg_get(ibuf, &imsg)) == -1)
do_disconnect = 1;
err = gotd_imsg_recv_error(&client_id, &imsg);
break;
- case GOTD_IMSG_CONNECT:
- err = recv_connect(&client_id, &imsg);
+ case GOTD_IMSG_REPO_CHILD_READY:
+ do_list_refs = 1;
break;
case GOTD_IMSG_PACKFILE_DONE:
do_disconnect = 1;
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);
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)
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;
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
{
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);
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);
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");
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");
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;
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
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 {
/* 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
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
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
+.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 \
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
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