Annotation of embedaddon/tmux/job.c, revision 1.1.1.1
1.1 misho 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_callback(struct bufferevent *, short, void *);
36: static void job_write_callback(struct bufferevent *, void *);
37:
38: /* All jobs list. */
39: struct joblist all_jobs = LIST_HEAD_INITIALIZER(all_jobs);
40:
41: /* Start a job running, if it isn't already. */
42: struct job *
43: job_run(const char *cmd, struct session *s, const char *cwd,
44: void (*callbackfn)(struct job *), void (*freefn)(void *), void *data)
45: {
46: struct job *job;
47: struct environ *env;
48: pid_t pid;
49: int nullfd, out[2];
50: const char *home;
51:
52: if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, out) != 0)
53: return (NULL);
54:
55: env = environ_for_session(s);
56: switch (pid = fork()) {
57: case -1:
58: environ_free(env);
59: close(out[0]);
60: close(out[1]);
61: return (NULL);
62: case 0: /* child */
63: clear_signals(1);
64:
65: if (cwd == NULL || chdir(cwd) != 0) {
66: if ((home = find_home()) == NULL || chdir(home) != 0)
67: chdir("/");
68: }
69:
70: environ_push(env);
71: environ_free(env);
72:
73: if (dup2(out[1], STDIN_FILENO) == -1)
74: fatal("dup2 failed");
75: if (dup2(out[1], STDOUT_FILENO) == -1)
76: fatal("dup2 failed");
77: if (out[1] != STDIN_FILENO && out[1] != STDOUT_FILENO)
78: close(out[1]);
79: close(out[0]);
80:
81: nullfd = open(_PATH_DEVNULL, O_RDWR, 0);
82: if (nullfd < 0)
83: fatal("open failed");
84: if (dup2(nullfd, STDERR_FILENO) == -1)
85: fatal("dup2 failed");
86: if (nullfd != STDERR_FILENO)
87: close(nullfd);
88:
89: closefrom(STDERR_FILENO + 1);
90:
91: execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);
92: fatal("execl failed");
93: }
94:
95: /* parent */
96: environ_free(env);
97: close(out[1]);
98:
99: job = xmalloc(sizeof *job);
100: job->state = JOB_RUNNING;
101:
102: job->cmd = xstrdup(cmd);
103: job->pid = pid;
104: job->status = 0;
105:
106: LIST_INSERT_HEAD(&all_jobs, job, lentry);
107:
108: job->callbackfn = callbackfn;
109: job->freefn = freefn;
110: job->data = data;
111:
112: job->fd = out[0];
113: setblocking(job->fd, 0);
114:
115: job->event = bufferevent_new(job->fd, NULL, job_write_callback,
116: job_callback, job);
117: bufferevent_enable(job->event, EV_READ|EV_WRITE);
118:
119: log_debug("run job %p: %s, pid %ld", job, job->cmd, (long) job->pid);
120: return (job);
121: }
122:
123: /* Kill and free an individual job. */
124: void
125: job_free(struct job *job)
126: {
127: log_debug("free job %p: %s", job, job->cmd);
128:
129: LIST_REMOVE(job, lentry);
130: free(job->cmd);
131:
132: if (job->freefn != NULL && job->data != NULL)
133: job->freefn(job->data);
134:
135: if (job->pid != -1)
136: kill(job->pid, SIGTERM);
137: if (job->event != NULL)
138: bufferevent_free(job->event);
139: if (job->fd != -1)
140: close(job->fd);
141:
142: free(job);
143: }
144:
145: /* Called when output buffer falls below low watermark (default is 0). */
146: static void
147: job_write_callback(__unused struct bufferevent *bufev, void *data)
148: {
149: struct job *job = data;
150: size_t len = EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job->event));
151:
152: log_debug("job write %p: %s, pid %ld, output left %zu", job, job->cmd,
153: (long) job->pid, len);
154:
155: if (len == 0) {
156: shutdown(job->fd, SHUT_WR);
157: bufferevent_disable(job->event, EV_WRITE);
158: }
159: }
160:
161: /* Job buffer error callback. */
162: static void
163: job_callback(__unused struct bufferevent *bufev, __unused short events,
164: void *data)
165: {
166: struct job *job = data;
167:
168: log_debug("job error %p: %s, pid %ld", job, job->cmd, (long) job->pid);
169:
170: if (job->state == JOB_DEAD) {
171: if (job->callbackfn != NULL)
172: job->callbackfn(job);
173: job_free(job);
174: } else {
175: bufferevent_disable(job->event, EV_READ);
176: job->state = JOB_CLOSED;
177: }
178: }
179:
180: /* Job died (waitpid() returned its pid). */
181: void
182: job_died(struct job *job, int status)
183: {
184: log_debug("job died %p: %s, pid %ld", job, job->cmd, (long) job->pid);
185:
186: job->status = status;
187:
188: if (job->state == JOB_CLOSED) {
189: if (job->callbackfn != NULL)
190: job->callbackfn(job);
191: job_free(job);
192: } else {
193: job->pid = -1;
194: job->state = JOB_DEAD;
195: }
196: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>