Merge lp:~xnox/ubuntu/utopic/sysvinit/upstart-task into lp:ubuntu/utopic/sysvinit
- Utopic (14.10)
- upstart-task
- Merge into utopic
Status: | Rejected |
---|---|
Rejected by: | Martin Pitt |
Proposed branch: | lp:~xnox/ubuntu/utopic/sysvinit/upstart-task |
Merge into: | lp:ubuntu/utopic/sysvinit |
Diff against target: |
812 lines (+739/-4) (has conflicts) 7 files modified
.pc/applied-patches (+1/-0) .pc/startpar-upstart-tasks.patch/startpar/makeboot.c (+688/-0) debian/changelog (+10/-0) debian/patches/series (+1/-0) debian/patches/startpar-upstart-tasks.patch (+28/-0) debian/src/sysv-rc/etc/init.d/rc (+1/-3) startpar/makeboot.c (+10/-1) Text conflict in debian/changelog |
To merge this branch: | bzr merge lp:~xnox/ubuntu/utopic/sysvinit/upstart-task |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pitt | Needs Fixing | ||
Adam Conrad | Pending | ||
Stéphane Graber | Pending | ||
Steve Langasek | Pending | ||
Ubuntu branches | Pending | ||
Review via email: mp+221493@code.launchpad.net |
Commit message
Description of the change
Some of the init.d scripts have been converted to tasks. Given the way ubuntu boots (e.g. udev based assembly of lvm/raid etc); and the fact that upstart-native tasks are typically complete before invoking rc; and no way to tell that an upstart task has been run. It should be sufficient to consider all task jobs as complete from startpar bridge point of view, and thus not block rc, waiting to execute a task job.
Martin Pitt (pitti) wrote : | # |
Steve Langasek (vorlon) wrote : | # |
On Fri, May 30, 2014 at 08:19:15AM -0000, Martin Pitt wrote:
> The way I understand it the really clean way to implement this would be to
> tell apart the case "this task job never ran" from "it ran at least once,
> but is stopped". This could either be by a different status message (like
> "stop/waiting" vs. "finished/waiting", or similar), or creating a stamp
> file in /run for every ran task. Both of which would be upstart changes.
> This also introduces the assumption that upstart's tasks will always run
> before startpar so that the init.d dependencies will always be satisfied
> for tasks. That's not quite correct and sounds like introducing race
> conditions.
In practice, *everything* in /etc/rcS.d is supposed to be redundant with
upstart jobs in the upstart case, and the impact of a race condition - as
opposed to hang - should be nil.
If we have task jobs that map to scripts in /etc/rc2.d, I think we should
take a close look at this. However, for the "unofficial upstart jobs" case,
I don't think we should put the effort into ensuring there are no race
conditions when the user has both created an upstart task job that conflicts
with an init script *and* has another init script depending on that first
task. Clearly we would want to address this if we were sticking with
upstart longer-term, but I don't think it's relevant on a transitional
basis.
Steve Langasek (vorlon) wrote : | # |
> --- startpar/makeboot.c 2012-06-27 23:00:45 +0000
> +++ startpar/makeboot.c 2014-05-30 07:54:58 +0000
> @@ -451,6 +451,13 @@
> t->name);
> }
> ret = system(command);
> + if (WEXITSTATUS(ret) != 0) {
> + free(command);
> + asprintf(&command,
> + "grep -q '^task$' %s",
> + path);
> + ret = system(command);
> + }
> if (WEXITSTATUS(ret) == 0) {
> nodevec[count] = t;
> finish_task(t);
>
This appears to give task jobs a free pass both when checking if they're
started, but also when checking if they're stopped. The reason for making
this change is because we can't detect if a task has run and already stopped
again; but if it's currently running we can certainly detect that. Should
we do so, to avoid letting an init script be called on shutdown while a task
is still running?
I had a look at the code around timeouts in startpar, and don't think it's
worth trying to hook the task handling up to it for what should be a
short-term solution.
- 209. By Dimitri John Ledkov
-
Give tasks a free-pass on boot only.
Martin Pitt (pitti) wrote : | # |
This updated version looks ok to me as per the discussion above. I'd still like the ^task$ to be extended to allow whitespace (as I suggested in my previous inline diff comments, but they might have drowned). Also, I think this should revert http://
I built this branch with CONCURRENCY back to "makefile" so that this has any effect. But now my VM hangs at early shutdown, and also I don't see anything at all during boot (even without "quiet splash"), so something is still wrong here. In which environment did you try this?
Thanks!
- 210. By Dimitri John Ledkov
-
Improve task grep pattern.
- 211. By Dimitri John Ledkov
-
Re-enable startpar.
Martin Pitt (pitti) wrote : | # |
Setting to work in progress for now, to get it out of the sponsoring queue. However, we might just bury this completely, as we discussed to not support startpar after all? It'd be more trouble than it's worth IMHO. So please feel free to delete this.
Martin Pitt (pitti) wrote : | # |
I think it's safe to say that we won't pursue this direction. We disabled startpar support in sysvinit and removed startpar from the archive.
Unmerged revisions
- 211. By Dimitri John Ledkov
-
Re-enable startpar.
- 210. By Dimitri John Ledkov
-
Improve task grep pattern.
- 209. By Dimitri John Ledkov
-
Give tasks a free-pass on boot only.
- 208. By Dimitri John Ledkov
-
Add 'task' knowledge to startpar.
Preview Diff
1 | === modified file '.pc/applied-patches' |
2 | --- .pc/applied-patches 2013-05-17 20:53:22 +0000 |
3 | +++ .pc/applied-patches 2014-06-05 13:25:18 +0000 |
4 | @@ -23,3 +23,4 @@ |
5 | 93_run_initctl.patch |
6 | 94_kfreebsd_xterm.patch |
7 | upstart_support.patch |
8 | +startpar-upstart-tasks.patch |
9 | |
10 | === added directory '.pc/startpar-upstart-tasks.patch' |
11 | === added directory '.pc/startpar-upstart-tasks.patch/startpar' |
12 | === added file '.pc/startpar-upstart-tasks.patch/startpar/makeboot.c' |
13 | --- .pc/startpar-upstart-tasks.patch/startpar/makeboot.c 1970-01-01 00:00:00 +0000 |
14 | +++ .pc/startpar-upstart-tasks.patch/startpar/makeboot.c 2014-06-05 13:25:18 +0000 |
15 | @@ -0,0 +1,688 @@ |
16 | +/* |
17 | + * very very simple makefile parser |
18 | + * |
19 | + * Copyright (c) 2003 SuSE Linux AG |
20 | + * |
21 | + * This program is free software; you can redistribute it and/or modify |
22 | + * it under the terms of the GNU General Public License as published by |
23 | + * the Free Software Foundation; either version 2, or (at your option) |
24 | + * any later version. |
25 | + * |
26 | + * This program is distributed in the hope that it will be useful, |
27 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
28 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
29 | + * GNU General Public License for more details. |
30 | + * |
31 | + * You should have received a copy of the GNU General Public License |
32 | + * along with this program (see the file COPYING); if not, write to the |
33 | + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
34 | + * MA 02110-1301, USA. |
35 | + * |
36 | + */ |
37 | + |
38 | +#include <stdio.h> |
39 | +#include <stddef.h> |
40 | +#include <string.h> |
41 | +#include <malloc.h> |
42 | +#include <ctype.h> |
43 | +#include <stdlib.h> |
44 | +#include <dirent.h> |
45 | +#include <unistd.h> |
46 | +#include <stdarg.h> |
47 | +#include <errno.h> |
48 | +#include <limits.h> |
49 | +#include <sys/socket.h> |
50 | +#include <sys/un.h> |
51 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
52 | +# include <sys/types.h> |
53 | +# include <sys/stat.h> |
54 | +# include <fcntl.h> |
55 | +#ifndef POSIX_FADV_SEQUENTIAL |
56 | +#define posix_fadvise(fd, off, len, adv) (-1) |
57 | +#endif |
58 | +#ifndef O_DIRECT |
59 | +#define O_DIRECT 0 |
60 | +#endif |
61 | +static int o_flags = O_RDONLY; |
62 | +#endif |
63 | +#ifdef USE_BLOGD |
64 | +# include <libblogger.h> |
65 | +#endif |
66 | +#include "makeboot.h" |
67 | + |
68 | +#define DBUS_ADDRESS_UPSTART "@/com/ubuntu/upstart" |
69 | + |
70 | +int tree_entries = 0; |
71 | +struct makenode *tree_list = NULL; |
72 | + |
73 | +/* |
74 | + * search for the node with the given name |
75 | + * returns the node pointer or NULL if not found. |
76 | + * |
77 | + * FIXME: we should use hash for the effective search. |
78 | + */ |
79 | +struct makenode *lookup_target(const char *name) |
80 | +{ |
81 | + struct makenode *t; |
82 | + |
83 | + for (t = tree_list; t; t = t->next) |
84 | + if (! strcmp(t->name, name)) |
85 | + return t; |
86 | + return NULL; |
87 | +} |
88 | + |
89 | +/* |
90 | + * look for the node with the given name. if not exist, |
91 | + * create a new one and append to the node list. |
92 | + */ |
93 | +static struct makenode *add_target(const char *name) |
94 | +{ |
95 | + struct makenode *__restrict node; |
96 | + struct makenode *prev, *t; |
97 | + |
98 | + node = lookup_target(name); |
99 | + if (node) |
100 | + return node; |
101 | + if (posix_memalign((void*)&node, sizeof(void*), alignof(struct makenode)+strsize(name)) < 0) { |
102 | + fprintf(stderr, "Can't malloc: %s\n", strerror(errno)); |
103 | + exit(1); |
104 | + } |
105 | + memset(node, 0, alignof(struct makenode)+strsize(name)); |
106 | + node->name = ((char*)node)+alignof(struct makenode); |
107 | + strcpy(node->name, name); |
108 | + |
109 | + /* append to the list in alphabetical order */ |
110 | + prev = NULL; |
111 | + for (t = tree_list; t; prev = t, t = t->next) |
112 | + if (strcmp(node->name, t->name) < 0) |
113 | + break; |
114 | + if (prev) |
115 | + prev->next = node; |
116 | + else |
117 | + tree_list = node; |
118 | + node->next = t; |
119 | + tree_entries++; |
120 | + return node; |
121 | +} |
122 | + |
123 | +/* |
124 | + * Set and propagate importance of a node to all depencies of this node |
125 | + */ |
126 | +static void add_importance(struct makenode *node, int importance) |
127 | +{ |
128 | + struct makelist *s = node->depend; |
129 | + |
130 | + node->importance += importance; |
131 | + for (s = node->depend; s; s = s->next) |
132 | + add_importance(s->node, importance); |
133 | +} |
134 | + |
135 | +/* |
136 | + * create a dependecy/selection node |
137 | + */ |
138 | +static struct makelist *new_list(struct makenode *node, struct makelist *next) |
139 | +{ |
140 | + struct makelist *x; |
141 | + |
142 | + x = xcalloc(1, sizeof(*x)); |
143 | + x->node = node; |
144 | + x->next = next; |
145 | + return x; |
146 | +} |
147 | + |
148 | +/* |
149 | + * check whether the given target would create an infinte loop |
150 | + */ |
151 | +static int loop; |
152 | +static int check_loop(struct makenode *dep, struct makenode *src) |
153 | +{ |
154 | + struct makelist *s; |
155 | + for (s = dep->depend; s; s = s->next) { |
156 | + if (s->node == src) { |
157 | + fprintf(stderr, "loop exists %s in %s!\n", dep->name, src->name); |
158 | + return 1; |
159 | + } |
160 | + if (loop++ > 99999) { |
161 | + fprintf(stderr, "too many loops! (loop=%d, dep->name=%s, src->name=%s)\n", |
162 | + loop, dep->name, src->name); |
163 | + return 1; |
164 | + } |
165 | + if (check_loop(s->node, src)) |
166 | + return 1; |
167 | + } |
168 | + return 0; |
169 | +} |
170 | + |
171 | +/* |
172 | + * add to the dependecy and selection lists |
173 | + */ |
174 | +static void add_depend(struct makenode *node, const char *dst) |
175 | +{ |
176 | + struct makenode *dep; |
177 | + |
178 | + dep = add_target(dst); |
179 | + loop = 0; |
180 | + if (check_loop(dep, node)) |
181 | + return; |
182 | + dep->select = new_list(node, dep->select); |
183 | + dep->num_sels++; |
184 | + node->depend = new_list(dep, node->depend); |
185 | + node->num_deps++; |
186 | +} |
187 | + |
188 | +/* |
189 | + * mark the selected service as an interactive task |
190 | + * that should run solely |
191 | + */ |
192 | +static void mark_interactive(const char *name) |
193 | +{ |
194 | + struct makenode *node = lookup_target(name); |
195 | + if (node) |
196 | + node->interactive = 1; |
197 | +} |
198 | + |
199 | + |
200 | +#define DELIMITER " \t\r\n" |
201 | + |
202 | +/* |
203 | + * parse (pseudo) makefile |
204 | + * |
205 | + * it may have only the following form: |
206 | + * |
207 | + * TARGETS = xxx ... |
208 | + * INTERACTIVE = yyy ... |
209 | + * aaa: |
210 | + * bbb: xxx ddd ... |
211 | + * |
212 | + * other lines are ignored. |
213 | + */ |
214 | +void parse_makefile(const char *path) |
215 | +{ |
216 | + FILE *fp; |
217 | + char buf[LINE_MAX]; /* FIXME: is this enough big? */ |
218 | + char *s, *strp, *p; |
219 | + struct makenode *node; |
220 | + |
221 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
222 | + int fd; |
223 | + |
224 | + if (getuid() == (uid_t)0) |
225 | + o_flags |= O_NOATIME; |
226 | + if ((fd = open(path, o_flags)) < 0) { |
227 | + fprintf(stderr, "Can't open %s: %s\n", path, strerror(errno)); |
228 | + exit(1); |
229 | + } |
230 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED); |
231 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); |
232 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE); |
233 | + |
234 | + if ((fp = fdopen(fd, "r")) == NULL) |
235 | +#else |
236 | + if ((fp = fopen(path, "r")) == NULL) |
237 | +#endif |
238 | + { |
239 | + fprintf(stderr, "Can't open %s: %s\n", path, strerror(errno)); |
240 | + exit(1); |
241 | + } |
242 | + |
243 | + while (fgets(buf, sizeof(buf), fp)) { |
244 | + for (s = buf; *s && isspace(*s); s++) |
245 | + ; |
246 | + if (! *s || *s == '#') |
247 | + continue; |
248 | + if (! strncmp(s, "TARGETS =", 9)) { |
249 | + s += 9; |
250 | + strp = s; |
251 | + while ((s = strsep(&strp, DELIMITER))) { |
252 | + if (! *s) |
253 | + continue; |
254 | + add_target(s); |
255 | + } |
256 | + } else if (! strncmp(s, "INTERACTIVE =", 13)) { |
257 | + s += 13; |
258 | + strp = s; |
259 | + while ((s = strsep(&strp, DELIMITER))) { |
260 | + if (! *s) |
261 | + continue; |
262 | + mark_interactive(s); |
263 | + } |
264 | + } else { |
265 | + p = strchr(s, ':'); |
266 | + if (! p) |
267 | + continue; |
268 | + *p = 0; |
269 | + node = add_target(s); |
270 | + strp = p + 1; |
271 | + while ((s = strsep(&strp, DELIMITER))) { |
272 | + if (! *s) |
273 | + continue; |
274 | + add_depend(node, s); |
275 | + } |
276 | + } |
277 | + } |
278 | + |
279 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
280 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED); |
281 | +#endif |
282 | + |
283 | + fclose(fp); |
284 | + |
285 | + for (node = tree_list; node; node = node->next) { |
286 | + int importance = 0; |
287 | + |
288 | + if (! strcmp(node->name, "xdm") |
289 | + || ! strncmp(node->name, "gdm", 3) |
290 | + || ! strncmp(node->name, "kdm", 3) |
291 | + || ! strcmp(node->name, "boot.udev") |
292 | + || ! strcmp(node->name, "udev")) |
293 | + importance = 100; |
294 | + |
295 | + if (! strcmp(node->name, "sshd")) |
296 | + importance = 2000; |
297 | + |
298 | + if (! strncmp(node->name, "early", 5)) |
299 | + importance = 8000; |
300 | + |
301 | + if (importance) |
302 | + add_importance(node, importance); |
303 | + } |
304 | +} |
305 | + |
306 | +/* |
307 | + * filter out the list targets |
308 | + */ |
309 | + |
310 | +static int filter_prefix; |
311 | +static int dirfilter(const struct dirent *d) |
312 | +{ |
313 | + return *d->d_name == filter_prefix && |
314 | + strlen(d->d_name) >= 4; /* to be sure */ |
315 | +} |
316 | + |
317 | +static void filter_files(const char *dir, int prefix, int inverse) |
318 | +{ |
319 | + char path[64]; |
320 | + int i, ndirs; |
321 | + static struct dirent **dirlist; |
322 | + struct makenode *t, *next; |
323 | + |
324 | + filter_prefix = prefix; |
325 | +#ifdef SUSE /* SuSE */ |
326 | + snprintf(path, sizeof(path), "/etc/init.d/%s.d", dir); |
327 | +#else /* Debian */ |
328 | + snprintf(path, sizeof(path), "/etc/%s.d", dir); |
329 | +#endif |
330 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
331 | + if ((i = open(path, o_flags|O_DIRECTORY|O_LARGEFILE)) >= 0) { |
332 | + (void)posix_fadvise(i, 0, 0, POSIX_FADV_SEQUENTIAL); |
333 | + (void)posix_fadvise(i, 0, 0, POSIX_FADV_NOREUSE); |
334 | + } |
335 | +#endif |
336 | + ndirs = scandir(path, &dirlist, dirfilter, alphasort); |
337 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
338 | + if (i >= 0) { |
339 | + (void)posix_fadvise(i, 0, 0, POSIX_FADV_DONTNEED); |
340 | + close(i); |
341 | + } |
342 | +#endif |
343 | + /* mark all matching nodes */ |
344 | + if (ndirs >= 0) { |
345 | + for (i = 0; i < ndirs; i++) { |
346 | + t = lookup_target(dirlist[i]->d_name + 3); |
347 | + if (t) { |
348 | + t->status = 1; |
349 | + t->filter_prefix = filter_prefix; |
350 | + if (asprintf(&t->arg0, "%s/%s", path, dirlist[i]->d_name) < 0) |
351 | + t->arg0 = (char*)0; |
352 | + } |
353 | + free(dirlist[i]); |
354 | + } |
355 | + free(dirlist); |
356 | + } |
357 | + /* deselect non-matching nodes */ |
358 | + for (t = tree_list; t; t = next) { |
359 | + next = t->next; |
360 | + if ((! t->status && ! inverse) || (t->status && inverse)) { |
361 | + /* remove from the list */ |
362 | + struct makelist *x, *nx; |
363 | + struct makenode *p; |
364 | + for (x = t->select; x; x = nx) { |
365 | + nx = x->next; |
366 | + x->node->num_deps--; |
367 | + free(x); |
368 | + } |
369 | + for (x = t->depend; x; x = nx) { |
370 | + nx = x->next; |
371 | + x->node->num_sels--; |
372 | + free(x); |
373 | + } |
374 | + if (t == tree_list) |
375 | + tree_list = next; |
376 | + else { |
377 | + for (p = tree_list; p->next != t; p = p->next) |
378 | + ; |
379 | + p->next = next; |
380 | + } |
381 | + /* don't free the node instance itself - it may be selected |
382 | + * by others |
383 | + */ |
384 | + tree_entries--; |
385 | + continue; |
386 | + } |
387 | + t->status = 0; |
388 | + } |
389 | +} |
390 | + |
391 | +/* |
392 | + * mark the unnecessary services as finished. |
393 | + * |
394 | + * action is either boot, start or stop. |
395 | + * prev and run are the previous and the next runlevel. |
396 | + */ |
397 | +void check_run_files(const char *action, const char *prev, const char *run) |
398 | +{ |
399 | + char buf[4] = "rc0"; |
400 | + if (! strcmp(action, "boot")) { |
401 | +#ifdef SUSE /* SuSE */ |
402 | + filter_files("boot", 'S', 0); |
403 | + } else if (! strcmp(action, "halt")) { |
404 | + filter_files("boot", 'K', 0); |
405 | + } else if (! strcmp(action, "start")) { |
406 | + buf[2] = *prev; |
407 | + filter_files(buf, 'K', 1); |
408 | + buf[2] = *run; |
409 | + filter_files(buf, 'S', 0); |
410 | + } else { |
411 | + buf[2] = *prev; |
412 | + filter_files(buf, 'K', 0); |
413 | + buf[2] = *run; |
414 | + filter_files(buf, 'S', 1); |
415 | +#else /* Debian */ |
416 | + filter_files("rcS", 'S', 0); |
417 | + } else if (! strcmp(action, "start")) { |
418 | + buf[2] = *prev; |
419 | + filter_files(buf, 'S', 1); |
420 | + buf[2] = *run; |
421 | + filter_files(buf, 'S', 0); |
422 | + } else { |
423 | + buf[2] = *prev; |
424 | + filter_files(buf, 'K', 1); |
425 | + buf[2] = *run; |
426 | + filter_files(buf, 'K', 0); |
427 | +#endif |
428 | + } |
429 | +} |
430 | + |
431 | +#ifdef __linux__ |
432 | +/* |
433 | + * mark upstart services as finished. |
434 | + * |
435 | + * action is either boot, start or stop. |
436 | + */ |
437 | +int check_upstart_jobs(const char *action, const struct makenode **nodevec) |
438 | +{ |
439 | + struct makenode *t; |
440 | + int count = 0; |
441 | + |
442 | + if (!init_is_upstart()) |
443 | + return 0; |
444 | + |
445 | + for (t = tree_list; t; t = t->next) |
446 | + { |
447 | + char path[131]; /* three bytes longer than the max allowed init script name... */ |
448 | + struct stat job; |
449 | + |
450 | + snprintf(path, sizeof(path), "/etc/init/%s.conf", t->name); |
451 | + if (!stat(path,&job)) { |
452 | + int ret; |
453 | + char *command; |
454 | + |
455 | + t->upstart = 1; |
456 | + /* Upstart jobs are never interactive in this sense */ |
457 | + t->interactive = 0; |
458 | + if (!strcmp(action,"start") || !strcmp(action,"boot")) |
459 | + { |
460 | + asprintf(&command, |
461 | + "/sbin/initctl status %s | grep -q start/running", |
462 | + t->name); |
463 | + } else { |
464 | + asprintf(&command, |
465 | + "/sbin/initctl status %s | grep -q stop/waiting", |
466 | + t->name); |
467 | + } |
468 | + ret = system(command); |
469 | + if (WEXITSTATUS(ret) == 0) { |
470 | + nodevec[count] = t; |
471 | + finish_task(t); |
472 | + count++; |
473 | + } |
474 | + free(command); |
475 | + } |
476 | + } |
477 | + return count; |
478 | +} |
479 | + |
480 | +/* |
481 | + * return true if PID 1 is upstart, false otherwise. |
482 | + */ |
483 | +boolean init_is_upstart(void) { |
484 | + static int is_upstart = -1; |
485 | + int fd; |
486 | + struct sockaddr_un saddr; |
487 | + socklen_t addrlen; |
488 | + struct ucred ucred; |
489 | + socklen_t slen; |
490 | + |
491 | + if (is_upstart != -1) |
492 | + return is_upstart; |
493 | + |
494 | + fd = socket(AF_LOCAL, SOCK_STREAM, 0); |
495 | + /* Weird, but we'll just have to assume no upstart. */ |
496 | + if (fd < 0) |
497 | + goto fail; |
498 | + |
499 | + saddr.sun_family = AF_LOCAL; |
500 | + strcpy(saddr.sun_path, DBUS_ADDRESS_UPSTART); |
501 | + addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path); |
502 | + |
503 | + /* translate leading '@' to abstract namespace */ |
504 | + if (saddr.sun_path[0] == '@') |
505 | + saddr.sun_path[0] = '\0'; |
506 | + |
507 | + if (connect(fd, (struct sockaddr *)&saddr, addrlen) < 0) |
508 | + goto fail; |
509 | + |
510 | + /* Make sure it's really upstart and not something lying to us! */ |
511 | + slen = sizeof(ucred); |
512 | + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &slen) < 0) |
513 | + goto fail; |
514 | + |
515 | + close(fd); |
516 | + if (ucred.uid == 0) |
517 | + is_upstart = 1; |
518 | + else |
519 | + is_upstart = 0; |
520 | + |
521 | + return is_upstart; |
522 | + |
523 | +fail: |
524 | + if (fd >= 0) |
525 | + close(fd); |
526 | + is_upstart = 0; |
527 | + return is_upstart; |
528 | +} |
529 | +#endif |
530 | + |
531 | +/* |
532 | + * call blogd |
533 | + */ |
534 | +#ifndef USE_BLOGD |
535 | +# define bootlog(arg...) |
536 | +# define closeblog() |
537 | +#endif |
538 | + |
539 | +/* |
540 | + * pick up the next running task |
541 | + * return NULL if not found. |
542 | + */ |
543 | +struct makenode *pickup_task(void) |
544 | +{ |
545 | + struct makenode *node, *best = (struct makenode*)0; |
546 | + |
547 | + for (node = tree_list; node; node = node->next) { |
548 | + if (node->status != T_READY) |
549 | + continue; |
550 | + if (node->num_deps > 0) |
551 | + continue; |
552 | + if (!best || (node->importance > best->importance)) |
553 | + best = node; |
554 | + } |
555 | + if (best) { |
556 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
557 | + char path[128]; |
558 | + int fd; |
559 | + snprintf(path, sizeof(path), "/etc/init.d/%s", best->name); |
560 | + if ((fd = open(path, o_flags|O_DIRECT)) >= 0) { |
561 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_WILLNEED); |
562 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL); |
563 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_NOREUSE); |
564 | + close(fd); |
565 | + } |
566 | +#endif |
567 | + bootlog(B_NOTICE, "service %s %s", best->name, (best->filter_prefix == 'K') ? "stop" : "start"); |
568 | + best->status = T_RUNNING; |
569 | + } |
570 | + return best; |
571 | +} |
572 | + |
573 | +/* |
574 | + * finish the running task |
575 | + */ |
576 | +void finish_task(struct makenode *node) |
577 | +{ |
578 | + struct makelist *n; |
579 | + |
580 | + if (! node) |
581 | + return; |
582 | + /* Ignore any further upstart signals for this job */ |
583 | + node->upstart = 0; |
584 | + for (n = node->select; n; n = n->next) |
585 | + n->node->num_deps--; |
586 | +#if defined _XOPEN_SOURCE && (_XOPEN_SOURCE - 0) >= 600 |
587 | + { |
588 | + char path[128]; |
589 | + int fd; |
590 | + snprintf(path, sizeof(path), "/etc/init.d/%s", node->name); |
591 | + if ((fd = open(path, o_flags|O_DIRECT)) >= 0) { |
592 | + (void)posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED); |
593 | + close(fd); |
594 | + } |
595 | + } |
596 | +#endif |
597 | + node->status = T_FINISHED; |
598 | + bootlog(B_NOTICE, "service %s done", node->name); |
599 | +} |
600 | + |
601 | + |
602 | +/* |
603 | + * Print out the status that bash can run eval. |
604 | + * The following things will be printed: |
605 | + * failed services, skipped services and the current progress value. |
606 | + */ |
607 | +void print_run_result(int *resvec, struct makenode **nodevec, const char *action) |
608 | +{ |
609 | + int i, r, stop = (! strcmp(action, "stop")); |
610 | + |
611 | + printf("failed_service=\""); |
612 | + i = r = 0; |
613 | + for (i = 0; i < tree_entries; i++) { |
614 | +#if DEBUG |
615 | + if (resvec[i] == 255) { |
616 | + fprintf(stderr, "ERROR: forgotten process??\n"); |
617 | + exit(1); |
618 | + } |
619 | +#endif |
620 | + if (resvec[i] >= 1 && resvec[i] <= 4) { |
621 | + if (r) |
622 | + printf(" "); |
623 | + printf("%s", nodevec[i]->name); |
624 | + r++; |
625 | + } else if (!stop && resvec[i] == 7) { |
626 | + if (r) |
627 | + printf(" "); |
628 | + printf("%s", nodevec[i]->name); |
629 | + r++; |
630 | + } |
631 | + } |
632 | + printf("\"\n"); |
633 | + printf("skipped_service_not_installed=\""); |
634 | + i = r = 0; |
635 | + for (i = 0; i < tree_entries; i++) { |
636 | + if (resvec[i] == 5) { |
637 | + if (r) |
638 | + printf(" "); |
639 | + printf("%s", nodevec[i]->name); |
640 | + r++; |
641 | + } |
642 | + } |
643 | + printf("\"\n"); |
644 | + printf("skipped_service_not_configured=\""); |
645 | + i = r = 0; |
646 | + for (i = 0; i < tree_entries; i++) { |
647 | + if (resvec[i] == 6) { |
648 | + if (r) |
649 | + printf(" "); |
650 | + printf("%s", nodevec[i]->name); |
651 | + r++; |
652 | + } |
653 | + } |
654 | + printf("\"\n"); |
655 | +} |
656 | + |
657 | +#if DEBUG |
658 | +void dump_status(void) |
659 | +{ |
660 | + struct makenode *node; |
661 | + |
662 | + for (node = tree_list; node; node = node->next) |
663 | + fprintf(stderr, "XXX %s: status = %d, dep = %d, int = %d, imp = %d\n", |
664 | + node->name, node->status, node->num_deps, node->interactive, node->importance); |
665 | +} |
666 | +#endif |
667 | + |
668 | +#ifdef TEST |
669 | +void *xcalloc(size_t nmemb, size_t size) |
670 | +{ |
671 | + void *r; |
672 | + if ((r = (void *)calloc(nmemb, size)) == 0) { |
673 | + fprintf(stderr, "calloc: out of memory\n"); |
674 | + exit(1); |
675 | + } |
676 | + return r; |
677 | +} |
678 | + |
679 | +int main(int argc, char **argv) |
680 | +{ |
681 | + struct makenode *nodevec; |
682 | + char makefile[64]; |
683 | + |
684 | + if (argc != 4) { |
685 | + fprintf(stderr, "usage: makeboot <action> [<prev> <run>]\n"); |
686 | + goto out; |
687 | + } |
688 | + |
689 | + snprintf(makefile, sizeof(makefile), "depend.%s", argv[1]); |
690 | + parse_makefile(makefile); |
691 | + |
692 | + fprintf(stderr, "check_run_files(%s, %s, %s)\n", argv[1], argv[2], |
693 | + argv[3]); |
694 | + check_run_files(argv[1], argv[2], argv[3]); |
695 | +out: |
696 | + while ((nodevec = pickup_task())) { |
697 | + fprintf(stdout, "%s (%s)\n", nodevec->name, nodevec->arg0); |
698 | + finish_task(nodevec); |
699 | + } |
700 | + |
701 | + return 0; |
702 | +} |
703 | +#endif |
704 | |
705 | === modified file 'debian/changelog' |
706 | --- debian/changelog 2014-05-30 11:44:23 +0000 |
707 | +++ debian/changelog 2014-06-05 13:25:18 +0000 |
708 | @@ -1,3 +1,4 @@ |
709 | +<<<<<<< TREE |
710 | sysvinit (2.88dsf-41ubuntu16) utopic; urgency=medium |
711 | |
712 | * Move sourcing of lsb/init-functions above the sourcing of helpers which we |
713 | @@ -6,6 +7,15 @@ |
714 | |
715 | -- Martin Pitt <martin.pitt@ubuntu.com> Fri, 30 May 2014 11:44:23 +0200 |
716 | |
717 | +======= |
718 | +sysvinit (2.88dsf-41ubuntu16) UNRELEASED; urgency=medium |
719 | + |
720 | + * Add 'task' knowledge to startpar. |
721 | + * Re-enable startpar. |
722 | + |
723 | + -- Dimitri John Ledkov <xnox@ubuntu.com> Fri, 30 May 2014 08:24:42 +0100 |
724 | + |
725 | +>>>>>>> MERGE-SOURCE |
726 | sysvinit (2.88dsf-41ubuntu15) utopic; urgency=medium |
727 | |
728 | * Disable startpar. It wreaks havoc with "task" upstart jobs as init.d |
729 | |
730 | === modified file 'debian/patches/series' |
731 | --- debian/patches/series 2013-05-17 06:03:10 +0000 |
732 | +++ debian/patches/series 2014-06-05 13:25:18 +0000 |
733 | @@ -23,3 +23,4 @@ |
734 | 93_run_initctl.patch |
735 | 94_kfreebsd_xterm.patch |
736 | upstart_support.patch |
737 | +startpar-upstart-tasks.patch |
738 | |
739 | === added file 'debian/patches/startpar-upstart-tasks.patch' |
740 | --- debian/patches/startpar-upstart-tasks.patch 1970-01-01 00:00:00 +0000 |
741 | +++ debian/patches/startpar-upstart-tasks.patch 2014-06-05 13:25:18 +0000 |
742 | @@ -0,0 +1,28 @@ |
743 | +Description: Add 'task' knowledge to startpar. |
744 | +Author: Dimitri John Ledkov <xnox@ubuntu.com> |
745 | + |
746 | +--- a/startpar/makeboot.c |
747 | ++++ b/startpar/makeboot.c |
748 | +@@ -445,12 +445,21 @@ |
749 | + asprintf(&command, |
750 | + "/sbin/initctl status %s | grep -q start/running", |
751 | + t->name); |
752 | ++ ret = system(command); |
753 | ++ if (WEXITSTATUS(ret) != 0) { |
754 | ++ free(command); |
755 | ++ asprintf(&command, |
756 | ++ "grep -q '^[ \t]*task[ \t]*$' %s", |
757 | ++ path); |
758 | ++ ret = system(command); |
759 | ++ } |
760 | ++ |
761 | + } else { |
762 | + asprintf(&command, |
763 | + "/sbin/initctl status %s | grep -q stop/waiting", |
764 | + t->name); |
765 | ++ ret = system(command); |
766 | + } |
767 | +- ret = system(command); |
768 | + if (WEXITSTATUS(ret) == 0) { |
769 | + nodevec[count] = t; |
770 | + finish_task(t); |
771 | |
772 | === modified file 'debian/src/sysv-rc/etc/init.d/rc' |
773 | --- debian/src/sysv-rc/etc/init.d/rc 2014-05-29 10:15:57 +0000 |
774 | +++ debian/src/sysv-rc/etc/init.d/rc 2014-06-05 13:25:18 +0000 |
775 | @@ -69,9 +69,7 @@ |
776 | # insserv package to be enabled. Boot concurrency also requires |
777 | # startpar to be installed. |
778 | # |
779 | -#CONCURRENCY=makefile |
780 | -# disable startpar, incompatible with "task" upstart jobs |
781 | -CONCURRENCY=none |
782 | +CONCURRENCY=makefile |
783 | test -s /etc/init.d/.depend.boot || CONCURRENCY="none" |
784 | test -s /etc/init.d/.depend.start || CONCURRENCY="none" |
785 | test -s /etc/init.d/.depend.stop || CONCURRENCY="none" |
786 | |
787 | === modified file 'startpar/makeboot.c' |
788 | --- startpar/makeboot.c 2012-06-27 23:00:45 +0000 |
789 | +++ startpar/makeboot.c 2014-06-05 13:25:18 +0000 |
790 | @@ -445,12 +445,21 @@ |
791 | asprintf(&command, |
792 | "/sbin/initctl status %s | grep -q start/running", |
793 | t->name); |
794 | + ret = system(command); |
795 | + if (WEXITSTATUS(ret) != 0) { |
796 | + free(command); |
797 | + asprintf(&command, |
798 | + "grep -q '^[ \t]*task[ \t]*$' %s", |
799 | + path); |
800 | + ret = system(command); |
801 | + } |
802 | + |
803 | } else { |
804 | asprintf(&command, |
805 | "/sbin/initctl status %s | grep -q stop/waiting", |
806 | t->name); |
807 | + ret = system(command); |
808 | } |
809 | - ret = system(command); |
810 | if (WEXITSTATUS(ret) == 0) { |
811 | nodevec[count] = t; |
812 | finish_task(t); |
The way I understand it the really clean way to implement this would be to tell apart the case "this task job never ran" from "it ran at least once, but is stopped". This could either be by a different status message (like "stop/waiting" vs. "finished/waiting", or similar), or creating a stamp file in /run for every ran task. Both of which would be upstart changes.
This also introduces the assumption that upstart's tasks will always run before startpar so that the init.d dependencies will always be satisfied for tasks. That's not quite correct and sounds like introducing race conditions.
That said, if this is supposed to be a better workaround instead of an actual solution, it seems ok to me for the time being. It's at least better than running all init.d scripts.
Thanks!