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