version 1.55, 2019/06/28 13:35:05 |
version 1.56, 2020/03/19 13:43:18 |
|
|
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
|
#include <util.h> |
|
|
#include "tmux.h" |
#include "tmux.h" |
|
|
|
|
struct job * |
struct job * |
job_run(const char *cmd, struct session *s, const char *cwd, |
job_run(const char *cmd, struct session *s, const char *cwd, |
job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, |
job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, |
void *data, int flags) |
void *data, int flags, int sx, int sy) |
{ |
{ |
struct job *job; |
struct job *job; |
struct environ *env; |
struct environ *env; |
pid_t pid; |
pid_t pid; |
int nullfd, out[2]; |
int nullfd, out[2], master; |
const char *home; |
const char *home; |
sigset_t set, oldset; |
sigset_t set, oldset; |
|
struct winsize ws; |
|
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) |
|
return (NULL); |
|
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); |
|
|
|
/* |
/* |
* Do not set TERM during .tmux.conf, it is nice to be able to use |
* Do not set TERM during .tmux.conf, it is nice to be able to use |
* if-shell to decide on default-terminal based on outside TERM. |
* if-shell to decide on default-terminal based on outside TERM. |
|
|
|
|
sigfillset(&set); |
sigfillset(&set); |
sigprocmask(SIG_BLOCK, &set, &oldset); |
sigprocmask(SIG_BLOCK, &set, &oldset); |
switch (pid = fork()) { |
|
|
if (flags & JOB_PTY) { |
|
memset(&ws, 0, sizeof ws); |
|
ws.ws_col = sx; |
|
ws.ws_row = sy; |
|
pid = fdforkpty(ptm_fd, &master, NULL, NULL, &ws); |
|
} else { |
|
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) |
|
goto fail; |
|
pid = fork(); |
|
} |
|
log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); |
|
|
|
switch (pid) { |
case -1: |
case -1: |
sigprocmask(SIG_SETMASK, &oldset, NULL); |
if (~flags & JOB_PTY) { |
environ_free(env); |
close(out[0]); |
close(out[0]); |
close(out[1]); |
close(out[1]); |
} |
return (NULL); |
goto fail; |
case 0: |
case 0: |
proc_clear_signals(server_proc, 1); |
proc_clear_signals(server_proc, 1); |
sigprocmask(SIG_SETMASK, &oldset, NULL); |
sigprocmask(SIG_SETMASK, &oldset, NULL); |
|
|
environ_push(env); |
environ_push(env); |
environ_free(env); |
environ_free(env); |
|
|
if (dup2(out[1], STDIN_FILENO) == -1) |
if (~flags & JOB_PTY) { |
fatal("dup2 failed"); |
if (dup2(out[1], STDIN_FILENO) == -1) |
if (dup2(out[1], STDOUT_FILENO) == -1) |
fatal("dup2 failed"); |
fatal("dup2 failed"); |
if (dup2(out[1], STDOUT_FILENO) == -1) |
if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) |
fatal("dup2 failed"); |
close(out[1]); |
if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) |
close(out[0]); |
close(out[1]); |
|
close(out[0]); |
|
|
nullfd = open(_PATH_DEVNULL, O_RDWR, 0); |
nullfd = open(_PATH_DEVNULL, O_RDWR, 0); |
if (nullfd == -1) |
if (nullfd == -1) |
fatal("open failed"); |
fatal("open failed"); |
if (dup2(nullfd, STDERR_FILENO) == -1) |
if (dup2(nullfd, STDERR_FILENO) == -1) |
fatal("dup2 failed"); |
fatal("dup2 failed"); |
if (nullfd != STDERR_FILENO) |
if (nullfd != STDERR_FILENO) |
close(nullfd); |
close(nullfd); |
|
} |
closefrom(STDERR_FILENO + 1); |
closefrom(STDERR_FILENO + 1); |
|
|
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); |
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); |
|
|
|
|
sigprocmask(SIG_SETMASK, &oldset, NULL); |
sigprocmask(SIG_SETMASK, &oldset, NULL); |
environ_free(env); |
environ_free(env); |
close(out[1]); |
|
|
|
job = xmalloc(sizeof *job); |
job = xmalloc(sizeof *job); |
job->state = JOB_RUNNING; |
job->state = JOB_RUNNING; |
|
|
job->freecb = freecb; |
job->freecb = freecb; |
job->data = data; |
job->data = data; |
|
|
job->fd = out[0]; |
if (~flags & JOB_PTY) { |
|
close(out[1]); |
|
job->fd = out[0]; |
|
} else |
|
job->fd = master; |
setblocking(job->fd, 0); |
setblocking(job->fd, 0); |
|
|
job->event = bufferevent_new(job->fd, job_read_callback, |
job->event = bufferevent_new(job->fd, job_read_callback, |
|
|
|
|
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); |
log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); |
return (job); |
return (job); |
|
|
|
fail: |
|
sigprocmask(SIG_SETMASK, &oldset, NULL); |
|
environ_free(env); |
|
return (NULL); |
} |
} |
|
|
/* Kill and free an individual job. */ |
/* Kill and free an individual job. */ |
|
|
log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, |
log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, |
(long) job->pid, len); |
(long) job->pid, len); |
|
|
if (len == 0) { |
if (len == 0 && (~job->flags & JOB_KEEPWRITE)) { |
shutdown(job->fd, SHUT_WR); |
shutdown(job->fd, SHUT_WR); |
bufferevent_disable(job->event, EV_WRITE); |
bufferevent_disable(job->event, EV_WRITE); |
} |
} |