summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Kerr <jk@ozlabs.org>2018-08-13 13:13:16 +0800
committerJeremy Kerr <jk@ozlabs.org>2018-08-13 13:13:16 +0800
commitfa1d37502c87310886614949a8d72124762b2dcb (patch)
treece8360f408e65b211e5e524856605ef23293263c
parenta87af8462346f7db6cac87ba13bac5a4b20132c3 (diff)
downloadjsnbd-fa1d37502c87310886614949a8d72124762b2dcb.tar.gz
jsnbd-fa1d37502c87310886614949a8d72124762b2dcb.zip
nbd-proxy: run start hook asynchronously
Currently, we run the start hook synchronously, and wait() for it to complete. This means that the proxy process will be blocked during the execution of that hook, and no servicing any read/write requests from the nbd device. If any part of the hook implementation needs to access the device, it'll block waiting for the device (which is waiting for the nbd-proxy, which is waiting for the hook). This change runs the hook (for the "start" action) in the background, while still servicing requests. This allows hooks to access the device. Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
-rw-r--r--nbd-proxy.c59
1 files changed, 45 insertions, 14 deletions
diff --git a/nbd-proxy.c b/nbd-proxy.c
index 2db70df..f98feec 100644
--- a/nbd-proxy.c
+++ b/nbd-proxy.c
@@ -56,6 +56,7 @@ struct ctx {
int signal_pipe[2];
char *sock_path;
pid_t nbd_client_pid;
+ pid_t state_hook_pid;
int nbd_timeout;
dev_t nbd_devno;
uint8_t *buf;
@@ -278,9 +279,42 @@ static void cleanup_signals(struct ctx *ctx)
close(ctx->signal_pipe[1]);
}
+static void process_sigchld(struct ctx *ctx, bool *exit)
+{
+ int status;
+ pid_t pid;
+
+ for (;;) {
+ pid = waitpid(-1, &status, WNOHANG);
+ if (pid == 0)
+ break;
+
+ if (pid == ctx->nbd_client_pid) {
+ warnx("nbd client stopped (%s: %d); exiting",
+ WIFEXITED(status) ? "rc" : "sig",
+ WIFEXITED(status) ?
+ WEXITSTATUS(status) :
+ WTERMSIG(status));
+ ctx->nbd_client_pid = 0;
+ *exit = true;
+
+ } else if (pid == ctx->state_hook_pid) {
+ if (!WIFEXITED(status) || WEXITSTATUS(status)) {
+ warnx("state hook failed (%s: %d); exiting",
+ WIFEXITED(status) ? "rc" : "sig",
+ WIFEXITED(status) ?
+ WEXITSTATUS(status) :
+ WTERMSIG(status));
+ *exit = true;
+ }
+ ctx->state_hook_pid = 0;
+ }
+ }
+}
+
static int process_signal_pipe(struct ctx *ctx, bool *exit)
{
- int buf, rc, status;
+ int buf, rc;
rc = read(ctx->signal_pipe[0], &buf, sizeof(buf));
if (rc != sizeof(buf))
@@ -290,15 +324,7 @@ static int process_signal_pipe(struct ctx *ctx, bool *exit)
switch (buf) {
case SIGCHLD:
- rc = waitpid(ctx->nbd_client_pid, &status, WNOHANG);
- if (rc > 0) {
- warnx("nbd client stopped (%s: %d); exiting",
- WIFEXITED(status) ? "rc" : "sig",
- WIFEXITED(status) ?
- WEXITSTATUS(status) :
- WTERMSIG(status));
- ctx->nbd_client_pid = 0;
- }
+ process_sigchld(ctx, exit);
break;
case SIGINT:
case SIGTERM:
@@ -350,14 +376,14 @@ static int wait_for_nbd_client(struct ctx *ctx)
return 0;
}
-static int run_state_hook(struct ctx *ctx, const char *action)
+static int run_state_hook(struct ctx *ctx, const char *action, bool wait)
{
int status, rc, fd;
pid_t pid;
/* if the hook isn't present or executable, that's not necessarily
* an error condition */
- if (!access(state_hook_path, X_OK))
+ if (access(state_hook_path, X_OK))
return 0;
pid = fork();
@@ -384,6 +410,11 @@ static int run_state_hook(struct ctx *ctx, const char *action)
exit(EXIT_FAILURE);
}
+ if (!wait) {
+ ctx->state_hook_pid = pid;
+ return 0;
+ }
+
rc = waitpid(pid, &status, 0);
if (rc < 0) {
warn("wait");
@@ -476,7 +507,7 @@ static int udev_process(struct ctx *ctx)
ctx->monitor = NULL;
ctx->udev = NULL;
- rc = run_state_hook(ctx, "start");
+ rc = run_state_hook(ctx, "start", false);
return rc;
}
@@ -826,7 +857,7 @@ int main(int argc, char **argv)
if (ctx->udev)
udev_free(ctx);
- run_state_hook(ctx, "stop");
+ run_state_hook(ctx, "stop", true);
out_stop_client:
/* we cleanup signals before stopping the client, because we
OpenPOWER on IntegriCloud