1 /* $OpenBSD: job.c,v 1.55 2019/06/28 13:35:05 deraadt 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/socket.h> 21 22 #include <fcntl.h> 23 #include <paths.h> 24 #include <signal.h> 25 #include <stdlib.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "tmux.h" 30 31 /* 32 * Job scheduling. Run queued commands in the background and record their 33 * output. 34 */ 35 36 static void job_read_callback(struct bufferevent *, void *); 37 static void job_write_callback(struct bufferevent *, void *); 38 static void job_error_callback(struct bufferevent *, short, void *); 39 40 /* A single job. */ 41 struct job { 42 enum { 43 JOB_RUNNING, 44 JOB_DEAD, 45 JOB_CLOSED 46 } state; 47 48 int flags; 49 50 char *cmd; 51 pid_t pid; 52 int status; 53 54 int fd; 55 struct bufferevent *event; 56 57 job_update_cb updatecb; 58 job_complete_cb completecb; 59 job_free_cb freecb; 60 void *data; 61 62 LIST_ENTRY(job) entry; 63 }; 64 65 /* All jobs list. */ 66 static LIST_HEAD(joblist, job) all_jobs = LIST_HEAD_INITIALIZER(all_jobs); 67 68 /* Start a job running, if it isn't already. */ 69 struct job * 70 job_run(const char *cmd, struct session *s, const char *cwd, 71 job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, 72 void *data, int flags) 73 { 74 struct job *job; 75 struct environ *env; 76 pid_t pid; 77 int nullfd, out[2]; 78 const char *home; 79 sigset_t set, oldset; 80 81 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) 82 return (NULL); 83 log_debug("%s: cmd=%s, cwd=%s", __func__, cmd, cwd == NULL ? "" : cwd); 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 switch (pid = fork()) { 94 case -1: 95 sigprocmask(SIG_SETMASK, &oldset, NULL); 96 environ_free(env); 97 close(out[0]); 98 close(out[1]); 99 return (NULL); 100 case 0: 101 proc_clear_signals(server_proc, 1); 102 sigprocmask(SIG_SETMASK, &oldset, NULL); 103 104 if (cwd == NULL || chdir(cwd) != 0) { 105 if ((home = find_home()) == NULL || chdir(home) != 0) 106 chdir("/"); 107 } 108 109 environ_push(env); 110 environ_free(env); 111 112 if (dup2(out[1], STDIN_FILENO) == -1) 113 fatal("dup2 failed"); 114 if (dup2(out[1], STDOUT_FILENO) == -1) 115 fatal("dup2 failed"); 116 if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) 117 close(out[1]); 118 close(out[0]); 119 120 nullfd = open(_PATH_DEVNULL, O_RDWR, 0); 121 if (nullfd == -1) 122 fatal("open failed"); 123 if (dup2(nullfd, STDERR_FILENO) == -1) 124 fatal("dup2 failed"); 125 if (nullfd != STDERR_FILENO) 126 close(nullfd); 127 128 closefrom(STDERR_FILENO + 1); 129 130 execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); 131 fatal("execl failed"); 132 } 133 134 sigprocmask(SIG_SETMASK, &oldset, NULL); 135 environ_free(env); 136 close(out[1]); 137 138 job = xmalloc(sizeof *job); 139 job->state = JOB_RUNNING; 140 job->flags = flags; 141 142 job->cmd = xstrdup(cmd); 143 job->pid = pid; 144 job->status = 0; 145 146 LIST_INSERT_HEAD(&all_jobs, job, entry); 147 148 job->updatecb = updatecb; 149 job->completecb = completecb; 150 job->freecb = freecb; 151 job->data = data; 152 153 job->fd = out[0]; 154 setblocking(job->fd, 0); 155 156 job->event = bufferevent_new(job->fd, job_read_callback, 157 job_write_callback, job_error_callback, job); 158 if (job->event == NULL) 159 fatalx("out of memory"); 160 bufferevent_enable(job->event, EV_READ|EV_WRITE); 161 162 log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); 163 return (job); 164 } 165 166 /* Kill and free an individual job. */ 167 void 168 job_free(struct job *job) 169 { 170 log_debug("free job %p: %s", job, job->cmd); 171 172 LIST_REMOVE(job, entry); 173 free(job->cmd); 174 175 if (job->freecb != NULL && job->data != NULL) 176 job->freecb(job->data); 177 178 if (job->pid != -1) 179 kill(job->pid, SIGTERM); 180 if (job->event != NULL) 181 bufferevent_free(job->event); 182 if (job->fd != -1) 183 close(job->fd); 184 185 free(job); 186 } 187 188 /* Job buffer read callback. */ 189 static void 190 job_read_callback(__unused struct bufferevent *bufev, void *data) 191 { 192 struct job *job = data; 193 194 if (job->updatecb != NULL) 195 job->updatecb(job); 196 } 197 198 /* 199 * Job buffer write callback. Fired when the buffer falls below watermark 200 * (default is empty). If all the data has been written, disable the write 201 * event. 202 */ 203 static void 204 job_write_callback(__unused struct bufferevent *bufev, void *data) 205 { 206 struct job *job = data; 207 size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); 208 209 log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, 210 (long) job->pid, len); 211 212 if (len == 0) { 213 shutdown(job->fd, SHUT_WR); 214 bufferevent_disable(job->event, EV_WRITE); 215 } 216 } 217 218 /* Job buffer error callback. */ 219 static void 220 job_error_callback(__unused struct bufferevent *bufev, __unused short events, 221 void *data) 222 { 223 struct job *job = data; 224 225 log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); 226 227 if (job->state == JOB_DEAD) { 228 if (job->completecb != NULL) 229 job->completecb(job); 230 job_free(job); 231 } else { 232 bufferevent_disable(job->event, EV_READ); 233 job->state = JOB_CLOSED; 234 } 235 } 236 237 /* Job died (waitpid() returned its pid). */ 238 void 239 job_check_died(pid_t pid, int status) 240 { 241 struct job *job; 242 243 LIST_FOREACH(job, &all_jobs, entry) { 244 if (pid == job->pid) 245 break; 246 } 247 if (job == NULL) 248 return; 249 log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); 250 251 job->status = status; 252 253 if (job->state == JOB_CLOSED) { 254 if (job->completecb != NULL) 255 job->completecb(job); 256 job_free(job); 257 } else { 258 job->pid = -1; 259 job->state = JOB_DEAD; 260 } 261 } 262 263 /* Get job status. */ 264 int 265 job_get_status(struct job *job) 266 { 267 return (job->status); 268 } 269 270 /* Get job data. */ 271 void * 272 job_get_data(struct job *job) 273 { 274 return (job->data); 275 } 276 277 /* Get job event. */ 278 struct bufferevent * 279 job_get_event(struct job *job) 280 { 281 return (job->event); 282 } 283 284 /* Kill all jobs. */ 285 void 286 job_kill_all(void) 287 { 288 struct job *job; 289 290 LIST_FOREACH(job, &all_jobs, entry) { 291 if (job->pid != -1) 292 kill(job->pid, SIGTERM); 293 } 294 } 295 296 /* Are any jobs still running? */ 297 int 298 job_still_running(void) 299 { 300 struct job *job; 301 302 LIST_FOREACH(job, &all_jobs, entry) { 303 if ((~job->flags & JOB_NOWAIT) && job->state == JOB_RUNNING) 304 return (1); 305 } 306 return (0); 307 } 308 309 /* Print job summary. */ 310 void 311 job_print_summary(struct cmdq_item *item, int blank) 312 { 313 struct job *job; 314 u_int n = 0; 315 316 LIST_FOREACH(job, &all_jobs, entry) { 317 if (blank) { 318 cmdq_print(item, "%s", ""); 319 blank = 0; 320 } 321 cmdq_print(item, "Job %u: %s [fd=%d, pid=%ld, status=%d]", 322 n, job->cmd, job->fd, (long)job->pid, job->status); 323 n++; 324 } 325 } 326