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 *);
37 struct joblist all_jobs
= LIST_HEAD_INITIALIZER(all_jobs
);
39 /* Start a job running, if it isn't already. */
41 job_run(const char *cmd
,
42 void (*callbackfn
)(struct job
*), void (*freefn
)(void *), void *data
)
49 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, out
) != 0)
53 environ_copy(&global_environ
, &env
);
54 server_fill_environ(NULL
, &env
);
56 switch (pid
= fork()) {
66 if (dup2(out
[1], STDOUT_FILENO
) == -1)
68 if (out
[1] != STDOUT_FILENO
)
72 nullfd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
75 if (dup2(nullfd
, STDIN_FILENO
) == -1)
77 if (dup2(nullfd
, STDERR_FILENO
) == -1)
79 if (nullfd
!= STDIN_FILENO
&& nullfd
!= STDERR_FILENO
)
82 closefrom(STDERR_FILENO
+ 1);
84 execl(_PATH_BSHELL
, "sh", "-c", cmd
, (char *) NULL
);
85 fatal("execl failed");
92 job
= xmalloc(sizeof *job
);
93 job
->cmd
= xstrdup(cmd
);
97 LIST_INSERT_HEAD(&all_jobs
, job
, lentry
);
99 job
->callbackfn
= callbackfn
;
100 job
->freefn
= freefn
;
104 setblocking(job
->fd
, 0);
106 job
->event
= bufferevent_new(job
->fd
, NULL
, NULL
, job_callback
, job
);
107 bufferevent_enable(job
->event
, EV_READ
);
109 log_debug("run job %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
113 /* Kill and free an individual job. */
115 job_free(struct job
*job
)
117 log_debug("free job %p: %s", job
, job
->cmd
);
119 LIST_REMOVE(job
, lentry
);
122 if (job
->freefn
!= NULL
&& job
->data
!= NULL
)
123 job
->freefn(job
->data
);
126 kill(job
->pid
, SIGTERM
);
129 if (job
->event
!= NULL
)
130 bufferevent_free(job
->event
);
135 /* Job buffer error callback. */
138 job_callback(unused
struct bufferevent
*bufev
, unused
short events
, void *data
)
140 struct job
*job
= data
;
142 log_debug("job error %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
144 if (job
->pid
== -1) {
145 if (job
->callbackfn
!= NULL
)
146 job
->callbackfn(job
);
149 bufferevent_disable(job
->event
, EV_READ
);
155 /* Job died (waitpid() returned its pid). */
157 job_died(struct job
*job
, int status
)
159 log_debug("job died %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
161 job
->status
= status
;
164 if (job
->callbackfn
!= NULL
)
165 job
->callbackfn(job
);