comparison sciworkerd.c @ 0:f1de39079243

misc: initial import
author David Demelier <markand@malikania.fr>
date Mon, 07 Jun 2021 09:41:37 +0200
parents
children 5afdb14df924
comparison
equal deleted inserted replaced
-1:000000000000 0:f1de39079243
1 #include <sys/queue.h>
2 #include <sys/types.h>
3 #include <sys/wait.h>
4 #include <assert.h>
5 #include <err.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <poll.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14
15 #include "project.h"
16 #include "config.h"
17 #include "log.h"
18 #include "util.h"
19
20 struct job {
21 pid_t child;
22 int running;
23 int pipe[2];
24 char project[PROJECT_NAME_MAX];
25 char out[SCI_JOB_OUTPUT_MAX];
26 TAILQ_ENTRY(job) link;
27 };
28
29 struct fds {
30 struct pollfd *list;
31 size_t listsz;
32 };
33
34 struct result {
35 pid_t pid;
36 int status;
37 };
38
39 TAILQ_HEAD(jobs, job);
40
41 static struct jobs running = TAILQ_HEAD_INITIALIZER(running);
42 static struct jobs queue = TAILQ_HEAD_INITIALIZER(queue);
43 static int sigpipe[2];
44
45 static struct job *
46 find(const char *project)
47 {
48 struct job *j;
49
50 TAILQ_FOREACH(j, &running, link)
51 if (strcmp(j->project, project) == 0)
52 return j;
53
54 return NULL;
55 }
56
57 static struct job *
58 find_by_fd(int fd)
59 {
60 struct job *j;
61
62 TAILQ_FOREACH(j, &running, link)
63 if (j->pipe[0] == fd)
64 return j;
65
66 return NULL;
67 }
68
69 static struct job *
70 find_by_pid(pid_t pid)
71 {
72 struct job *j;
73
74 TAILQ_FOREACH(j, &running, link)
75 if (j->child == pid)
76 return j;
77
78 return NULL;
79 }
80
81 static int
82 spawn(const char *project, const char *script)
83 {
84 struct job *j;
85
86 if (find(project))
87 return -1;
88
89 j = util_calloc(1, sizeof (*j));
90 j->pipe[0] = j->pipe[1] = -1;
91 j->running = 1;
92 strlcpy(j->project, project, sizeof (j->project));
93
94 if (pipe(j->pipe) < 0)
95 goto cleanup;
96
97 switch ((j->child = fork())) {
98 case -1:
99 goto cleanup;
100 case 0:
101 /* Child. */
102 dup2(j->pipe[1], STDOUT_FILENO);
103 dup2(j->pipe[1], STDERR_FILENO);
104 close(j->pipe[0]);
105 close(j->pipe[1]);
106
107 if (execl(script, script, NULL) < 0)
108 exit(0);
109 break;
110 default:
111 /* Parent */
112 TAILQ_INSERT_TAIL(&running, j, link);
113 break;
114 }
115
116 return 0;
117
118 cleanup:
119 if (j->pipe[0] != -1)
120 close(j->pipe[0]);
121 if (j->pipe[1] != -1)
122 close(j->pipe[1]);
123
124 free(j);
125
126 return -1;
127 }
128
129 static void
130 complete(int signum, siginfo_t *sinfo, void *ctx)
131 {
132 (void)ctx;
133 (void)signum;
134
135 struct result r;
136
137 if (sinfo->si_code != CLD_EXITED)
138 return;
139
140 r.pid = sinfo->si_pid;
141 r.status = 0;
142
143 if (waitpid(sinfo->si_pid, &r.status, 0) < 0) {
144 log_warn("waitpid: %s", strerror(errno));
145 return;
146 }
147
148 /*
149 * Signal may happen at any time from any thread so we can't use
150 * mutexes so use the good old self-pipe trick. Yes, signals are
151 * probably the most fundamental broken UNIX feature.
152 */
153 if (write(sigpipe[1], &r, sizeof (r)) < 0)
154 err(1, "write");
155 }
156
157 static void
158 init(void)
159 {
160 struct sigaction sa;
161 int flags;
162
163 sa.sa_flags = SA_SIGINFO;
164 sa.sa_sigaction = complete;
165 sigemptyset(&sa.sa_mask);
166
167 if (sigaction(SIGCHLD, &sa, NULL) < 0)
168 err(1, "sigaction");
169
170 log_open("sciworkerd");
171
172 if (pipe(sigpipe) < 0)
173 err(1, "pipe");
174 if ((flags = fcntl(sigpipe[1], F_GETFL, 0)) < 0 ||
175 fcntl(sigpipe[1], F_SETFL, flags | O_NONBLOCK) < 0)
176 err(1, "fcntl");
177 }
178
179 static struct fds
180 prepare(void)
181 {
182 struct fds fds = {0};
183 struct job *j;
184 size_t i = 1;
185
186 TAILQ_FOREACH(j, &running, link)
187 fds.listsz++;
188
189 fds.list = util_calloc(++fds.listsz, sizeof (*fds.list));
190 fds.list[0].fd = sigpipe[0];
191 fds.list[0].events = POLLIN;
192
193 TAILQ_FOREACH(j, &running, link) {
194 fds.list[i].fd = j->pipe[0];
195 fds.list[i++].events = POLLIN | POLLPRI;
196 }
197
198 return fds;
199 }
200
201 static void
202 finished(pid_t pid)
203 {
204 struct job *job;
205
206 if (!(job = find_by_pid(pid)))
207 return;
208
209 /* TODO: send response. */
210
211 TAILQ_REMOVE(&running, job, link);
212 free(job);
213 }
214
215 static void
216 run(void)
217 {
218 struct fds fds;
219 struct result r;
220 struct job *job;
221 char buf[BUFSIZ];
222 ssize_t nr;
223
224 fds = prepare();
225
226 if (poll(fds.list, fds.listsz, -1) < 0 && errno != EINTR)
227 err(1, "poll");
228
229 for (size_t i = 1; i < fds.listsz; ++i) {
230 if (fds.list[i].revents == 0)
231 continue;
232 if (!(job = find_by_fd(fds.list[i].fd)))
233 continue;
234
235 if ((nr = read(fds.list[i].fd, buf, sizeof (buf) - 1)) <= 0) {
236 finished(job->child);
237 } else {
238 buf[nr] = 0;
239 strlcat(job->out, buf, sizeof (job->out));
240 }
241 }
242
243 if (fds.list->revents) {
244 r.pid = 0;
245 r.status = 0;
246
247 if (read(sigpipe[0], &r, sizeof (r)) <= 0 && errno != EINTR)
248 err(1, "read");
249
250 finished(r.pid);
251 }
252 }
253
254 int
255 main(int argc, char **argv)
256 {
257 (void)argc;
258 (void)argv;
259
260 init();
261
262 for (;;)
263 run();
264 }