1 /* $OpenBSD: job.c,v 1.5 2009/10/11 08:58:05 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 21 #include <fcntl.h> 22 #include <paths.h> 23 #include <string.h> 24 #include <unistd.h> 25 26 #include "tmux.h" 27 28 /* 29 * Job scheduling. Run queued commands in the background and record their 30 * output. 31 */ 32 33 /* All jobs list. */ 34 struct joblist all_jobs = SLIST_HEAD_INITIALIZER(&all_jobs); 35 36 RB_GENERATE(jobs, job, entry, job_cmp); 37 38 int 39 job_cmp(struct job *job1, struct job *job2) 40 { 41 return (strcmp(job1->cmd, job2->cmd)); 42 } 43 44 /* Initialise job tree. */ 45 void 46 job_tree_init(struct jobs *jobs) 47 { 48 RB_INIT(jobs); 49 } 50 51 /* Count the number of jobs in a tree. */ 52 u_int 53 job_tree_size(struct jobs *jobs) 54 { 55 struct job *job; 56 u_int n; 57 58 n = 0; 59 RB_FOREACH(job, jobs, jobs) 60 n++; 61 return (n); 62 } 63 64 /* Destroy a job tree. */ 65 void 66 job_tree_free(struct jobs *jobs) 67 { 68 struct job *job; 69 70 while (!RB_EMPTY(jobs)) { 71 job = RB_ROOT(jobs); 72 RB_REMOVE(jobs, jobs, job); 73 job_free(job); 74 } 75 } 76 77 /* Find a job and return it. */ 78 struct job * 79 job_get(struct jobs *jobs, const char *cmd) 80 { 81 struct job job; 82 83 job.cmd = (char *) cmd; 84 return (RB_FIND(jobs, jobs, &job)); 85 } 86 87 /* Add a job. */ 88 struct job * 89 job_add(struct jobs *jobs, struct client *c, const char *cmd, 90 void (*callbackfn)(struct job *), void (*freefn)(void *), void *data) 91 { 92 struct job *job; 93 94 job = xmalloc(sizeof *job); 95 job->cmd = xstrdup(cmd); 96 job->pid = -1; 97 98 job->client = c; 99 100 job->fd = -1; 101 job->out = buffer_create(BUFSIZ); 102 103 job->callbackfn = callbackfn; 104 job->freefn = freefn; 105 job->data = data; 106 107 job->flags = JOB_DONE; 108 109 if (jobs != NULL) 110 RB_INSERT(jobs, jobs, job); 111 SLIST_INSERT_HEAD(&all_jobs, job, lentry); 112 113 return (job); 114 } 115 116 /* Kill and free an individual job. */ 117 void 118 job_free(struct job *job) 119 { 120 job_kill(job); 121 122 SLIST_REMOVE(&all_jobs, job, job, lentry); 123 xfree(job->cmd); 124 125 if (job->freefn != NULL && job->data != NULL) 126 job->freefn(job->data); 127 128 if (job->fd != -1) 129 close(job->fd); 130 if (job->out != NULL) 131 buffer_destroy(job->out); 132 133 xfree(job); 134 } 135 136 /* Start a job running, if it isn't already. */ 137 int 138 job_run(struct job *job) 139 { 140 int nullfd, out[2], mode; 141 142 if (!(job->flags & JOB_DONE)) 143 return (0); 144 job->flags &= ~JOB_DONE; 145 146 if (pipe(out) != 0) 147 return (-1); 148 149 switch (job->pid = fork()) { 150 case -1: 151 return (-1); 152 case 0: /* child */ 153 sigreset(); 154 /* XXX environ? */ 155 156 nullfd = open(_PATH_DEVNULL, O_RDONLY, 0); 157 if (nullfd < 0) 158 fatal("open failed"); 159 if (dup2(nullfd, STDIN_FILENO) == -1) 160 fatal("dup2 failed"); 161 if (dup2(nullfd, STDERR_FILENO) == -1) 162 fatal("dup2 failed"); 163 if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO) 164 close(nullfd); 165 166 close(out[1]); 167 if (dup2(out[0], STDOUT_FILENO) == -1) 168 fatal("dup2 failed"); 169 if (out[0] != STDOUT_FILENO) 170 close(out[0]); 171 172 execl(_PATH_BSHELL, "sh", "-c", job->cmd, (char *) NULL); 173 fatal("execl failed"); 174 default: /* parent */ 175 close(out[0]); 176 177 job->fd = out[1]; 178 if ((mode = fcntl(job->fd, F_GETFL)) == -1) 179 fatal("fcntl failed"); 180 if (fcntl(job->fd, F_SETFL, mode|O_NONBLOCK) == -1) 181 fatal("fcntl failed"); 182 if (fcntl(job->fd, F_SETFD, FD_CLOEXEC) == -1) 183 fatal("fcntl failed"); 184 185 if (BUFFER_USED(job->out) != 0) 186 buffer_remove(job->out, BUFFER_USED(job->out)); 187 188 return (0); 189 } 190 } 191 192 /* Kill a job. */ 193 void 194 job_kill(struct job *job) 195 { 196 if (job->pid == -1) 197 return; 198 kill(job->pid, SIGTERM); 199 job->pid = -1; 200 } 201