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