Switch run-shell over to queue the command in the background like #().

This commit is contained in:
Nicholas Marriott 2009-10-11 08:58:05 +00:00
parent abedfa77da
commit cebc988dd4
4 changed files with 89 additions and 42 deletions

View File

@ -29,6 +29,9 @@
int cmd_run_shell_exec(struct cmd *, struct cmd_ctx *); 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 = { const struct cmd_entry cmd_run_shell_entry = {
"run-shell", "run", "run-shell", "run",
"command", "command",
@ -40,57 +43,97 @@ const struct cmd_entry cmd_run_shell_entry = {
cmd_target_print cmd_target_print
}; };
struct cmd_run_shell_data {
char *cmd;
struct cmd_ctx ctx;
};
int int
cmd_run_shell_exec(struct cmd *self, struct cmd_ctx *ctx) cmd_run_shell_exec(struct cmd *self, struct cmd_ctx *ctx)
{ {
struct cmd_target_data *data = self->data; struct cmd_target_data *data = self->data;
FILE *fp; struct cmd_run_shell_data *cdata;
char *buf, *lbuf, *msg; struct job *job;
size_t len;
int has_output, ret, status;
if ((fp = popen(data->arg, "r")) == NULL) { cdata = xmalloc(sizeof *cdata);
ctx->error(ctx, "popen error"); cdata->cmd = xstrdup(data->arg);
return (-1); memcpy(&cdata->ctx, ctx, sizeof cdata->ctx);
}
has_output = 0; if (ctx->cmdclient != NULL)
lbuf = NULL; ctx->cmdclient->references++;
while ((buf = fgetln(fp, &len)) != NULL) { if (ctx->curclient != NULL)
if (buf[len - 1] == '\n') ctx->curclient->references++;
buf[len - 1] = '\0';
else { job = job_add(NULL, NULL,
lbuf = xmalloc(len + 1); data->arg, cmd_run_shell_callback, cmd_run_shell_free, cdata);
memcpy(lbuf, buf, len); job_run(job);
lbuf[len] = '\0';
buf = lbuf; 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); llen = buf + len - line;
has_output = 1; if (llen > 0 && llen < INT_MAX)
ctx->print(ctx, "%.*s", (int) llen, line);
} }
if (lbuf != NULL)
xfree(lbuf);
msg = NULL; msg = NULL;
status = pclose(fp); if (WIFEXITED(job->status)) {
if ((retcode = WEXITSTATUS(job->status)) != 0)
if (WIFEXITED(status)) { xasprintf(&msg, "'%s' returned %d", cmd, retcode);
if ((ret = WEXITSTATUS(status)) == 0) } else if (WIFSIGNALED(job->status)) {
return (0); retcode = WTERMSIG(job->status);
xasprintf(&msg, "'%s' returned %d", data->arg, ret); xasprintf(&msg, "'%s' terminated by signal %d", cmd, retcode);
} else if (WIFSIGNALED(status)) {
xasprintf(
&msg, "'%s' terminated by signal %d", data->arg,
WTERMSIG(status));
} }
if (msg != NULL) { if (msg != NULL) {
if (has_output) if (len != 0)
ctx->print(ctx, "%s", msg); ctx->print(ctx, "%s", msg);
else else
ctx->info(ctx, "%s", msg); ctx->info(ctx, "%s", msg);
xfree(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);
} }

2
job.c
View File

@ -70,7 +70,6 @@ job_tree_free(struct jobs *jobs)
while (!RB_EMPTY(jobs)) { while (!RB_EMPTY(jobs)) {
job = RB_ROOT(jobs); job = RB_ROOT(jobs);
RB_REMOVE(jobs, jobs, job); RB_REMOVE(jobs, jobs, job);
SLIST_REMOVE(&all_jobs, job, job, lentry);
job_free(job); job_free(job);
} }
} }
@ -120,6 +119,7 @@ job_free(struct job *job)
{ {
job_kill(job); job_kill(job);
SLIST_REMOVE(&all_jobs, job, job, lentry);
xfree(job->cmd); xfree(job->cmd);
if (job->freefn != NULL && job->data != NULL) if (job->freefn != NULL && job->data != NULL)

View File

@ -811,12 +811,16 @@ server_check_jobs(void)
{ {
struct job *job; struct job *job;
restart:
SLIST_FOREACH(job, &all_jobs, lentry) { SLIST_FOREACH(job, &all_jobs, lentry) {
if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1) if (job->flags & JOB_DONE || job->fd != -1 || job->pid != -1)
continue; continue;
if (job->callbackfn != NULL)
job->callbackfn(job);
job->flags |= JOB_DONE; job->flags |= JOB_DONE;
if (job->callbackfn != NULL) {
job->callbackfn(job);
goto restart; /* could be freed by callback */
}
} }
} }

4
tmux.1
View File

@ -2068,8 +2068,8 @@ option.
.D1 (alias: Ic run ) .D1 (alias: Ic run )
Execute Execute
.Ar command .Ar command
without creating a window. in the background without creating a window.
Any output to stdout is displayed in output mode. After the command finishes, any output to stdout is displayed in output mode.
If If
.Ar command .Ar command
doesn't return success, the exit status is also displayed. doesn't return success, the exit status is also displayed.