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