Merge branch 'obsd-master'
[tmux.git] / job.c
blobcab91d2caef33c7876a0f2358d510f1614b74f5a
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
20 #include <sys/ioctl.h>
21 #include <sys/socket.h>
22 #include <sys/wait.h>
24 #include <fcntl.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
30 #include "tmux.h"
33 * Job scheduling. Run queued commands in the background and record their
34 * output.
37 static void job_read_callback(struct bufferevent *, void *);
38 static void job_write_callback(struct bufferevent *, void *);
39 static void job_error_callback(struct bufferevent *, short, void *);
41 /* A single job. */
42 struct job {
43 enum {
44 JOB_RUNNING,
45 JOB_DEAD,
46 JOB_CLOSED
47 } state;
49 int flags;
51 char *cmd;
52 pid_t pid;
53 char tty[TTY_NAME_MAX];
54 int status;
56 int fd;
57 struct bufferevent *event;
59 job_update_cb updatecb;
60 job_complete_cb completecb;
61 job_free_cb freecb;
62 void *data;
64 LIST_ENTRY(job) entry;
67 /* All jobs list. */
68 static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
70 /* Start a job running. */
71 struct job *
72 job_run(const char *cmd, int argc, char **argv, struct environ *e, struct session *s,
73 const char *cwd, job_update_cb updatecb, job_complete_cb completecb,
74 job_free_cb freecb, void *data, int flags, int sx, int sy)
76 struct job *job;
77 struct environ *env;
78 pid_t pid;
79 int nullfd, out[2], master;
80 const char *home, *shell;
81 sigset_t set, oldset;
82 struct winsize ws;
83 char **argvp, tty[TTY_NAME_MAX], *argv0;
86 * Do not set TERM during .tmux.conf (second argument here), it is nice
87 * to be able to use if-shell to decide on default-terminal based on
88 * outside TERM.
90 env = environ_for_session(s, !cfg_finished);
91 if (e != NULL)
92 environ_copy(e, env);
94 if (s != NULL)
95 shell = options_get_string(s->options, "default-shell");
96 else
97 shell = options_get_string(global_s_options, "default-shell");
98 if (!checkshell(shell))
99 shell = _PATH_BSHELL;
100 argv0 = shell_argv0(shell, 0);
102 sigfillset(&set);
103 sigprocmask(SIG_BLOCK, &set, &oldset);
105 if (flags & JOB_PTY) {
106 memset(&ws, 0, sizeof ws);
107 ws.ws_col = sx;
108 ws.ws_row = sy;
109 pid = fdforkpty(ptm_fd, &master, tty, NULL, &ws);
110 } else {
111 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
112 goto fail;
113 pid = fork();
115 if (cmd == NULL) {
116 cmd_log_argv(argc, argv, "%s:", __func__);
117 log_debug("%s: cwd=%s, shell=%s", __func__,
118 cwd == NULL ? "" : cwd, shell);
119 } else {
120 log_debug("%s: cmd=%s, cwd=%s, shell=%s", __func__, cmd,
121 cwd == NULL ? "" : cwd, shell);
124 switch (pid) {
125 case -1:
126 if (~flags & JOB_PTY) {
127 close(out[0]);
128 close(out[1]);
130 goto fail;
131 case 0:
132 proc_clear_signals(server_proc, 1);
133 sigprocmask(SIG_SETMASK, &oldset, NULL);
135 if ((cwd == NULL || chdir(cwd) != 0) &&
136 ((home = find_home()) == NULL || chdir(home) != 0) &&
137 chdir("/") != 0)
138 fatal("chdir failed");
140 environ_push(env);
141 environ_free(env);
143 if (~flags & JOB_PTY) {
144 if (dup2(out[1], STDIN_FILENO) == -1)
145 fatal("dup2 failed");
146 if (dup2(out[1], STDOUT_FILENO) == -1)
147 fatal("dup2 failed");
148 if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO)
149 close(out[1]);
150 close(out[0]);
152 nullfd = open(_PATH_DEVNULL, O_RDWR);
153 if (nullfd == -1)
154 fatal("open failed");
155 if (dup2(nullfd, STDERR_FILENO) == -1)
156 fatal("dup2 failed");
157 if (nullfd != STDERR_FILENO)
158 close(nullfd);
160 closefrom(STDERR_FILENO + 1);
162 if (cmd != NULL) {
163 setenv("SHELL", shell, 1);
164 execl(shell, argv0, "-c", cmd, (char *)NULL);
165 fatal("execl failed");
166 } else {
167 argvp = cmd_copy_argv(argc, argv);
168 execvp(argvp[0], argvp);
169 fatal("execvp failed");
173 sigprocmask(SIG_SETMASK, &oldset, NULL);
174 environ_free(env);
175 free(argv0);
177 job = xmalloc(sizeof *job);
178 job->state = JOB_RUNNING;
179 job->flags = flags;
181 if (cmd != NULL)
182 job->cmd = xstrdup(cmd);
183 else
184 job->cmd = cmd_stringify_argv(argc, argv);
185 job->pid = pid;
186 strlcpy(job->tty, tty, sizeof job->tty);
187 job->status = 0;
189 LIST_INSERT_HEAD(&all_jobs, job, entry);
191 job->updatecb = updatecb;
192 job->completecb = completecb;
193 job->freecb = freecb;
194 job->data = data;
196 if (~flags & JOB_PTY) {
197 close(out[1]);
198 job->fd = out[0];
199 } else
200 job->fd = master;
201 setblocking(job->fd, 0);
203 job->event = bufferevent_new(job->fd, job_read_callback,
204 job_write_callback, job_error_callback, job);
205 if (job->event == NULL)
206 fatalx("out of memory");
207 bufferevent_enable(job->event, EV_READ|EV_WRITE);
209 log_debug("run job %p: %s, pid %ld", job, job->cmd, (long)job->pid);
210 return (job);
212 fail:
213 sigprocmask(SIG_SETMASK, &oldset, NULL);
214 environ_free(env);
215 free(argv0);
216 return (NULL);
219 /* Take job's file descriptor and free the job. */
221 job_transfer(struct job *job, pid_t *pid, char *tty, size_t ttylen)
223 int fd = job->fd;
225 log_debug("transfer job %p: %s", job, job->cmd);
227 if (pid != NULL)
228 *pid = job->pid;
229 if (tty != NULL)
230 strlcpy(tty, job->tty, ttylen);
232 LIST_REMOVE(job, entry);
233 free(job->cmd);
235 if (job->freecb != NULL && job->data != NULL)
236 job->freecb(job->data);
238 if (job->event != NULL)
239 bufferevent_free(job->event);
241 free(job);
242 return (fd);
245 /* Kill and free an individual job. */
246 void
247 job_free(struct job *job)
249 log_debug("free job %p: %s", job, job->cmd);
251 LIST_REMOVE(job, entry);
252 free(job->cmd);
254 if (job->freecb != NULL && job->data != NULL)
255 job->freecb(job->data);
257 if (job->pid != -1)
258 kill(job->pid, SIGTERM);
259 if (job->event != NULL)
260 bufferevent_free(job->event);
261 if (job->fd != -1)
262 close(job->fd);
264 free(job);
267 /* Resize job. */
268 void
269 job_resize(struct job *job, u_int sx, u_int sy)
271 struct winsize ws;
273 if (job->fd == -1 || (~job->flags & JOB_PTY))
274 return;
276 log_debug("resize job %p: %ux%u", job, sx, sy);
278 memset(&ws, 0, sizeof ws);
279 ws.ws_col = sx;
280 ws.ws_row = sy;
281 if (ioctl(job->fd, TIOCSWINSZ, &ws) == -1)
282 fatal("ioctl failed");
285 /* Job buffer read callback. */
286 static void
287 job_read_callback(__unused struct bufferevent *bufev, void *data)
289 struct job *job = data;
291 if (job->updatecb != NULL)
292 job->updatecb(job);
296 * Job buffer write callback. Fired when the buffer falls below watermark
297 * (default is empty). If all the data has been written, disable the write
298 * event.
300 static void
301 job_write_callback(__unused struct bufferevent *bufev, void *data)
303 struct job *job = data;
304 size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event));
306 log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd,
307 (long) job->pid, len);
309 if (len == 0 && (~job->flags & JOB_KEEPWRITE)) {
310 shutdown(job->fd, SHUT_WR);
311 bufferevent_disable(job->event, EV_WRITE);
315 /* Job buffer error callback. */
316 static void
317 job_error_callback(__unused struct bufferevent *bufev, __unused short events,
318 void *data)
320 struct job *job = data;
322 log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid);
324 if (job->state == JOB_DEAD) {
325 if (job->completecb != NULL)
326 job->completecb(job);
327 job_free(job);
328 } else {
329 bufferevent_disable(job->event, EV_READ);
330 job->state = JOB_CLOSED;
334 /* Job died (waitpid() returned its pid). */
335 void
336 job_check_died(pid_t pid, int status)
338 struct job *job;
340 LIST_FOREACH(job, &all_jobs, entry) {
341 if (pid == job->pid)
342 break;
344 if (job == NULL)
345 return;
346 if (WIFSTOPPED(status)) {
347 if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU)
348 return;
349 killpg(job->pid, SIGCONT);
350 return;
352 log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid);
354 job->status = status;
356 if (job->state == JOB_CLOSED) {
357 if (job->completecb != NULL)
358 job->completecb(job);
359 job_free(job);
360 } else {
361 job->pid = -1;
362 job->state = JOB_DEAD;
366 /* Get job status. */
368 job_get_status(struct job *job)
370 return (job->status);
373 /* Get job data. */
374 void *
375 job_get_data(struct job *job)
377 return (job->data);
380 /* Get job event. */
381 struct bufferevent *
382 job_get_event(struct job *job)
384 return (job->event);
387 /* Kill all jobs. */
388 void
389 job_kill_all(void)
391 struct job *job;
393 LIST_FOREACH(job, &all_jobs, entry) {
394 if (job->pid != -1)
395 kill(job->pid, SIGTERM);
399 /* Are any jobs still running? */
401 job_still_running(void)
403 struct job *job;
405 LIST_FOREACH(job, &all_jobs, entry) {
406 if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING)
407 return (1);
409 return (0);
412 /* Print job summary. */
413 void
414 job_print_summary(struct cmdq_item *item, int blank)
416 struct job *job;
417 u_int n = 0;
419 LIST_FOREACH(job, &all_jobs, entry) {
420 if (blank) {
421 cmdq_print(item, "%s", "");
422 blank = 0;
424 cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]",
425 n, job->cmd, job->fd, (long)job->pid, job->status);
426 n++;