diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 3936fff8..4cb64b04 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -1,4 +1,4 @@ -/* $Id: cmd-run-shell.c,v 1.1 2009-09-20 22:20:10 tcunha Exp $ */ +/* $Id: cmd-run-shell.c,v 1.2 2009-10-12 00:25:25 tcunha Exp $ */ /* * Copyright (c) 2009 Tiago Cunha @@ -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 14754693..5ec1ccac 100644 --- a/job.c +++ b/job.c @@ -1,4 +1,4 @@ -/* $Id: job.c,v 1.5 2009-10-12 00:22:17 tcunha Exp $ */ +/* $Id: job.c,v 1.6 2009-10-12 00:25:25 tcunha Exp $ */ /* * Copyright (c) 2009 Nicholas Marriott @@ -69,7 +69,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); } } @@ -119,6 +118,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 4158044e..27ad3d34 100644 --- a/server.c +++ b/server.c @@ -1,4 +1,4 @@ -/* $Id: server.c,v 1.204 2009-10-12 00:21:08 tcunha Exp $ */ +/* $Id: server.c,v 1.205 2009-10-12 00:25:25 tcunha Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -813,13 +813,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 99bcac5e..60450e9a 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1,4 +1,4 @@ -.\" $Id: tmux.1,v 1.187 2009-10-12 00:09:48 tcunha Exp $ +.\" $Id: tmux.1,v 1.188 2009-10-12 00:25:25 tcunha Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: October 10 2009 $ +.Dd $Mdocdate: October 11 2009 $ .Dt TMUX 1 .Os .Sh NAME @@ -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.