1 /* $OpenBSD: job.c,v 1.25 2011/01/26 01:54:56 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net> 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 <string.h> 25 #include <unistd.h> 26 27 #include "tmux.h" 28 29 /* 30 * Job scheduling. Run queued commands in the background and record their 31 * output. 32 */ 33 34 void job_callback(struct bufferevent *, short, void *); 35 36 /* All jobs list. */ 37 struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs); 38 39 /* Start a job running, if it isn't already. */ 40 struct job * 41 job_run(const char *cmd, 42 void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) 43 { 44 struct job *job; 45 struct environ env; 46 pid_t pid; 47 int nullfd, out[2]; 48 49 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0) 50 return (NULL); 51 52 environ_init(&env); 53 environ_copy(&global_environ, &env); 54 server_fill_environ(NULL, &env); 55 56 switch (pid = fork()) { 57 case -1: 58 environ_free(&env); 59 return (NULL); 60 case 0: /* child */ 61 clear_signals(1); 62 63 environ_push(&env); 64 environ_free(&env); 65 66 if (dup2(out[1], STDOUT_FILENO) == -1) 67 fatal("dup2 failed"); 68 if (out[1] != STDOUT_FILENO) 69 close(out[1]); 70 close(out[0]); 71 72 nullfd = open(_PATH_DEVNULL, O_RDWR, 0); 73 if (nullfd < 0) 74 fatal("open failed"); 75 if (dup2(nullfd, STDIN_FILENO) == -1) 76 fatal("dup2 failed"); 77 if (dup2(nullfd, STDERR_FILENO) == -1) 78 fatal("dup2 failed"); 79 if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO) 80 close(nullfd); 81 82 closefrom(STDERR_FILENO + 1); 83 84 execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL); 85 fatal("execl failed"); 86 } 87 88 /* parent */ 89 environ_free(&env); 90 close(out[1]); 91 92 job = xmalloc(sizeof *job); 93 job->cmd = xstrdup(cmd); 94 job->pid = pid; 95 job->status = 0; 96 97 LIST_INSERT_HEAD(&all_jobs, job, lentry); 98 99 job->callbackfn = callbackfn; 100 job->freefn = freefn; 101 job->data = data; 102 103 job->fd = out[0]; 104 setblocking(job->fd, 0); 105 106 job->event = bufferevent_new(job->fd, NULL, NULL, job_callback, job); 107 bufferevent_enable(job->event, EV_READ); 108 109 log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid); 110 return (job); 111 } 112 113 /* Kill and free an individual job. */ 114 void 115 job_free(struct job *job) 116 { 117 log_debug("free job %p: %s", job, job->cmd); 118 119 LIST_REMOVE(job, lentry); 120 xfree(job->cmd); 121 122 if (job->freefn != NULL && job->data != NULL) 123 job->freefn(job->data); 124 125 if (job->pid != -1) 126 kill(job->pid, SIGTERM); 127 if (job->fd != -1) 128 close(job->fd); 129 if (job->event != NULL) 130 bufferevent_free(job->event); 131 132 xfree(job); 133 } 134 135 /* Job buffer error callback. */ 136 /* ARGSUSED */ 137 void 138 job_callback(unused struct bufferevent *bufev, unused short events, void *data) 139 { 140 struct job *job = data; 141 142 log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid); 143 144 if (job->pid == -1) { 145 if (job->callbackfn != NULL) 146 job->callbackfn(job); 147 job_free(job); 148 } else { 149 bufferevent_disable(job->event, EV_READ); 150 close(job->fd); 151 job->fd = -1; 152 } 153 } 154 155 /* Job died (waitpid() returned its pid). */ 156 void 157 job_died(struct job *job, int status) 158 { 159 log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid); 160 161 job->status = status; 162 163 if (job->fd == -1) { 164 if (job->callbackfn != NULL) 165 job->callbackfn(job); 166 job_free(job); 167 } else 168 job->pid = -1; 169 } 170