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
35 struct joblist all_jobs
= SLIST_HEAD_INITIALIZER(all_jobs
);
37 RB_GENERATE(jobs
, job
, entry
, job_cmp
);
39 void job_callback(struct bufferevent
*, short, void *);
42 job_cmp(struct job
*job1
, struct job
*job2
)
44 return (strcmp(job1
->cmd
, job2
->cmd
));
47 /* Initialise job tree. */
49 job_tree_init(struct jobs
*jobs
)
54 /* Destroy a job tree. */
56 job_tree_free(struct jobs
*jobs
)
60 while (!RB_EMPTY(jobs
)) {
62 RB_REMOVE(jobs
, jobs
, job
);
67 /* Find a job and return it. */
69 job_get(struct jobs
*jobs
, const char *cmd
)
73 job
.cmd
= (char *) cmd
;
74 return (RB_FIND(jobs
, jobs
, &job
));
79 job_add(struct jobs
*jobs
, int flags
, struct client
*c
, const char *cmd
,
80 void (*callbackfn
)(struct job
*), void (*freefn
)(void *), void *data
)
84 job
= xmalloc(sizeof *job
);
85 job
->cmd
= xstrdup(cmd
);
94 job
->callbackfn
= callbackfn
;
101 RB_INSERT(jobs
, jobs
, job
);
102 SLIST_INSERT_HEAD(&all_jobs
, job
, lentry
);
107 /* Remove job from tree and free. */
109 job_remove(struct jobs
*jobs
, struct job
*job
)
112 RB_REMOVE(jobs
, jobs
, job
);
116 /* Kill and free an individual job. */
118 job_free(struct job
*job
)
122 SLIST_REMOVE(&all_jobs
, job
, job
, lentry
);
125 if (job
->freefn
!= NULL
&& job
->data
!= NULL
)
126 job
->freefn(job
->data
);
130 if (job
->event
!= NULL
)
131 bufferevent_free(job
->event
);
136 /* Start a job running, if it isn't already. */
138 job_run(struct job
*job
)
140 int nullfd
, out
[2], mode
;
142 if (job
->fd
!= -1 || job
->pid
!= -1)
145 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, out
) != 0)
148 switch (job
->pid
= fork()) {
154 environ_push(&global_environ
);
156 if (dup2(out
[1], STDOUT_FILENO
) == -1)
157 fatal("dup2 failed");
158 if (out
[1] != STDOUT_FILENO
)
162 nullfd
= open(_PATH_DEVNULL
, O_RDWR
, 0);
164 fatal("open failed");
165 if (dup2(nullfd
, STDIN_FILENO
) == -1)
166 fatal("dup2 failed");
167 if (dup2(nullfd
, STDERR_FILENO
) == -1)
168 fatal("dup2 failed");
169 if (nullfd
!= STDIN_FILENO
&& nullfd
!= STDERR_FILENO
)
172 closefrom(STDERR_FILENO
+ 1);
174 execl(_PATH_BSHELL
, "sh", "-c", job
->cmd
, (char *) NULL
);
175 fatal("execl failed");
176 default: /* parent */
180 if ((mode
= fcntl(job
->fd
, F_GETFL
)) == -1)
181 fatal("fcntl failed");
182 if (fcntl(job
->fd
, F_SETFL
, mode
|O_NONBLOCK
) == -1)
183 fatal("fcntl failed");
185 if (job
->event
!= NULL
)
186 bufferevent_free(job
->event
);
188 bufferevent_new(job
->fd
, NULL
, NULL
, job_callback
, job
);
189 bufferevent_enable(job
->event
, EV_READ
);
195 /* Job buffer error callback. */
198 job_callback(unused
struct bufferevent
*bufev
, unused
short events
, void *data
)
200 struct job
*job
= data
;
202 bufferevent_disable(job
->event
, EV_READ
);
206 if (job
->pid
== -1) {
207 if (job
->callbackfn
!= NULL
)
208 job
->callbackfn(job
);
209 if ((!job
->flags
& JOB_PERSIST
))
214 /* Job died (waitpid() returned its pid). */
216 job_died(struct job
*job
, int status
)
218 job
->status
= status
;
222 if (job
->callbackfn
!= NULL
)
223 job
->callbackfn(job
);
224 if ((!job
->flags
& JOB_PERSIST
))
231 job_kill(struct job
*job
)
235 kill(job
->pid
, SIGTERM
);