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 *);
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
,
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
);
55 server_fill_environ(NULL
, &env
);
57 switch (pid
= fork()) {
67 if (dup2(out
[1], STDOUT_FILENO
) == -1)
69 if (out
[1] != STDOUT_FILENO
)
73 nullfd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
76 if (dup2(nullfd
, STDIN_FILENO
) == -1)
78 if (dup2(nullfd
, STDERR_FILENO
) == -1)
80 if (nullfd
!= STDIN_FILENO
&& nullfd
!= STDERR_FILENO
)
83 closefrom(STDERR_FILENO
+ 1);
85 execl(_PATH_BSHELL
, "sh", "-c", cmd
, (char *) NULL
);
86 fatal("execl failed");
93 job
= xmalloc(sizeof *job
);
94 job
->cmd
= xstrdup(cmd
);
98 LIST_INSERT_HEAD(&all_jobs
, job
, lentry
);
100 job
->callbackfn
= callbackfn
;
101 job
->freefn
= freefn
;
105 setblocking(job
->fd
, 0);
107 job
->event
= bufferevent_new(job
->fd
, NULL
, NULL
, job_callback
, job
);
108 bufferevent_enable(job
->event
, EV_READ
);
110 log_debug("run job %p: %s, pid %ld", job
, job
->cmd
, (long) job
->pid
);
114 /* Kill and free an individual job. */
116 job_free(struct job
*job
)
118 log_debug("free job %p: %s", job
, job
->cmd
);
120 LIST_REMOVE(job
, lentry
);
123 if (job
->freefn
!= NULL
&& job
->data
!= NULL
)
124 job
->freefn(job
->data
);
127 kill(job
->pid
, SIGTERM
);
128 if (job
->event
!= NULL
)
129 bufferevent_free(job
->event
);
136 /* 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
);