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