Annotation of src/usr.bin/tmux/job.c, Revision 1.17
1.17 ! nicm 1: /* $OpenBSD: job.c,v 1.16 2010/04/04 19:02:09 nicm Exp $ */
1.1 nicm 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>
1.11 nicm 20: #include <sys/socket.h>
1.1 nicm 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:
1.2 nicm 34: /* All jobs list. */
1.15 nicm 35: struct joblist all_jobs = SLIST_HEAD_INITIALIZER(all_jobs);
1.2 nicm 36:
1.1 nicm 37: RB_GENERATE(jobs, job, entry, job_cmp);
38:
1.11 nicm 39: void job_callback(struct bufferevent *, short, void *);
40:
1.1 nicm 41: int
42: job_cmp(struct job *job1, struct job *job2)
43: {
44: return (strcmp(job1->cmd, job2->cmd));
45: }
46:
47: /* Initialise job tree. */
48: void
49: job_tree_init(struct jobs *jobs)
50: {
51: RB_INIT(jobs);
52: }
53:
54: /* Destroy a job tree. */
55: void
56: job_tree_free(struct jobs *jobs)
57: {
58: struct job *job;
59:
60: while (!RB_EMPTY(jobs)) {
61: job = RB_ROOT(jobs);
62: RB_REMOVE(jobs, jobs, job);
63: job_free(job);
64: }
65: }
66:
67: /* Find a job and return it. */
68: struct job *
69: job_get(struct jobs *jobs, const char *cmd)
70: {
71: struct job job;
72:
73: job.cmd = (char *) cmd;
74: return (RB_FIND(jobs, jobs, &job));
75: }
76:
77: /* Add a job. */
78: struct job *
1.9 nicm 79: job_add(struct jobs *jobs, int flags, struct client *c, const char *cmd,
1.1 nicm 80: void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
81: {
82: struct job *job;
1.14 nicm 83:
1.1 nicm 84: job = xmalloc(sizeof *job);
85: job->cmd = xstrdup(cmd);
1.2 nicm 86: job->pid = -1;
1.9 nicm 87: job->status = 0;
1.1 nicm 88:
89: job->client = c;
90:
91: job->fd = -1;
1.11 nicm 92: job->event = NULL;
1.1 nicm 93:
94: job->callbackfn = callbackfn;
95: job->freefn = freefn;
96: job->data = data;
97:
1.11 nicm 98: job->flags = flags;
1.3 nicm 99:
1.4 nicm 100: if (jobs != NULL)
101: RB_INSERT(jobs, jobs, job);
1.2 nicm 102: SLIST_INSERT_HEAD(&all_jobs, job, lentry);
1.9 nicm 103:
1.1 nicm 104: return (job);
1.9 nicm 105: }
106:
107: /* Remove job from tree and free. */
108: void
109: job_remove(struct jobs *jobs, struct job *job)
110: {
111: if (jobs != NULL)
112: RB_REMOVE(jobs, jobs, job);
113: job_free(job);
1.1 nicm 114: }
115:
116: /* Kill and free an individual job. */
117: void
118: job_free(struct job *job)
119: {
120: job_kill(job);
121:
1.5 nicm 122: SLIST_REMOVE(&all_jobs, job, job, lentry);
1.1 nicm 123: xfree(job->cmd);
1.4 nicm 124:
125: if (job->freefn != NULL && job->data != NULL)
126: job->freefn(job->data);
1.1 nicm 127:
128: if (job->fd != -1)
129: close(job->fd);
1.11 nicm 130: if (job->event != NULL)
131: bufferevent_free(job->event);
1.1 nicm 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:
1.11 nicm 142: if (job->fd != -1 || job->pid != -1)
1.1 nicm 143: return (0);
144:
1.11 nicm 145: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
1.1 nicm 146: return (-1);
147:
148: switch (job->pid = fork()) {
149: case -1:
150: return (-1);
151: case 0: /* child */
1.17 ! nicm 152: clear_signals();
1.16 nicm 153:
154: environ_push(&global_environ);
1.1 nicm 155:
1.7 nicm 156: if (dup2(out[1], STDOUT_FILENO) == -1)
1.6 nicm 157: fatal("dup2 failed");
1.7 nicm 158: if (out[1] != STDOUT_FILENO)
159: close(out[1]);
160: close(out[0]);
1.6 nicm 161:
1.7 nicm 162: nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
1.1 nicm 163: if (nullfd < 0)
164: fatal("open failed");
165: if (dup2(nullfd, STDIN_FILENO) == -1)
166: fatal("dup2 failed");
167: if (dup2(nullfd, STDERR_FILENO) == -1)
168: fatal("dup2 failed");
169: if (nullfd != STDIN_FILENO && nullfd != STDERR_FILENO)
170: close(nullfd);
171:
172: execl(_PATH_BSHELL, "sh", "-c", job->cmd, (char *) NULL);
173: fatal("execl failed");
174: default: /* parent */
1.7 nicm 175: close(out[1]);
1.1 nicm 176:
1.7 nicm 177: job->fd = out[0];
1.1 nicm 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:
1.11 nicm 185: if (job->event != NULL)
186: bufferevent_free(job->event);
1.14 nicm 187: job->event =
1.11 nicm 188: bufferevent_new(job->fd, NULL, NULL, job_callback, job);
189: bufferevent_enable(job->event, EV_READ);
1.1 nicm 190:
191: return (0);
192: }
1.11 nicm 193: }
194:
195: /* Job buffer error callback. */
1.13 nicm 196: /* ARGSUSED */
1.11 nicm 197: void
198: job_callback(unused struct bufferevent *bufev, unused short events, void *data)
199: {
200: struct job *job = data;
201:
202: bufferevent_disable(job->event, EV_READ);
203: close(job->fd);
204: job->fd = -1;
205:
1.12 nicm 206: if (job->pid == -1) {
207: if (job->callbackfn != NULL)
208: job->callbackfn(job);
209: if ((!job->flags & JOB_PERSIST))
210: job_free(job);
211: }
1.11 nicm 212: }
213:
214: /* Job died (waitpid() returned its pid). */
215: void
216: job_died(struct job *job, int status)
217: {
218: job->status = status;
219: job->pid = -1;
1.14 nicm 220:
1.12 nicm 221: if (job->fd == -1) {
222: if (job->callbackfn != NULL)
223: job->callbackfn(job);
224: if ((!job->flags & JOB_PERSIST))
225: job_free(job);
226: }
1.1 nicm 227: }
228:
229: /* Kill a job. */
230: void
231: job_kill(struct job *job)
232: {
233: if (job->pid == -1)
234: return;
235: kill(job->pid, SIGTERM);
236: job->pid = -1;
237: }