From cebc988dd4b8372b84747a132d0e2babaa84e988 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Sun, 11 Oct 2009 08:58:05 +0000 Subject: [PATCH] Switch run-shell over to queue the command in the background like #(). --- cmd-run-shell.c | 115 +++++++++++++++++++++++++++++++++--------------- job.c | 2 +- server.c | 10 +++-- tmux.1 | 4 +- 4 files changed, 89 insertions(+), 42 deletions(-) diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 71e5b96b..d00571a2 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -29,6 +29,9 @@ int cmd_run_shell_exec(struct cmd *, struct cmd_ctx *); +void cmd_run_shell_callback(struct job *); +void cmd_run_shell_free(void *); + const struct cmd_entry cmd_run_shell_entry = { "run-shell", "run", "command", @@ -40,57 +43,97 @@ const struct cmd_entry cmd_run_shell_entry = { cmd_target_print }; +struct cmd_run_shell_data { + char *cmd; + struct cmd_ctx ctx; +}; + int cmd_run_shell_exec(struct cmd *self, struct cmd_ctx *ctx) { - struct cmd_target_data *data = self->data; - FILE *fp; - char *buf, *lbuf, *msg; - size_t len; - int has_output, ret, status; + struct cmd_target_data *data = self->data; + struct cmd_run_shell_data *cdata; + struct job *job; - if ((fp = popen(data->arg, "r")) == NULL) { - ctx->error(ctx, "popen error"); - return (-1); - } + cdata = xmalloc(sizeof *cdata); + cdata->cmd = xstrdup(data->arg); + memcpy(&cdata->ctx, ctx, sizeof cdata->ctx); - has_output = 0; - lbuf = NULL; - while ((buf = fgetln(fp, &len)) != NULL) { - if (buf[len - 1] == '\n') - buf[len - 1] = '\0'; - else { - lbuf = xmalloc(len + 1); - memcpy(lbuf, buf, len); - lbuf[len] = '\0'; - buf = lbuf; + if (ctx->cmdclient != NULL) + ctx->cmdclient->references++; + if (ctx->curclient != NULL) + ctx->curclient->references++; + + job = job_add(NULL, NULL, + data->arg, cmd_run_shell_callback, cmd_run_shell_free, cdata); + job_run(job); + + return (1); /* don't let client exit */ +} + +void +cmd_run_shell_callback(struct job *job) +{ + struct cmd_run_shell_data *cdata = job->data; + struct cmd_ctx *ctx = &cdata->ctx; + char *cmd, *msg, *line, *buf; + size_t off, len, llen; + int retcode; + + buf = BUFFER_OUT(job->out); + len = BUFFER_USED(job->out); + + cmd = cdata->cmd; + + if (len != 0) { + line = buf; + for (off = 0; off < len; off++) { + if (buf[off] == '\n') { + llen = buf + off - line; + if (llen > INT_MAX) + break; + ctx->print(ctx, "%.*s", (int) llen, line); + line = buf + off + 1; + } } - ctx->print(ctx, "%s", buf); - has_output = 1; + llen = buf + len - line; + if (llen > 0 && llen < INT_MAX) + ctx->print(ctx, "%.*s", (int) llen, line); } - if (lbuf != NULL) - xfree(lbuf); msg = NULL; - status = pclose(fp); - - if (WIFEXITED(status)) { - if ((ret = WEXITSTATUS(status)) == 0) - return (0); - xasprintf(&msg, "'%s' returned %d", data->arg, ret); - } else if (WIFSIGNALED(status)) { - xasprintf( - &msg, "'%s' terminated by signal %d", data->arg, - WTERMSIG(status)); + if (WIFEXITED(job->status)) { + if ((retcode = WEXITSTATUS(job->status)) != 0) + xasprintf(&msg, "'%s' returned %d", cmd, retcode); + } else if (WIFSIGNALED(job->status)) { + retcode = WTERMSIG(job->status); + xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode); } - if (msg != NULL) { - if (has_output) + if (len != 0) ctx->print(ctx, "%s", msg); else ctx->info(ctx, "%s", msg); xfree(msg); } - return (0); + job_free(job); /* calls cmd_run_shell_free */ +} + +void +cmd_run_shell_free(void *data) +{ + struct cmd_run_shell_data *cdata = data; + struct cmd_ctx *ctx = &cdata->ctx; + + return; + if (ctx->cmdclient != NULL) { + ctx->cmdclient->references--; + server_write_client(ctx->cmdclient, MSG_EXIT, NULL, 0); + } + if (ctx->curclient != NULL) + ctx->curclient->references--; + + xfree(cdata->cmd); + xfree(cdata); } diff --git a/job.c b/job.c index 15155bb3..6a824fa6 100644 --- a/job.c +++ b/job.c @@ -70,7 +70,6 @@ job_tree_free(struct jobs *jobs) while (!RB_EMPTY(jobs)) { job = RB_ROOT(jobs); RB_REMOVE(jobs, jobs, job); - SLIST_REMOVE(&all_jobs, job, job, lentry); job_free(job); } } @@ -120,6 +119,7 @@ job_free(struct job *job) { job_kill(job); + SLIST_REMOVE(&all_jobs, job, job, lentry); xfree(job->cmd); if (job->freefn != NULL && job->data != NULL) diff --git a/server.c b/server.c index d19b321d..bf9aba94 100644 --- a/server.c +++ b/server.c @@ -810,13 +810,17 @@ void server_check_jobs(void) { struct job *job; - + +restart: SLIST_FOREACH(job, &all_jobs, lentry) { if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1) continue; - if (job->callbackfn != NULL) - job->callbackfn(job); job->flags |= JOB_DONE; + + if (job->callbackfn != NULL) { + job->callbackfn(job); + goto restart; /* could be freed by callback */ + } } } diff --git a/tmux.1 b/tmux.1 index 6921976d..f4616ed4 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2068,8 +2068,8 @@ option. .D1 (alias: Ic run ) Execute .Ar command -without creating a window. -Any output to stdout is displayed in output mode. +in the background without creating a window. +After the command finishes, any output to stdout is displayed in output mode. If .Ar command doesn't return success, the exit status is also displayed.