1 /* $OpenBSD: job.c,v 1.48 2017/07/14 18:49:07 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/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 /* All jobs list. */ 41 struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); 42 43 /* Start a job running, if it isn't already. */ 44 struct job * 45 job_run(const char *cmd, struct session *s, const char *cwd, 46 job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, 47 void *data) 48 { 49 struct job *job; 50 struct environ *env; 51 pid_t pid; 52 int nullfd, out[2]; 53 const char *home; 54 sigset_t set, oldset; 55 56 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) 57 return (NULL); 58 59 /* 60 * Do not set TERM during .tmux.conf, it is nice to be able to use 61 * if-shell to decide on default-terminal based on outside TERM. 62 */ 63 env = environ_for_session(s, !cfg_finished); 64 65 sigfillset(&set); 66 sigprocmask(SIG_BLOCK, &set, &oldset); 67 switch (pid = fork()) { 68 case -1: 69 sigprocmask(SIG_SETMASK, &oldset, NULL); 70 environ_free(env); 71 close(out[0]); 72 close(out[1]); 73 return (NULL); 74 case 0: 75 proc_clear_signals(server_proc, 1); 76 sigprocmask(SIG_SETMASK, &oldset, NULL); 77 78 if (cwd == NULL || chdir(cwd) != 0) { 79 if ((home = find_home()) == NULL || chdir(home) != 0) 80 chdir("/"); 81 } 82 83 environ_push(env); 84 environ_free(env); 85 86 if (dup2(out[1], STDIN_FILENO) == -1) 87 fatal("dup2 failed"); 88 if (dup2(out[1], STDOUT_FILENO) == -1) 89 fatal("dup2 failed"); 90 if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) 91 close(out[1]); 92 close(out[0]); 93 94 nullfd = open(_PATH_DEVNULL, O_RDWR, 0); 95 if (nullfd < 0) 96 fatal("open failed"); 97 if (dup2(nullfd, STDERR_FILENO) == -1) 98 fatal("dup2 failed"); 99 if (nullfd != STDERR_FILENO) 100 close(nullfd); 101 102 closefrom(STDERR_FILENO + 1); 103 104 execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); 105 fatal("execl failed"); 106 } 107 108 sigprocmask(SIG_SETMASK, &oldset, NULL); 109 environ_free(env); 110 close(out[1]); 111 112 job = xmalloc(sizeof *job); 113 job->state = JOB_RUNNING; 114 115 job->cmd = xstrdup(cmd); 116 job->pid = pid; 117 job->status = 0; 118 119 LIST_INSERT_HEAD(&all_jobs, job, entry); 120 121 job->updatecb = updatecb; 122 job->completecb = completecb; 123 job->freecb = freecb; 124 job->data = data; 125 126 job->fd = out[0]; 127 setblocking(job->fd, 0); 128 129 job->event = bufferevent_new(job->fd, job_read_callback, 130 job_write_callback, job_error_callback, job); 131 bufferevent_enable(job->event, EV_READ|EV_WRITE); 132 133 log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); 134 return (job); 135 } 136 137 /* Kill and free an individual job. */ 138 void 139 job_free(struct job *job) 140 { 141 log_debug("free job %p: %s", job, job->cmd); 142 143 LIST_REMOVE(job, entry); 144 free(job->cmd); 145 146 if (job->freecb != NULL && job->data != NULL) 147 job->freecb(job->data); 148 149 if (job->pid != -1) 150 kill(job->pid, SIGTERM); 151 if (job->event != NULL) 152 bufferevent_free(job->event); 153 if (job->fd != -1) 154 close(job->fd); 155 156 free(job); 157 } 158 159 /* Job buffer read callback. */ 160 static void 161 job_read_callback(__unused struct bufferevent *bufev, void *data) 162 { 163 struct job *job = data; 164 165 if (job->updatecb != NULL) 166 job->updatecb(job); 167 } 168 169 /* 170 * Job buffer write callback. Fired when the buffer falls below watermark 171 * (default is empty). If all the data has been written, disable the write 172 * event. 173 */ 174 static void 175 job_write_callback(__unused struct bufferevent *bufev, void *data) 176 { 177 struct job *job = data; 178 size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event)); 179 180 log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd, 181 (long) job->pid, len); 182 183 if (len == 0) { 184 shutdown(job->fd, SHUT_WR); 185 bufferevent_disable(job->event, EV_WRITE); 186 } 187 } 188 189 /* Job buffer error callback. */ 190 static void 191 job_error_callback(__unused struct bufferevent *bufev, __unused short events, 192 void *data) 193 { 194 struct job *job = data; 195 196 log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); 197 198 if (job->state == JOB_DEAD) { 199 if (job->completecb != NULL) 200 job->completecb(job); 201 job_free(job); 202 } else { 203 bufferevent_disable(job->event, EV_READ); 204 job->state = JOB_CLOSED; 205 } 206 } 207 208 /* Job died (waitpid() returned its pid). */ 209 void 210 job_died(struct job *job, int status) 211 { 212 log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); 213 214 job->status = status; 215 216 if (job->state == JOB_CLOSED) { 217 if (job->completecb != NULL) 218 job->completecb(job); 219 job_free(job); 220 } else { 221 job->pid = -1; 222 job->state = JOB_DEAD; 223 } 224 } 225