4 * Copyright (c) 2009 Nicholas Marriott <nicm@users.sourceforge.net>
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.
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.
19 #include <sys/types.h>
20 #include <sys/socket.h>
30 * Job scheduling. Run queued commands in the background and record their
34 void job_callback(struct bufferevent
*, short, void *);
35 void job_write_callback(struct bufferevent
*, void *);
38 struct joblist all_jobs
= LIST_HEAD_INITIALIZER(all_jobs
);
40 /* Start a job running, if it isn't already. */
42 job_run(const char *cmd
, struct session
*s
,
43 void (*callbackfn
)(struct job
*), void (*freefn
)(void *), void *data
)
50 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, out
) != 0)
54 environ_copy(&global_environ
, &env
);
56 environ_copy(&s
->environ
, &env
);
57 server_fill_environ(s
, &env
);
59 switch (pid
= fork()) {
69 if (dup2(out
[1], STDIN_FILENO
) == -1)
71 if (dup2(out
[1], STDOUT_FILENO
) == -1)
73 if (out
[1] != STDIN_FILENO
&& out
[1] != STDOUT_FILENO
)
77 nullfd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
80 if (dup2(nullfd
, STDERR_FILENO
) == -1)
82 if (nullfd
!= STDERR_FILENO
)
85 closefrom(STDERR_FILENO
+ 1);
87 execl(_PATH_BSHELL
, "sh", "-c", cmd
, (char *) NULL
);
88 fatal("execl failed");
95 job
= xmalloc(sizeof *job
);
96 job
->cmd
= xstrdup(cmd
);
100 LIST_INSERT_HEAD(&all_jobs
, job
, lentry
);
102 job
->callbackfn
= callbackfn
;
103 job
->freefn
= freefn
;
107 setblocking(job
->fd
, 0);
109 job
->event
= bufferevent_new(job
->fd
, NULL
, job_write_callback
,
111 bufferevent_enable(job
->event
, EV_READ
|EV_WRITE
);
113 log_debug("run job %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
117 /* Kill and free an individual job. */
119 job_free(struct job
*job
)
121 log_debug("free job %p: %s", job
, job
->cmd
);
123 LIST_REMOVE(job
, lentry
);
126 if (job
->freefn
!= NULL
&& job
->data
!= NULL
)
127 job
->freefn(job
->data
);
130 kill(job
->pid
, SIGTERM
);
131 if (job
->event
!= NULL
)
132 bufferevent_free(job
->event
);
139 /* Called when output buffer falls below low watermark (default is 0). */
141 job_write_callback(unused
struct bufferevent
*bufev
, void *data
)
143 struct job
*job
= data
;
144 size_t len
= EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job
->event
));
146 log_debug("job write %p: %s, pid %ld, output left %zu", job
, job
->cmd
,
147 (long) job
->pid
, len
);
150 shutdown(job
->fd
, SHUT_WR
);
151 bufferevent_disable(job
->event
, EV_WRITE
);
155 /* Job buffer error callback. */
157 job_callback(unused
struct bufferevent
*bufev
, unused
short events
, void *data
)
159 struct job
*job
= data
;
161 log_debug("job error %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
163 if (job
->pid
== -1) {
164 if (job
->callbackfn
!= NULL
)
165 job
->callbackfn(job
);
168 bufferevent_disable(job
->event
, EV_READ
);
174 /* Job died (waitpid() returned its pid). */
176 job_died(struct job
*job
, int status
)
178 log_debug("job died %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
180 job
->status
= status
;
183 if (job
->callbackfn
!= NULL
)
184 job
->callbackfn(job
);