1 /* $OpenBSD: job.c,v 1.69 2024/09/30 07:54:51 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 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. 9 * 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. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/ioctl.h> 21 #include <sys/socket.h> 22 #include <sys/wait.h> 23 24 #include <fcntl.h> 25 #include <paths.h> 26 #include <signal.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <util.h> 31 32 #include "tmux.h" 33 34 /* 35 * Job scheduling. Run queued commands in the background and record their 36 * output. 37 */ 38 39 static void job_read_callback(struct bufferevent *, void *); 40 static void job_write_callback(struct bufferevent *, void *); 41 static void job_error_callback(struct bufferevent *, short, void *); 42 43 /* A single job. */ 44 struct job { 45 enum { 46 JOB_RUNNING, 47 JOB_DEAD, 48 JOB_CLOSED 49 } state; 50 51 int flags; 52 53 char *cmd; 54 pid_t pid; 55 char tty[TTY_NAME_MAX]; 56 int status; 57 58 int fd; 59 struct bufferevent *event; 60 61 job_update_cb updatecb; 62 job_complete_cb completecb; 63 job_free_cb freecb; 64 void *data; 65 66 LIST_ENTRY(job) entry; 67 }; 68 69 /* All jobs list. */ 70 static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); 71 72 /* Start a job running. */ 73 struct job * 74 job_run(const char *cmd, int argc, char **argv, struct environ *e, 75 struct session *s, const char *cwd, job_update_cb updatecb, 76 job_complete_cb completecb, job_free_cb freecb, void *data, int flags, 77 int sx, int sy) 78 { 79 struct job *job; 80 struct environ *env; 81 pid_t pid; 82 int nullfd, out[2], master; 83 const char *home, *shell; 84 sigset_t set, oldset; 85 struct winsize ws; 86 char **argvp, tty[TTY_NAME_MAX], *argv0; 87 struct options *oo; 88 89 /* 90 * Do not set TERM during .tmux.conf (second argument here), it is nice 91 * to be able to use if-shell to decide on default-terminal based on 92 * outside TERM. 93 */ 94 env = environ_for_session(s, !cfg_finished); 95 if (e != NULL) 96 environ_copy(e, env); 97 98 if (~flags & JOB_DEFAULTSHELL) 99 shell = _PATH_BSHELL; 100 else { 101 if (s != NULL) 102 oo = s->options; 103 else 104 oo = global_s_options; 105 shell = options_get_string(oo, "default-shell"); 106 if (!checkshell(shell)) 107 shell = _PATH_BSHELL; 108 } 109 argv0 = shell_argv0(shell, 0); 110 111 sigfillset(&set); 112 sigprocmask(SIG_BLOCK, &set, &oldset); 113 114 if (flags & JOB_PTY) { 115 memset(&ws, 0, sizeof ws); 116 ws.ws_col = sx; 117 ws.ws_row = sy; 118 pid = fdforkpty(ptm_fd, &master, tty, NULL, &ws); 119 } else { 120 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) 121 goto fail; 122 pid = fork(); 123 } 124 if (cmd == NULL) { 125 cmd_log_argv(argc, argv, "%s:", __func__); 126 log_debug("%s: cwd=%s, shell=%s", __func__, 127 cwd == NULL ? "" : cwd, shell); 128 } else { 129 log_debug("%s: cmd=%s, cwd=%s, shell=%s", __func__, cmd, 130 cwd == NULL ? "" : cwd, shell); 131 } 132 133 switch (pid) { 134 case -1: 135 if (~flags & JOB_PTY) { 136 close(out[0]); 137 close(out[1]); 138 } 139 goto fail; 140 case 0: 141 proc_clear_signals(server_proc, 1); 142 sigprocmask(SIG_SETMASK, &oldset, NULL); 143 144 if ((cwd == NULL || chdir(cwd) != 0) && 145 ((home = find_home()) == NULL || chdir(home) != 0) && 146 chdir("/") != 0) 147 fatal("chdir failed"); 148 149 environ_push(env); 150 environ_free(env); 151 152 if (~flags & JOB_PTY) { 153 if (dup2(out[1], STDIN_FILENO) == -1) 154 fatal("dup2 failed"); 155 if (dup2(out[1], STDOUT_FILENO) == -1) 156 fatal("dup2 failed"); 157 if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) 158 close(out[1]); 159 close(out[0]); 160 161 nullfd = open(_PATH_DEVNULL, O_RDWR); 162 if (nullfd == -1) 163 fatal("open failed"); 164 if (dup2(nullfd, STDERR_FILENO) == -1) 165 fatal("dup2 failed"); 166 if (nullfd != STDERR_FILENO) 167 close(nullfd); 168 } 169 closefrom(STDERR_FILENO + 1); 170 171 if (cmd != NULL) { 172 setenv("SHELL", shell, 1); 173 execl(shell, argv0, "-c", cmd, (char *)NULL); 174 fatal("execl failed"); 175 } else { 176 argvp = cmd_copy_argv(argc, argv); 177 execvp(argvp[0], argvp); 178 fatal("execvp failed"); 179 } 180 } 181 182 sigprocmask(SIG_SETMASK, &oldset, NULL); 183 environ_free(env); 184 free(argv0); 185 186 job = xmalloc(sizeof *job); 187 job->state = JOB_RUNNING; 188 job->flags = flags; 189 190 if (cmd != NULL) 191 job->cmd = xstrdup(cmd); 192 else 193 job->cmd = cmd_stringify_argv(argc, argv); 194 job->pid = pid; 195 strlcpy(job->tty, tty, sizeof job->tty); 196 job->status = 0; 197 198 LIST_INSERT_HEAD(&all_jobs, job, entry); 199 200 job->updatecb = updatecb; 201 job->completecb = completecb; 202 job->freecb = freecb; 203 job->data = data; 204 205 if (~flags & JOB_PTY) { 206 close(out[1]); 207 job->fd = out[0]; 208 } else 209 job->fd = master; 210 setblocking(job->fd, 0); 211 212 job->event = bufferevent_new(job->fd, job_read_callback, 213 job_write_callback, job_error_callback, job); 214 if (job->event == NULL) 215 fatalx("out of memory"); 216 bufferevent_enable(job->event, EV_READ|EV_WRITE); 217 218 log_debug("run job %p: %s, pid %ld", job, job->cmd, (long)job->pid); 219 return (job); 220 221 fail: 222 sigprocmask(SIG_SETMASK, &oldset, NULL); 223 environ_free(env); 224 free(argv0); 225 return (NULL); 226 } 227 228 /* Take job's file descriptor and free the job. */ 229 int 230 job_transfer(struct job *job, pid_t *pid, char *tty, size_t ttylen) 231 { 232 int fd = job->fd; 233 234 log_debug("transfer job %p: %s", job, job->cmd); 235 236 if (pid != NULL) 237 *pid = job->pid; 238 if (tty != NULL) 239 strlcpy(tty, job->tty, ttylen); 240 241 LIST_REMOVE(job, entry); 242 free(job->cmd); 243 244 if (job->freecb != NULL && job->data != NULL) 245 job->freecb(job->data); 246 247 if (job->event != NULL) 248 bufferevent_free(job->event); 249 250 free(job); 251 return (fd); 252 } 253 254 /* Kill and free an individual job. */ 255 void 256 job_free(struct job *job) 257 { 258 log_debug("free job %p: %s", job, job->cmd); 259 260 LIST_REMOVE(job, entry); 261 free(job->cmd); 262 263 if (job->freecb != NULL && job->data != NULL) 264 job->freecb(job->data); 265 266 if (job->pid != -1) 267 kill(job->pid, SIGTERM); 268 if (job->event != NULL) 269 bufferevent_free(job->event); 270 if (job->fd != -1) 271 close(job->fd); 272 273 free(job); 274 } 275 276 /* Resize job. */ 277 void 278 job_resize(struct job *job, u_int sx, u_int sy) 279 { 280 struct winsize ws; 281 282 if (job->fd == -1 || (~job->flags & JOB_PTY)) 283 return; 284 285 log_debug("resize job %p: %ux%u", job, sx, sy); 286 287 memset(&ws, 0, sizeof ws); 288 ws.ws_col = sx; 289 ws.ws_row = sy; 290 if (ioctl(job->fd, TIOCSWINSZ, &ws) == -1) 291 fatal("ioctl failed"); 292 } 293 294 /* Job buffer read callback. */ 295 static void 296 job_read_callback(__unused struct bufferevent *bufev, void *data) 297 { 298 struct job *job = data; 299 300 if (job->updatecb != NULL) 301 job->updatecb(job); 302 } 303 304 /* 305 * Job buffer write callback. Fired when the buffer falls below watermark 306 * (default is empty). If all the data has been written, disable the write 307 * event. 308 */ 309 static void 310 job_write_callback(__unused struct bufferevent *bufev, void *data) 311 { 312 struct job *job = data; 313 size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); 314 315 log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, 316 (long) job->pid, len); 317 318 if (len == 0 && (~job->flags & JOB_KEEPWRITE)) { 319 shutdown(job->fd, SHUT_WR); 320 bufferevent_disable(job->event, EV_WRITE); 321 } 322 } 323 324 /* Job buffer error callback. */ 325 static void 326 job_error_callback(__unused struct bufferevent *bufev, __unused short events, 327 void *data) 328 { 329 struct job *job = data; 330 331 log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); 332 333 if (job->state == JOB_DEAD) { 334 if (job->completecb != NULL) 335 job->completecb(job); 336 job_free(job); 337 } else { 338 bufferevent_disable(job->event, EV_READ); 339 job->state = JOB_CLOSED; 340 } 341 } 342 343 /* Job died (waitpid() returned its pid). */ 344 void 345 job_check_died(pid_t pid, int status) 346 { 347 struct job *job; 348 349 LIST_FOREACH(job, &all_jobs, entry) { 350 if (pid == job->pid) 351 break; 352 } 353 if (job == NULL) 354 return; 355 if (WIFSTOPPED(status)) { 356 if (WSTOPSIG(status) == SIGTTIN || WSTOPSIG(status) == SIGTTOU) 357 return; 358 killpg(job->pid, SIGCONT); 359 return; 360 } 361 log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); 362 363 job->status = status; 364 365 if (job->state == JOB_CLOSED) { 366 if (job->completecb != NULL) 367 job->completecb(job); 368 job_free(job); 369 } else { 370 job->pid = -1; 371 job->state = JOB_DEAD; 372 } 373 } 374 375 /* Get job status. */ 376 int 377 job_get_status(struct job *job) 378 { 379 return (job->status); 380 } 381 382 /* Get job data. */ 383 void * 384 job_get_data(struct job *job) 385 { 386 return (job->data); 387 } 388 389 /* Get job event. */ 390 struct bufferevent * 391 job_get_event(struct job *job) 392 { 393 return (job->event); 394 } 395 396 /* Kill all jobs. */ 397 void 398 job_kill_all(void) 399 { 400 struct job *job; 401 402 LIST_FOREACH(job, &all_jobs, entry) { 403 if (job->pid != -1) 404 kill(job->pid, SIGTERM); 405 } 406 } 407 408 /* Are any jobs still running? */ 409 int 410 job_still_running(void) 411 { 412 struct job *job; 413 414 LIST_FOREACH(job, &all_jobs, entry) { 415 if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING) 416 return (1); 417 } 418 return (0); 419 } 420 421 /* Print job summary. */ 422 void 423 job_print_summary(struct cmdq_item *item, int blank) 424 { 425 struct job *job; 426 u_int n = 0; 427 428 LIST_FOREACH(job, &all_jobs, entry) { 429 if (blank) { 430 cmdq_print(item, "%s", ""); 431 blank = 0; 432 } 433 cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]", 434 n, job->cmd, job->fd, (long)job->pid, job->status); 435 n++; 436 } 437 } 438