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>
31 * Job scheduling. Run queued commands in the background and record their
35 void job_callback(struct bufferevent
*, short, void *);
36 void job_write_callback(struct bufferevent
*, void *);
39 struct joblist all_jobs
= LIST_HEAD_INITIALIZER(all_jobs
);
41 /* Start a job running, if it isn't already. */
43 job_run(const char *cmd
, struct session
*s
,
44 void (*callbackfn
)(struct job
*), void (*freefn
)(void *), void *data
)
51 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, out
) != 0)
55 environ_copy(&global_environ
, &env
);
57 environ_copy(&s
->environ
, &env
);
58 server_fill_environ(s
, &env
);
60 switch (pid
= fork()) {
70 if (dup2(out
[1], STDIN_FILENO
) == -1)
72 if (dup2(out
[1], STDOUT_FILENO
) == -1)
74 if (out
[1] != STDIN_FILENO
&& out
[1] != STDOUT_FILENO
)
78 nullfd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
81 if (dup2(nullfd
, STDERR_FILENO
) == -1)
83 if (nullfd
!= STDERR_FILENO
)
86 closefrom(STDERR_FILENO
+ 1);
88 execl(_PATH_BSHELL
, "sh", "-c", cmd
, (char *) NULL
);
89 fatal("execl failed");
96 job
= xmalloc(sizeof *job
);
97 job
->cmd
= xstrdup(cmd
);
101 LIST_INSERT_HEAD(&all_jobs
, job
, lentry
);
103 job
->callbackfn
= callbackfn
;
104 job
->freefn
= freefn
;
108 setblocking(job
->fd
, 0);
110 job
->event
= bufferevent_new(job
->fd
, NULL
, job_write_callback
,
112 bufferevent_enable(job
->event
, EV_READ
|EV_WRITE
);
114 log_debug("run job %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
118 /* Kill and free an individual job. */
120 job_free(struct job
*job
)
122 log_debug("free job %p: %s", job
, job
->cmd
);
124 LIST_REMOVE(job
, lentry
);
127 if (job
->freefn
!= NULL
&& job
->data
!= NULL
)
128 job
->freefn(job
->data
);
131 kill(job
->pid
, SIGTERM
);
132 if (job
->event
!= NULL
)
133 bufferevent_free(job
->event
);
140 /* Called when output buffer falls below low watermark (default is 0). */
142 job_write_callback(unused
struct bufferevent
*bufev
, void *data
)
144 struct job
*job
= data
;
145 size_t len
= EVBUFFER_LENGTH(EVBUFFER_OUTPUT(job
->event
));
147 log_debug("job write %p: %s, pid %ld, output left %zu", job
, job
->cmd
,
148 (long) job
->pid
, len
);
151 shutdown(job
->fd
, SHUT_WR
);
152 bufferevent_disable(job
->event
, EV_WRITE
);
156 /* Job buffer error callback. */
158 job_callback(unused
struct bufferevent
*bufev
, unused
short events
, void *data
)
160 struct job
*job
= data
;
162 log_debug("job error %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
164 if (job
->pid
== -1) {
165 if (job
->callbackfn
!= NULL
)
166 job
->callbackfn(job
);
169 bufferevent_disable(job
->event
, EV_READ
);
175 /* Job died (waitpid() returned its pid). */
177 job_died(struct job
*job
, int status
)
179 log_debug("job died %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
181 job
->status
= status
;
184 if (job
->callbackfn
!= NULL
)
185 job
->callbackfn(job
);