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