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 /* All jobs list. */ 40 struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); 41 42 /* Start a job running, if it isn't already. */ 43 struct job * 44 job_run(const char *cmd, struct session *s, const char *cwd, 45 job_update_cb updatecb, job_complete_cb completecb, job_free_cb freecb, 46 void *data, int flags) 47 { 48 struct job *job; 49 struct environ *env; 50 pid_t pid; 51 int nullfd, out[2]; 52 const char *home; 53 sigset_t set, oldset; 54 55 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) 56 return (NULL); 57 58 /* 59 * Do not set TERM during .tmux.conf, it is nice to be able to use 60 * if-shell to decide on default-terminal based on outside TERM. 61 */ 62 env = environ_for_session(s, !cfg_finished); 63 64 sigfillset(&set); 65 sigprocmask(SIG_BLOCK, &set, &oldset); 66 switch (pid = fork()) { 67 case -1: 68 sigprocmask(SIG_SETMASK, &oldset, NULL); 69 environ_free(env); 70 close(out[0]); 71 close(out[1]); 72 return (NULL); 73 case 0: 74 proc_clear_signals(server_proc, 1); 75 sigprocmask(SIG_SETMASK, &oldset, NULL); 76 77 if (cwd == NULL || chdir(cwd) != 0) { 78 if ((home = find_home()) == NULL || chdir(home) != 0) 79 chdir("/"); 80 } 81 82 environ_push(env); 83 environ_free(env); 84 85 if (dup2(out[1], STDIN_FILENO) == -1) 86 fatal("dup2 failed"); 87 if (dup2(out[1], STDOUT_FILENO) == -1) 88 fatal("dup2 failed"); 89 if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO) 90 close(out[1]); 91 close(out[0]); 92 93 nullfd = open(_PATH_DEVNULL, O_RDWR, 0); 94 if (nullfd < 0) 95 fatal("open failed"); 96 if (dup2(nullfd, STDERR_FILENO) == -1) 97 fatal("dup2 failed"); 98 if (nullfd != STDERR_FILENO) 99 close(nullfd); 100 101 closefrom(STDERR_FILENO + 1); 102 103 execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); 104 fatal("execl failed"); 105 } 106 107 sigprocmask(SIG_SETMASK, &oldset, NULL); 108 environ_free(env); 109 close(out[1]); 110 111 job = xmalloc(sizeof *job); 112 job->state = JOB_RUNNING; 113 job->flags = flags; 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