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