]>
Commit | Line | Data |
---|---|---|
60918275 LP |
1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ |
2 | ||
3 | #include <assert.h> | |
4 | #include <errno.h> | |
87d1515d | 5 | #include <string.h> |
9152c765 LP |
6 | #include <sys/epoll.h> |
7 | #include <signal.h> | |
8 | #include <sys/signalfd.h> | |
9 | #include <sys/wait.h> | |
10 | #include <unistd.h> | |
11 | #include <sys/poll.h> | |
60918275 LP |
12 | |
13 | #include "manager.h" | |
14 | #include "hashmap.h" | |
15 | #include "macro.h" | |
16 | #include "strv.h" | |
16354eff | 17 | #include "log.h" |
2a987ee8 | 18 | #include "util.h" |
60918275 LP |
19 | |
20 | Manager* manager_new(void) { | |
21 | Manager *m; | |
9152c765 LP |
22 | sigset_t mask; |
23 | struct epoll_event ev; | |
60918275 LP |
24 | |
25 | if (!(m = new0(Manager, 1))) | |
26 | return NULL; | |
27 | ||
acbb0225 | 28 | m->signal_watch.fd = m->epoll_fd = -1; |
9152c765 | 29 | |
87f0e418 | 30 | if (!(m->units = hashmap_new(string_hash_func, string_compare_func))) |
60918275 LP |
31 | goto fail; |
32 | ||
33 | if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) | |
34 | goto fail; | |
35 | ||
e5b5ae50 | 36 | if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) |
60918275 LP |
37 | goto fail; |
38 | ||
9152c765 LP |
39 | if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func))) |
40 | goto fail; | |
41 | ||
42 | if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) | |
43 | goto fail; | |
44 | ||
2a987ee8 LP |
45 | assert_se(reset_all_signal_handlers() == 0); |
46 | ||
9152c765 LP |
47 | assert_se(sigemptyset(&mask) == 0); |
48 | assert_se(sigaddset(&mask, SIGCHLD) == 0); | |
b9cd2ec1 | 49 | assert_se(sigaddset(&mask, SIGINT) == 0); |
6632c602 LP |
50 | assert_se(sigaddset(&mask, SIGTERM) == 0); |
51 | assert_se(sigaddset(&mask, SIGWINCH) == 0); | |
52 | assert_se(sigaddset(&mask, SIGHUP) == 0); | |
9152c765 LP |
53 | assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0); |
54 | ||
acbb0225 LP |
55 | m->signal_watch.type = WATCH_SIGNAL_FD; |
56 | if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) | |
9152c765 LP |
57 | goto fail; |
58 | ||
59 | zero(ev); | |
60 | ev.events = EPOLLIN; | |
acbb0225 | 61 | ev.data.ptr = &m->signal_watch; |
9152c765 | 62 | |
acbb0225 | 63 | if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0) |
9152c765 LP |
64 | goto fail; |
65 | ||
60918275 LP |
66 | return m; |
67 | ||
68 | fail: | |
69 | manager_free(m); | |
70 | return NULL; | |
71 | } | |
72 | ||
73 | void manager_free(Manager *m) { | |
87f0e418 | 74 | Unit *u; |
e5b5ae50 | 75 | Job *j; |
60918275 LP |
76 | |
77 | assert(m); | |
78 | ||
87f0e418 | 79 | while ((j = hashmap_first(m->transaction_jobs))) |
e5b5ae50 LP |
80 | job_free(j); |
81 | ||
87f0e418 LP |
82 | while ((u = hashmap_first(m->units))) |
83 | unit_free(u); | |
84 | ||
85 | hashmap_free(m->units); | |
60918275 | 86 | hashmap_free(m->jobs); |
e5b5ae50 | 87 | hashmap_free(m->transaction_jobs); |
9152c765 LP |
88 | hashmap_free(m->watch_pids); |
89 | ||
90 | if (m->epoll_fd >= 0) | |
91 | close_nointr(m->epoll_fd); | |
acbb0225 LP |
92 | if (m->signal_watch.fd >= 0) |
93 | close_nointr(m->signal_watch.fd); | |
60918275 LP |
94 | |
95 | free(m); | |
96 | } | |
97 | ||
302d0040 LP |
98 | static void transaction_delete_job(Manager *m, Job *j) { |
99 | assert(m); | |
100 | assert(j); | |
101 | ||
1ffba6fe LP |
102 | /* Deletes one job from the transaction */ |
103 | ||
302d0040 LP |
104 | manager_transaction_unlink_job(m, j); |
105 | ||
ac1135be | 106 | if (!j->installed) |
302d0040 LP |
107 | job_free(j); |
108 | } | |
109 | ||
87f0e418 | 110 | static void transaction_delete_unit(Manager *m, Unit *u) { |
1ffba6fe LP |
111 | Job *j; |
112 | ||
87f0e418 | 113 | /* Deletes all jobs associated with a certain unit from the |
1ffba6fe LP |
114 | * transaction */ |
115 | ||
87f0e418 | 116 | while ((j = hashmap_get(m->transaction_jobs, u))) |
1ffba6fe LP |
117 | transaction_delete_job(m, j); |
118 | } | |
119 | ||
f04fa1d5 LP |
120 | static void transaction_clean_dependencies(Manager *m) { |
121 | Iterator i; | |
122 | Job *j; | |
123 | ||
124 | assert(m); | |
125 | ||
126 | /* Drops all dependencies of all installed jobs */ | |
127 | ||
128 | HASHMAP_FOREACH(j, m->jobs, i) { | |
129 | while (j->subject_list) | |
130 | job_dependency_free(j->subject_list); | |
131 | while (j->object_list) | |
132 | job_dependency_free(j->object_list); | |
133 | } | |
134 | ||
135 | assert(!m->transaction_anchor); | |
136 | } | |
137 | ||
11dd41ce LP |
138 | static void transaction_abort(Manager *m) { |
139 | Job *j; | |
140 | ||
141 | assert(m); | |
11dd41ce | 142 | |
e5b5ae50 | 143 | while ((j = hashmap_first(m->transaction_jobs))) |
ac1135be | 144 | if (j->installed) |
302d0040 | 145 | transaction_delete_job(m, j); |
e5b5ae50 LP |
146 | else |
147 | job_free(j); | |
148 | ||
149 | assert(hashmap_isempty(m->transaction_jobs)); | |
f04fa1d5 LP |
150 | |
151 | transaction_clean_dependencies(m); | |
e5b5ae50 LP |
152 | } |
153 | ||
154 | static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) { | |
155 | JobDependency *l; | |
156 | ||
157 | assert(m); | |
158 | ||
87f0e418 | 159 | /* A recursive sweep through the graph that marks all units |
1ffba6fe LP |
160 | * that matter to the anchor job, i.e. are directly or |
161 | * indirectly a dependency of the anchor job via paths that | |
162 | * are fully marked as mattering. */ | |
163 | ||
44d8db9e LP |
164 | if (j) |
165 | l = j->subject_list; | |
166 | else | |
167 | l = m->transaction_anchor; | |
168 | ||
169 | LIST_FOREACH(subject, l, l) { | |
e5b5ae50 LP |
170 | |
171 | /* This link does not matter */ | |
172 | if (!l->matters) | |
173 | continue; | |
174 | ||
87f0e418 | 175 | /* This unit has already been marked */ |
e5b5ae50 LP |
176 | if (l->object->generation == generation) |
177 | continue; | |
178 | ||
179 | l->object->matters_to_anchor = true; | |
180 | l->object->generation = generation; | |
181 | ||
182 | transaction_find_jobs_that_matter_to_anchor(m, l->object, generation); | |
183 | } | |
184 | } | |
185 | ||
7fad411c | 186 | static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) { |
e5b5ae50 LP |
187 | JobDependency *l, *last; |
188 | ||
189 | assert(j); | |
190 | assert(other); | |
87f0e418 | 191 | assert(j->unit == other->unit); |
ac1135be | 192 | assert(!j->installed); |
e5b5ae50 | 193 | |
1ffba6fe LP |
194 | /* Merges 'other' into 'j' and then deletes j. */ |
195 | ||
e5b5ae50 LP |
196 | j->type = t; |
197 | j->state = JOB_WAITING; | |
5cb5a6ff | 198 | j->forced = j->forced || other->forced; |
e5b5ae50 LP |
199 | |
200 | j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor; | |
201 | ||
202 | /* Patch us in as new owner of the JobDependency objects */ | |
203 | last = NULL; | |
44d8db9e | 204 | LIST_FOREACH(subject, l, other->subject_list) { |
e5b5ae50 LP |
205 | assert(l->subject == other); |
206 | l->subject = j; | |
207 | last = l; | |
208 | } | |
209 | ||
210 | /* Merge both lists */ | |
211 | if (last) { | |
212 | last->subject_next = j->subject_list; | |
213 | if (j->subject_list) | |
214 | j->subject_list->subject_prev = last; | |
215 | j->subject_list = other->subject_list; | |
216 | } | |
217 | ||
218 | /* Patch us in as new owner of the JobDependency objects */ | |
219 | last = NULL; | |
44d8db9e | 220 | LIST_FOREACH(object, l, other->object_list) { |
e5b5ae50 LP |
221 | assert(l->object == other); |
222 | l->object = j; | |
223 | last = l; | |
224 | } | |
225 | ||
226 | /* Merge both lists */ | |
227 | if (last) { | |
228 | last->object_next = j->object_list; | |
229 | if (j->object_list) | |
230 | j->object_list->object_prev = last; | |
231 | j->object_list = other->object_list; | |
232 | } | |
233 | ||
e5b5ae50 LP |
234 | /* Kill the other job */ |
235 | other->subject_list = NULL; | |
236 | other->object_list = NULL; | |
302d0040 | 237 | transaction_delete_job(m, other); |
e5b5ae50 LP |
238 | } |
239 | ||
5cb5a6ff | 240 | static int delete_one_unmergeable_job(Manager *m, Job *j) { |
1ffba6fe LP |
241 | Job *k; |
242 | ||
243 | assert(j); | |
244 | ||
245 | /* Tries to delete one item in the linked list | |
246 | * j->transaction_next->transaction_next->... that conflicts | |
247 | * whith another one, in an attempt to make an inconsistent | |
248 | * transaction work. */ | |
249 | ||
250 | /* We rely here on the fact that if a merged with b does not | |
251 | * merge with c, either a or b merge with c neither */ | |
034c6ed7 LP |
252 | LIST_FOREACH(transaction, j, j) |
253 | LIST_FOREACH(transaction, k, j->transaction_next) { | |
1ffba6fe LP |
254 | Job *d; |
255 | ||
256 | /* Is this one mergeable? Then skip it */ | |
5cb5a6ff | 257 | if (job_type_is_mergeable(j->type, k->type)) |
1ffba6fe LP |
258 | continue; |
259 | ||
260 | /* Ok, we found two that conflict, let's see if we can | |
261 | * drop one of them */ | |
262 | if (!j->matters_to_anchor) | |
263 | d = j; | |
264 | else if (!k->matters_to_anchor) | |
265 | d = k; | |
266 | else | |
267 | return -ENOEXEC; | |
268 | ||
269 | /* Ok, we can drop one, so let's do so. */ | |
87f0e418 | 270 | log_debug("Try to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type)); |
1ffba6fe LP |
271 | transaction_delete_job(m, d); |
272 | return 0; | |
273 | } | |
274 | ||
275 | return -EINVAL; | |
276 | } | |
277 | ||
e5b5ae50 | 278 | static int transaction_merge_jobs(Manager *m) { |
11dd41ce | 279 | Job *j; |
034c6ed7 | 280 | Iterator i; |
e5b5ae50 LP |
281 | int r; |
282 | ||
283 | assert(m); | |
284 | ||
1ffba6fe LP |
285 | /* First step, check whether any of the jobs for one specific |
286 | * task conflict. If so, try to drop one of them. */ | |
034c6ed7 | 287 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
1ffba6fe LP |
288 | JobType t; |
289 | Job *k; | |
290 | ||
291 | t = j->type; | |
034c6ed7 | 292 | LIST_FOREACH(transaction, k, j->transaction_next) { |
1ffba6fe LP |
293 | if ((r = job_type_merge(&t, k->type)) >= 0) |
294 | continue; | |
295 | ||
296 | /* OK, we could not merge all jobs for this | |
297 | * action. Let's see if we can get rid of one | |
298 | * of them */ | |
299 | ||
5cb5a6ff | 300 | if ((r = delete_one_unmergeable_job(m, j)) >= 0) |
1ffba6fe LP |
301 | /* Ok, we managed to drop one, now |
302 | * let's ask our callers to call us | |
303 | * again after garbage collecting */ | |
304 | return -EAGAIN; | |
305 | ||
306 | /* We couldn't merge anything. Failure */ | |
307 | return r; | |
308 | } | |
309 | } | |
310 | ||
311 | /* Second step, merge the jobs. */ | |
034c6ed7 | 312 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
e5b5ae50 LP |
313 | JobType t = j->type; |
314 | Job *k; | |
315 | ||
e094e853 | 316 | /* Merge all transactions */ |
034c6ed7 | 317 | LIST_FOREACH(transaction, k, j->transaction_next) |
1ffba6fe | 318 | assert_se(job_type_merge(&t, k->type) == 0); |
e5b5ae50 | 319 | |
5cb5a6ff | 320 | /* If an active job is mergeable, merge it too */ |
87f0e418 LP |
321 | if (j->unit->meta.job) |
322 | job_type_merge(&t, j->unit->meta.job->type); /* Might fail. Which is OK */ | |
e094e853 | 323 | |
e5b5ae50 | 324 | while ((k = j->transaction_next)) { |
ac1135be | 325 | if (j->installed) { |
7fad411c | 326 | transaction_merge_and_delete_job(m, k, j, t); |
e5b5ae50 LP |
327 | j = k; |
328 | } else | |
7fad411c | 329 | transaction_merge_and_delete_job(m, j, k, t); |
e5b5ae50 LP |
330 | } |
331 | ||
332 | assert(!j->transaction_next); | |
333 | assert(!j->transaction_prev); | |
334 | } | |
335 | ||
7fad411c | 336 | return 0; |
e5b5ae50 LP |
337 | } |
338 | ||
87f0e418 LP |
339 | static bool unit_matters_to_anchor(Unit *u, Job *j) { |
340 | assert(u); | |
1ffba6fe LP |
341 | assert(!j->transaction_prev); |
342 | ||
87f0e418 | 343 | /* Checks whether at least one of the jobs for this unit |
1ffba6fe LP |
344 | * matters to the anchor. */ |
345 | ||
034c6ed7 | 346 | LIST_FOREACH(transaction, j, j) |
1ffba6fe LP |
347 | if (j->matters_to_anchor) |
348 | return true; | |
349 | ||
350 | return false; | |
351 | } | |
352 | ||
e5b5ae50 | 353 | static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) { |
034c6ed7 | 354 | Iterator i; |
87f0e418 | 355 | Unit *u; |
11dd41ce | 356 | int r; |
e5b5ae50 LP |
357 | |
358 | assert(m); | |
359 | assert(j); | |
1ffba6fe LP |
360 | assert(!j->transaction_prev); |
361 | ||
362 | /* Does a recursive sweep through the ordering graph, looking | |
363 | * for a cycle. If we find cycle we try to break it. */ | |
e5b5ae50 | 364 | |
7fad411c | 365 | /* Did we find a cycle? */ |
e5b5ae50 LP |
366 | if (j->marker && j->generation == generation) { |
367 | Job *k; | |
368 | ||
369 | /* So, we already have been here. We have a | |
1ffba6fe LP |
370 | * cycle. Let's try to break it. We go backwards in |
371 | * our path and try to find a suitable job to | |
372 | * remove. We use the marker to find our way back, | |
373 | * since smart how we are we stored our way back in | |
374 | * there. */ | |
e5b5ae50 LP |
375 | |
376 | for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { | |
1ffba6fe | 377 | |
ac1135be | 378 | if (!k->installed && |
87f0e418 | 379 | !unit_matters_to_anchor(k->unit, k)) { |
1ffba6fe LP |
380 | /* Ok, we can drop this one, so let's |
381 | * do so. */ | |
87f0e418 LP |
382 | log_debug("Breaking order cycle by deleting job %s/%s", unit_id(k->unit), job_type_to_string(k->type)); |
383 | transaction_delete_unit(m, k->unit); | |
e5b5ae50 LP |
384 | return -EAGAIN; |
385 | } | |
386 | ||
387 | /* Check if this in fact was the beginning of | |
7fad411c | 388 | * the cycle */ |
e5b5ae50 LP |
389 | if (k == j) |
390 | break; | |
391 | } | |
392 | ||
1ffba6fe | 393 | return -ENOEXEC; |
e5b5ae50 LP |
394 | } |
395 | ||
1ffba6fe LP |
396 | /* Make the marker point to where we come from, so that we can |
397 | * find our way backwards if we want to break a cycle */ | |
e5b5ae50 LP |
398 | j->marker = from; |
399 | j->generation = generation; | |
400 | ||
1ffba6fe | 401 | /* We assume that the the dependencies are bidirectional, and |
87f0e418 LP |
402 | * hence can ignore UNIT_AFTER */ |
403 | SET_FOREACH(u, j->unit->meta.dependencies[UNIT_BEFORE], i) { | |
e5b5ae50 LP |
404 | Job *o; |
405 | ||
87f0e418 LP |
406 | /* Is there a job for this unit? */ |
407 | if (!(o = hashmap_get(m->transaction_jobs, u))) | |
1ffba6fe LP |
408 | |
409 | /* Ok, there is no job for this in the | |
410 | * transaction, but maybe there is already one | |
411 | * running? */ | |
87f0e418 | 412 | if (!(o = u->meta.job)) |
e5b5ae50 LP |
413 | continue; |
414 | ||
415 | if ((r = transaction_verify_order_one(m, o, j, generation)) < 0) | |
416 | return r; | |
417 | } | |
418 | ||
419 | return 0; | |
420 | } | |
421 | ||
422 | static int transaction_verify_order(Manager *m, unsigned *generation) { | |
1ffba6fe LP |
423 | Job *j; |
424 | int r; | |
034c6ed7 | 425 | Iterator i; |
1ffba6fe | 426 | |
e5b5ae50 LP |
427 | assert(m); |
428 | assert(generation); | |
429 | ||
1ffba6fe LP |
430 | /* Check if the ordering graph is cyclic. If it is, try to fix |
431 | * that up by dropping one of the jobs. */ | |
e5b5ae50 | 432 | |
034c6ed7 | 433 | HASHMAP_FOREACH(j, m->transaction_jobs, i) |
1ffba6fe LP |
434 | if ((r = transaction_verify_order_one(m, j, NULL, (*generation)++)) < 0) |
435 | return r; | |
e5b5ae50 LP |
436 | |
437 | return 0; | |
438 | } | |
439 | ||
440 | static void transaction_collect_garbage(Manager *m) { | |
441 | bool again; | |
442 | ||
443 | assert(m); | |
444 | ||
1ffba6fe LP |
445 | /* Drop jobs that are not required by any other job */ |
446 | ||
e5b5ae50 | 447 | do { |
034c6ed7 | 448 | Iterator i; |
e5b5ae50 LP |
449 | Job *j; |
450 | ||
451 | again = false; | |
452 | ||
034c6ed7 | 453 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
e5b5ae50 LP |
454 | if (j->object_list) |
455 | continue; | |
456 | ||
87f0e418 | 457 | log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type)); |
302d0040 | 458 | transaction_delete_job(m, j); |
e5b5ae50 LP |
459 | again = true; |
460 | break; | |
461 | } | |
462 | ||
463 | } while (again); | |
464 | } | |
465 | ||
466 | static int transaction_is_destructive(Manager *m, JobMode mode) { | |
034c6ed7 | 467 | Iterator i; |
e5b5ae50 | 468 | Job *j; |
11dd41ce LP |
469 | |
470 | assert(m); | |
11dd41ce | 471 | |
e5b5ae50 LP |
472 | /* Checks whether applying this transaction means that |
473 | * existing jobs would be replaced */ | |
11dd41ce | 474 | |
034c6ed7 | 475 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
e094e853 LP |
476 | |
477 | /* Assume merged */ | |
478 | assert(!j->transaction_prev); | |
479 | assert(!j->transaction_next); | |
480 | ||
87f0e418 LP |
481 | if (j->unit->meta.job && |
482 | j->unit->meta.job != j && | |
483 | !job_type_is_superset(j->type, j->unit->meta.job->type)) | |
e5b5ae50 | 484 | return -EEXIST; |
e094e853 | 485 | } |
11dd41ce | 486 | |
e5b5ae50 LP |
487 | return 0; |
488 | } | |
489 | ||
e094e853 LP |
490 | static void transaction_minimize_impact(Manager *m) { |
491 | bool again; | |
492 | assert(m); | |
493 | ||
494 | /* Drops all unnecessary jobs that reverse already active jobs | |
495 | * or that stop a running service. */ | |
496 | ||
497 | do { | |
498 | Job *j; | |
034c6ed7 | 499 | Iterator i; |
e094e853 LP |
500 | |
501 | again = false; | |
502 | ||
034c6ed7 LP |
503 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
504 | LIST_FOREACH(transaction, j, j) { | |
e094e853 LP |
505 | |
506 | /* If it matters, we shouldn't drop it */ | |
507 | if (j->matters_to_anchor) | |
508 | continue; | |
509 | ||
510 | /* Would this stop a running service? | |
511 | * Would this change an existing job? | |
512 | * If so, let's drop this entry */ | |
87f0e418 LP |
513 | if ((j->type != JOB_STOP || UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(j->unit))) && |
514 | (!j->unit->meta.job || job_type_is_conflicting(j->type, j->unit->meta.job->state))) | |
e094e853 LP |
515 | continue; |
516 | ||
517 | /* Ok, let's get rid of this */ | |
87f0e418 | 518 | log_debug("Deleting %s/%s to minimize impact", unit_id(j->unit), job_type_to_string(j->type)); |
e094e853 LP |
519 | transaction_delete_job(m, j); |
520 | again = true; | |
521 | break; | |
522 | } | |
523 | ||
524 | if (again) | |
525 | break; | |
526 | } | |
527 | ||
528 | } while (again); | |
529 | } | |
530 | ||
e5b5ae50 | 531 | static int transaction_apply(Manager *m, JobMode mode) { |
034c6ed7 | 532 | Iterator i; |
e5b5ae50 LP |
533 | Job *j; |
534 | int r; | |
535 | ||
1ffba6fe LP |
536 | /* Moves the transaction jobs to the set of active jobs */ |
537 | ||
034c6ed7 | 538 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
e094e853 LP |
539 | /* Assume merged */ |
540 | assert(!j->transaction_prev); | |
541 | assert(!j->transaction_next); | |
542 | ||
ac1135be | 543 | if (j->installed) |
e5b5ae50 LP |
544 | continue; |
545 | ||
546 | if ((r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j)) < 0) | |
11dd41ce LP |
547 | goto rollback; |
548 | } | |
549 | ||
e5b5ae50 | 550 | while ((j = hashmap_steal_first(m->transaction_jobs))) { |
ac1135be | 551 | if (j->installed) |
e5b5ae50 LP |
552 | continue; |
553 | ||
87f0e418 LP |
554 | if (j->unit->meta.job) |
555 | job_free(j->unit->meta.job); | |
11dd41ce | 556 | |
87f0e418 | 557 | j->unit->meta.job = j; |
ac1135be | 558 | j->installed = true; |
11dd41ce | 559 | |
e5b5ae50 LP |
560 | /* We're fully installed. Now let's free data we don't |
561 | * need anymore. */ | |
562 | ||
563 | assert(!j->transaction_next); | |
564 | assert(!j->transaction_prev); | |
565 | ||
acbb0225 | 566 | job_schedule_run(j); |
01184e04 LP |
567 | } |
568 | ||
569 | /* As last step, kill all remaining job dependencies. */ | |
f04fa1d5 | 570 | transaction_clean_dependencies(m); |
1ffba6fe | 571 | |
11dd41ce LP |
572 | return 0; |
573 | ||
574 | rollback: | |
575 | ||
034c6ed7 | 576 | HASHMAP_FOREACH(j, m->transaction_jobs, i) { |
ac1135be | 577 | if (j->installed) |
e5b5ae50 LP |
578 | continue; |
579 | ||
580 | hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); | |
581 | } | |
582 | ||
583 | return r; | |
584 | } | |
585 | ||
e5b5ae50 LP |
586 | static int transaction_activate(Manager *m, JobMode mode) { |
587 | int r; | |
588 | unsigned generation = 1; | |
589 | ||
590 | assert(m); | |
591 | ||
592 | /* This applies the changes recorded in transaction_jobs to | |
593 | * the actual list of jobs, if possible. */ | |
594 | ||
595 | /* First step: figure out which jobs matter */ | |
596 | transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++); | |
597 | ||
e094e853 LP |
598 | /* Second step: Try not to stop any running services if |
599 | * we don't have to. Don't try to reverse running | |
600 | * jobs if we don't have to. */ | |
601 | transaction_minimize_impact(m); | |
602 | ||
1ffba6fe | 603 | for (;;) { |
e094e853 | 604 | /* Third step: Let's remove unneeded jobs that might |
1ffba6fe LP |
605 | * be lurking. */ |
606 | transaction_collect_garbage(m); | |
e5b5ae50 | 607 | |
e094e853 | 608 | /* Fourth step: verify order makes sense and correct |
1ffba6fe LP |
609 | * cycles if necessary and possible */ |
610 | if ((r = transaction_verify_order(m, &generation)) >= 0) | |
611 | break; | |
e5b5ae50 | 612 | |
1ffba6fe LP |
613 | if (r != -EAGAIN) |
614 | goto rollback; | |
e5b5ae50 | 615 | |
1ffba6fe LP |
616 | /* Let's see if the resulting transaction ordering |
617 | * graph is still cyclic... */ | |
618 | } | |
619 | ||
620 | for (;;) { | |
5cb5a6ff | 621 | /* Fifth step: let's drop unmergeable entries if |
1ffba6fe LP |
622 | * necessary and possible, merge entries we can |
623 | * merge */ | |
624 | if ((r = transaction_merge_jobs(m)) >= 0) | |
625 | break; | |
626 | ||
627 | if (r != -EAGAIN) | |
628 | goto rollback; | |
629 | ||
e094e853 | 630 | /* Sixth step: an entry got dropped, let's garbage |
1ffba6fe LP |
631 | * collect its dependencies. */ |
632 | transaction_collect_garbage(m); | |
633 | ||
634 | /* Let's see if the resulting transaction still has | |
5cb5a6ff | 635 | * unmergeable entries ... */ |
1ffba6fe LP |
636 | } |
637 | ||
e094e853 | 638 | /* Seventh step: check whether we can actually apply this */ |
e5b5ae50 LP |
639 | if (mode == JOB_FAIL) |
640 | if ((r = transaction_is_destructive(m, mode)) < 0) | |
641 | goto rollback; | |
642 | ||
e094e853 | 643 | /* Eights step: apply changes */ |
e5b5ae50 LP |
644 | if ((r = transaction_apply(m, mode)) < 0) |
645 | goto rollback; | |
646 | ||
647 | assert(hashmap_isempty(m->transaction_jobs)); | |
648 | assert(!m->transaction_anchor); | |
649 | ||
650 | return 0; | |
11dd41ce | 651 | |
e5b5ae50 | 652 | rollback: |
11dd41ce LP |
653 | transaction_abort(m); |
654 | return r; | |
655 | } | |
656 | ||
87f0e418 | 657 | static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool force, bool *is_new) { |
e5b5ae50 | 658 | Job *j, *f; |
60918275 LP |
659 | int r; |
660 | ||
661 | assert(m); | |
87f0e418 | 662 | assert(unit); |
60918275 | 663 | |
e5b5ae50 LP |
664 | /* Looks for an axisting prospective job and returns that. If |
665 | * it doesn't exist it is created and added to the prospective | |
666 | * jobs list. */ | |
60918275 | 667 | |
87f0e418 | 668 | f = hashmap_get(m->transaction_jobs, unit); |
60918275 | 669 | |
034c6ed7 | 670 | LIST_FOREACH(transaction, j, f) { |
87f0e418 | 671 | assert(j->unit == unit); |
60918275 | 672 | |
e5b5ae50 LP |
673 | if (j->type == type) { |
674 | if (is_new) | |
675 | *is_new = false; | |
676 | return j; | |
677 | } | |
678 | } | |
60918275 | 679 | |
87f0e418 LP |
680 | if (unit->meta.job && unit->meta.job->type == type) |
681 | j = unit->meta.job; | |
682 | else if (!(j = job_new(m, type, unit))) | |
e5b5ae50 | 683 | return NULL; |
60918275 | 684 | |
e5b5ae50 LP |
685 | j->generation = 0; |
686 | j->marker = NULL; | |
687 | j->matters_to_anchor = false; | |
5cb5a6ff | 688 | j->forced = force; |
60918275 | 689 | |
034c6ed7 LP |
690 | LIST_PREPEND(Job, transaction, f, j); |
691 | ||
87f0e418 | 692 | if ((r = hashmap_replace(m->transaction_jobs, unit, f)) < 0) { |
034c6ed7 LP |
693 | job_free(j); |
694 | return NULL; | |
695 | } | |
696 | ||
e5b5ae50 LP |
697 | if (is_new) |
698 | *is_new = true; | |
60918275 | 699 | |
e5b5ae50 LP |
700 | return j; |
701 | } | |
11dd41ce | 702 | |
302d0040 | 703 | void manager_transaction_unlink_job(Manager *m, Job *j) { |
e5b5ae50 LP |
704 | assert(m); |
705 | assert(j); | |
11dd41ce | 706 | |
e5b5ae50 LP |
707 | if (j->transaction_prev) |
708 | j->transaction_prev->transaction_next = j->transaction_next; | |
709 | else if (j->transaction_next) | |
87f0e418 | 710 | hashmap_replace(m->transaction_jobs, j->unit, j->transaction_next); |
e5b5ae50 | 711 | else |
87f0e418 | 712 | hashmap_remove_value(m->transaction_jobs, j->unit, j); |
e5b5ae50 LP |
713 | |
714 | if (j->transaction_next) | |
715 | j->transaction_next->transaction_prev = j->transaction_prev; | |
716 | ||
717 | j->transaction_prev = j->transaction_next = NULL; | |
718 | ||
719 | while (j->subject_list) | |
720 | job_dependency_free(j->subject_list); | |
1e198baf LP |
721 | |
722 | while (j->object_list) { | |
723 | Job *other = j->object_list->matters ? j->object_list->subject : NULL; | |
724 | ||
e5b5ae50 | 725 | job_dependency_free(j->object_list); |
1e198baf LP |
726 | |
727 | if (other) { | |
5cb5a6ff | 728 | log_debug("Deleting job %s/%s as dependency of job %s/%s", |
87f0e418 LP |
729 | unit_id(other->unit), job_type_to_string(other->type), |
730 | unit_id(j->unit), job_type_to_string(j->type)); | |
302d0040 | 731 | transaction_delete_job(m, other); |
1e198baf LP |
732 | } |
733 | } | |
e5b5ae50 LP |
734 | } |
735 | ||
87f0e418 | 736 | static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit *unit, Job *by, bool matters, bool force, Job **_ret) { |
e5b5ae50 | 737 | Job *ret; |
034c6ed7 | 738 | Iterator i; |
87f0e418 | 739 | Unit *dep; |
e5b5ae50 LP |
740 | int r; |
741 | bool is_new; | |
742 | ||
743 | assert(m); | |
744 | assert(type < _JOB_TYPE_MAX); | |
87f0e418 | 745 | assert(unit); |
e5b5ae50 | 746 | |
87f0e418 | 747 | if (unit->meta.load_state != UNIT_LOADED) |
21b293e8 LP |
748 | return -EINVAL; |
749 | ||
87f0e418 | 750 | if (!unit_job_is_applicable(unit, type)) |
cd2dbd7d LP |
751 | return -EBADR; |
752 | ||
e5b5ae50 | 753 | /* First add the job. */ |
87f0e418 | 754 | if (!(ret = transaction_add_one_job(m, type, unit, force, &is_new))) |
e5b5ae50 LP |
755 | return -ENOMEM; |
756 | ||
757 | /* Then, add a link to the job. */ | |
758 | if (!job_dependency_new(by, ret, matters)) | |
759 | return -ENOMEM; | |
760 | ||
761 | if (is_new) { | |
762 | /* Finally, recursively add in all dependencies. */ | |
763 | if (type == JOB_START || type == JOB_RELOAD_OR_START) { | |
87f0e418 | 764 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i) |
542563ba | 765 | if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 | 766 | goto fail; |
87f0e418 | 767 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUIRES], i) |
542563ba | 768 | if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 | 769 | goto fail; |
87f0e418 | 770 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i) |
542563ba | 771 | if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 | 772 | goto fail; |
87f0e418 | 773 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i) |
542563ba | 774 | if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 | 775 | goto fail; |
87f0e418 | 776 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUISITE], i) |
542563ba | 777 | if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 | 778 | goto fail; |
87f0e418 | 779 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i) |
542563ba | 780 | if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 LP |
781 | goto fail; |
782 | ||
783 | } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { | |
784 | ||
87f0e418 | 785 | SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i) |
542563ba | 786 | if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR) |
e5b5ae50 LP |
787 | goto fail; |
788 | } | |
789 | ||
790 | /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */ | |
791 | } | |
60918275 LP |
792 | |
793 | return 0; | |
794 | ||
795 | fail: | |
e5b5ae50 LP |
796 | return r; |
797 | } | |
798 | ||
87f0e418 | 799 | int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret) { |
e5b5ae50 LP |
800 | int r; |
801 | Job *ret; | |
802 | ||
803 | assert(m); | |
804 | assert(type < _JOB_TYPE_MAX); | |
87f0e418 | 805 | assert(unit); |
e5b5ae50 | 806 | assert(mode < _JOB_MODE_MAX); |
60918275 | 807 | |
87f0e418 | 808 | if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret))) { |
11dd41ce | 809 | transaction_abort(m); |
e5b5ae50 LP |
810 | return r; |
811 | } | |
11dd41ce | 812 | |
e5b5ae50 LP |
813 | if ((r = transaction_activate(m, mode)) < 0) |
814 | return r; | |
815 | ||
816 | if (_ret) | |
817 | *_ret = ret; | |
60918275 | 818 | |
e5b5ae50 LP |
819 | return 0; |
820 | } | |
60918275 LP |
821 | |
822 | Job *manager_get_job(Manager *m, uint32_t id) { | |
823 | assert(m); | |
824 | ||
825 | return hashmap_get(m->jobs, UINT32_TO_PTR(id)); | |
826 | } | |
827 | ||
87f0e418 | 828 | Unit *manager_get_unit(Manager *m, const char *name) { |
60918275 LP |
829 | assert(m); |
830 | assert(name); | |
831 | ||
87f0e418 | 832 | return hashmap_get(m->units, name); |
60918275 LP |
833 | } |
834 | ||
034c6ed7 | 835 | static void dispatch_load_queue(Manager *m) { |
60918275 LP |
836 | Meta *meta; |
837 | ||
838 | assert(m); | |
839 | ||
223dabab LP |
840 | /* Make sure we are not run recursively */ |
841 | if (m->dispatching_load_queue) | |
034c6ed7 | 842 | return; |
223dabab LP |
843 | |
844 | m->dispatching_load_queue = true; | |
845 | ||
87f0e418 | 846 | /* Dispatches the load queue. Takes a unit from the queue and |
60918275 LP |
847 | * tries to load its data until the queue is empty */ |
848 | ||
849 | while ((meta = m->load_queue)) { | |
034c6ed7 LP |
850 | assert(meta->in_load_queue); |
851 | ||
87f0e418 | 852 | unit_load(UNIT(meta)); |
60918275 LP |
853 | } |
854 | ||
223dabab | 855 | m->dispatching_load_queue = false; |
60918275 LP |
856 | } |
857 | ||
0301abf4 | 858 | int manager_load_unit(Manager *m, const char *path, Unit **_ret) { |
87f0e418 | 859 | Unit *ret; |
60918275 | 860 | int r; |
0301abf4 | 861 | const char *name; |
60918275 LP |
862 | |
863 | assert(m); | |
0301abf4 | 864 | assert(path); |
60918275 | 865 | assert(_ret); |
60918275 | 866 | |
223dabab | 867 | /* This will load the service information files, but not actually |
0301abf4 LP |
868 | * start any services or anything. */ |
869 | ||
870 | name = file_name_from_path(path); | |
60918275 | 871 | |
87f0e418 | 872 | if ((ret = manager_get_unit(m, name))) { |
034c6ed7 LP |
873 | *_ret = ret; |
874 | return 0; | |
875 | } | |
60918275 | 876 | |
87f0e418 | 877 | if (!(ret = unit_new(m))) |
60918275 LP |
878 | return -ENOMEM; |
879 | ||
0301abf4 LP |
880 | if (is_path(path)) { |
881 | if (!(ret->meta.load_path = strdup(path))) { | |
882 | unit_free(ret); | |
883 | return -ENOMEM; | |
884 | } | |
885 | } | |
886 | ||
87f0e418 LP |
887 | if ((r = unit_add_name(ret, name)) < 0) { |
888 | unit_free(ret); | |
1ffba6fe | 889 | return r; |
60918275 LP |
890 | } |
891 | ||
87f0e418 | 892 | unit_add_to_load_queue(ret); |
60918275 LP |
893 | dispatch_load_queue(m); |
894 | ||
60918275 LP |
895 | *_ret = ret; |
896 | return 0; | |
897 | } | |
a66d02c3 | 898 | |
cea8e32e | 899 | void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) { |
034c6ed7 | 900 | Iterator i; |
a66d02c3 LP |
901 | Job *j; |
902 | ||
903 | assert(s); | |
904 | assert(f); | |
905 | ||
034c6ed7 | 906 | HASHMAP_FOREACH(j, s->jobs, i) |
cea8e32e | 907 | job_dump(j, f, prefix); |
a66d02c3 LP |
908 | } |
909 | ||
87f0e418 | 910 | void manager_dump_units(Manager *s, FILE *f, const char *prefix) { |
034c6ed7 | 911 | Iterator i; |
87f0e418 | 912 | Unit *u; |
11dd41ce | 913 | const char *t; |
a66d02c3 LP |
914 | |
915 | assert(s); | |
916 | assert(f); | |
917 | ||
87f0e418 LP |
918 | HASHMAP_FOREACH_KEY(u, t, s->units, i) |
919 | if (unit_id(u) == t) | |
920 | unit_dump(u, f, prefix); | |
a66d02c3 | 921 | } |
7fad411c LP |
922 | |
923 | void manager_clear_jobs(Manager *m) { | |
924 | Job *j; | |
925 | ||
926 | assert(m); | |
927 | ||
928 | transaction_abort(m); | |
929 | ||
930 | while ((j = hashmap_first(m->jobs))) | |
931 | job_free(j); | |
932 | } | |
83c60c9f | 933 | |
034c6ed7 | 934 | void manager_dispatch_run_queue(Manager *m) { |
83c60c9f | 935 | Job *j; |
83c60c9f | 936 | |
034c6ed7 LP |
937 | if (m->dispatching_run_queue) |
938 | return; | |
939 | ||
940 | m->dispatching_run_queue = true; | |
9152c765 | 941 | |
034c6ed7 | 942 | while ((j = m->run_queue)) { |
ac1135be | 943 | assert(j->installed); |
034c6ed7 LP |
944 | assert(j->in_run_queue); |
945 | ||
946 | job_run_and_invalidate(j); | |
9152c765 | 947 | } |
034c6ed7 LP |
948 | |
949 | m->dispatching_run_queue = false; | |
9152c765 LP |
950 | } |
951 | ||
034c6ed7 | 952 | static int manager_dispatch_sigchld(Manager *m) { |
9152c765 LP |
953 | assert(m); |
954 | ||
acbb0225 LP |
955 | log_debug("dispatching SIGCHLD"); |
956 | ||
9152c765 LP |
957 | for (;;) { |
958 | siginfo_t si; | |
87f0e418 | 959 | Unit *u; |
9152c765 LP |
960 | |
961 | zero(si); | |
acbb0225 LP |
962 | if (waitid(P_ALL, 0, &si, WEXITED|WNOHANG) < 0) { |
963 | ||
964 | if (errno == ECHILD) | |
965 | break; | |
966 | ||
9152c765 | 967 | return -errno; |
acbb0225 | 968 | } |
9152c765 LP |
969 | |
970 | if (si.si_pid == 0) | |
971 | break; | |
972 | ||
034c6ed7 LP |
973 | if (si.si_code != CLD_EXITED && si.si_code != CLD_KILLED && si.si_code != CLD_DUMPED) |
974 | continue; | |
975 | ||
acbb0225 LP |
976 | log_debug("child %llu died (code=%s, status=%i)", (long long unsigned) si.si_pid, sigchld_code(si.si_code), si.si_status); |
977 | ||
87f0e418 | 978 | if (!(u = hashmap_remove(m->watch_pids, UINT32_TO_PTR(si.si_pid)))) |
9152c765 LP |
979 | continue; |
980 | ||
87f0e418 | 981 | UNIT_VTABLE(u)->sigchld_event(u, si.si_pid, si.si_code, si.si_status); |
9152c765 LP |
982 | } |
983 | ||
984 | return 0; | |
985 | } | |
986 | ||
b9cd2ec1 | 987 | static int manager_process_signal_fd(Manager *m, bool *quit) { |
9152c765 LP |
988 | ssize_t n; |
989 | struct signalfd_siginfo sfsi; | |
990 | bool sigchld = false; | |
991 | ||
992 | assert(m); | |
993 | ||
994 | for (;;) { | |
acbb0225 | 995 | if ((n = read(m->signal_watch.fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) { |
9152c765 LP |
996 | |
997 | if (n >= 0) | |
998 | return -EIO; | |
999 | ||
1000 | if (errno == EAGAIN) | |
acbb0225 | 1001 | break; |
9152c765 LP |
1002 | |
1003 | return -errno; | |
1004 | } | |
1005 | ||
b9cd2ec1 LP |
1006 | switch (sfsi.ssi_signo) { |
1007 | ||
1008 | case SIGCHLD: | |
9152c765 | 1009 | sigchld = true; |
b9cd2ec1 LP |
1010 | break; |
1011 | ||
1012 | case SIGINT: | |
6632c602 | 1013 | case SIGTERM: |
b9cd2ec1 LP |
1014 | *quit = true; |
1015 | return 0; | |
6632c602 LP |
1016 | |
1017 | default: | |
1018 | log_info("Got unhandled signal <%s>.", strsignal(sfsi.ssi_signo)); | |
b9cd2ec1 | 1019 | } |
9152c765 LP |
1020 | } |
1021 | ||
1022 | if (sigchld) | |
034c6ed7 LP |
1023 | return manager_dispatch_sigchld(m); |
1024 | ||
1025 | return 0; | |
1026 | } | |
1027 | ||
b9cd2ec1 | 1028 | static int process_event(Manager *m, struct epoll_event *ev, bool *quit) { |
034c6ed7 | 1029 | int r; |
acbb0225 | 1030 | Watch *w; |
034c6ed7 LP |
1031 | |
1032 | assert(m); | |
1033 | assert(ev); | |
1034 | ||
acbb0225 | 1035 | assert(w = ev->data.ptr); |
034c6ed7 | 1036 | |
acbb0225 | 1037 | switch (w->type) { |
034c6ed7 | 1038 | |
acbb0225 | 1039 | case WATCH_SIGNAL_FD: |
034c6ed7 | 1040 | |
acbb0225 LP |
1041 | /* An incoming signal? */ |
1042 | if (ev->events != POLLIN) | |
1043 | return -EINVAL; | |
034c6ed7 | 1044 | |
b9cd2ec1 | 1045 | if ((r = manager_process_signal_fd(m, quit)) < 0) |
acbb0225 | 1046 | return r; |
034c6ed7 | 1047 | |
acbb0225 | 1048 | break; |
034c6ed7 | 1049 | |
acbb0225 | 1050 | case WATCH_FD: |
034c6ed7 | 1051 | |
acbb0225 LP |
1052 | /* Some fd event, to be dispatched to the units */ |
1053 | UNIT_VTABLE(w->unit)->fd_event(w->unit, w->fd, ev->events, w); | |
1054 | break; | |
034c6ed7 | 1055 | |
acbb0225 LP |
1056 | case WATCH_TIMER: { |
1057 | uint64_t v; | |
1058 | ssize_t k; | |
034c6ed7 | 1059 | |
acbb0225 LP |
1060 | /* Some timer event, to be dispatched to the units */ |
1061 | if ((k = read(ev->data.fd, &v, sizeof(v))) != sizeof(v)) { | |
034c6ed7 | 1062 | |
acbb0225 LP |
1063 | if (k < 0 && (errno == EINTR || errno == EAGAIN)) |
1064 | break; | |
034c6ed7 | 1065 | |
acbb0225 | 1066 | return k < 0 ? -errno : -EIO; |
034c6ed7 LP |
1067 | } |
1068 | ||
acbb0225 LP |
1069 | UNIT_VTABLE(w->unit)->timer_event(w->unit, v, w); |
1070 | break; | |
1071 | } | |
1072 | ||
1073 | default: | |
1074 | assert_not_reached("Unknown epoll event type."); | |
034c6ed7 | 1075 | } |
9152c765 LP |
1076 | |
1077 | return 0; | |
1078 | } | |
1079 | ||
1080 | int manager_loop(Manager *m) { | |
1081 | int r; | |
b9cd2ec1 | 1082 | bool quit = false; |
9152c765 LP |
1083 | |
1084 | assert(m); | |
1085 | ||
1086 | for (;;) { | |
034c6ed7 | 1087 | struct epoll_event events[32]; |
9152c765 LP |
1088 | int n, i; |
1089 | ||
034c6ed7 LP |
1090 | manager_dispatch_run_queue(m); |
1091 | ||
9152c765 LP |
1092 | if ((n = epoll_wait(m->epoll_fd, events, ELEMENTSOF(events), -1)) < 0) { |
1093 | ||
1094 | if (errno == -EINTR) | |
1095 | continue; | |
1096 | ||
1097 | return -errno; | |
1098 | } | |
1099 | ||
b9cd2ec1 LP |
1100 | for (i = 0; i < n; i++) { |
1101 | if ((r = process_event(m, events + i, &quit)) < 0) | |
034c6ed7 | 1102 | return r; |
b9cd2ec1 LP |
1103 | |
1104 | if (quit) | |
1105 | return 0; | |
1106 | } | |
83c60c9f LP |
1107 | } |
1108 | } |