]>
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> |
60918275 LP |
6 | |
7 | #include "manager.h" | |
8 | #include "hashmap.h" | |
9 | #include "macro.h" | |
10 | #include "strv.h" | |
223dabab | 11 | #include "load-fragment.h" |
60918275 LP |
12 | |
13 | Manager* manager_new(void) { | |
14 | Manager *m; | |
15 | ||
16 | if (!(m = new0(Manager, 1))) | |
17 | return NULL; | |
18 | ||
19 | if (!(m->names = hashmap_new(string_hash_func, string_compare_func))) | |
20 | goto fail; | |
21 | ||
22 | if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) | |
23 | goto fail; | |
24 | ||
e5b5ae50 | 25 | if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func))) |
60918275 LP |
26 | goto fail; |
27 | ||
28 | return m; | |
29 | ||
30 | fail: | |
31 | manager_free(m); | |
32 | return NULL; | |
33 | } | |
34 | ||
35 | void manager_free(Manager *m) { | |
36 | Name *n; | |
e5b5ae50 | 37 | Job *j; |
60918275 LP |
38 | |
39 | assert(m); | |
40 | ||
41 | while ((n = hashmap_first(m->names))) | |
42 | name_free(n); | |
43 | ||
e5b5ae50 LP |
44 | while ((j = hashmap_steal_first(m->transaction_jobs))) |
45 | job_free(j); | |
46 | ||
60918275 LP |
47 | hashmap_free(m->names); |
48 | hashmap_free(m->jobs); | |
e5b5ae50 | 49 | hashmap_free(m->transaction_jobs); |
60918275 LP |
50 | |
51 | free(m); | |
52 | } | |
53 | ||
11dd41ce LP |
54 | static void transaction_abort(Manager *m) { |
55 | Job *j; | |
56 | ||
57 | assert(m); | |
11dd41ce | 58 | |
e5b5ae50 LP |
59 | while ((j = hashmap_first(m->transaction_jobs))) |
60 | if (j->linked) | |
61 | manager_transaction_delete_job(m, j); | |
62 | else | |
63 | job_free(j); | |
64 | ||
65 | assert(hashmap_isempty(m->transaction_jobs)); | |
66 | assert(!m->transaction_anchor); | |
67 | } | |
68 | ||
69 | static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) { | |
70 | JobDependency *l; | |
71 | ||
72 | assert(m); | |
73 | ||
74 | for (l = j ? j->subject_list : m->transaction_anchor; l; l = l->subject_next) { | |
75 | ||
76 | /* This link does not matter */ | |
77 | if (!l->matters) | |
78 | continue; | |
79 | ||
80 | /* This name has already been marked */ | |
81 | if (l->object->generation == generation) | |
82 | continue; | |
83 | ||
84 | l->object->matters_to_anchor = true; | |
85 | l->object->generation = generation; | |
86 | ||
87 | transaction_find_jobs_that_matter_to_anchor(m, l->object, generation); | |
88 | } | |
89 | } | |
90 | ||
91 | static bool types_match(JobType a, JobType b, JobType c, JobType d) { | |
92 | return | |
93 | (a == c && b == d) || | |
94 | (a == d && b == c); | |
95 | } | |
96 | ||
97 | static int types_merge(JobType *a, JobType b) { | |
98 | if (*a == b) | |
99 | return 0; | |
11dd41ce | 100 | |
e5b5ae50 LP |
101 | if (types_match(*a, b, JOB_START, JOB_VERIFY_STARTED)) |
102 | *a = JOB_START; | |
103 | else if (types_match(*a, b, JOB_START, JOB_RELOAD) || | |
104 | types_match(*a, b, JOB_START, JOB_RELOAD_OR_START) || | |
105 | types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD_OR_START) || | |
106 | types_match(*a, b, JOB_RELOAD, JOB_RELOAD_OR_START)) | |
107 | *a = JOB_RELOAD_OR_START; | |
108 | else if (types_match(*a, b, JOB_START, JOB_RESTART) || | |
109 | types_match(*a, b, JOB_START, JOB_TRY_RESTART) || | |
110 | types_match(*a, b, JOB_VERIFY_STARTED, JOB_RESTART) || | |
111 | types_match(*a, b, JOB_RELOAD, JOB_RESTART) || | |
112 | types_match(*a, b, JOB_RELOAD_OR_START, JOB_RESTART) || | |
113 | types_match(*a, b, JOB_RELOAD_OR_START, JOB_TRY_RESTART) || | |
114 | types_match(*a, b, JOB_RESTART, JOB_TRY_RESTART)) | |
115 | *a = JOB_RESTART; | |
116 | else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_RELOAD)) | |
117 | *a = JOB_RELOAD; | |
118 | else if (types_match(*a, b, JOB_VERIFY_STARTED, JOB_TRY_RESTART) || | |
119 | types_match(*a, b, JOB_RELOAD, JOB_TRY_RESTART)) | |
120 | *a = JOB_TRY_RESTART; | |
121 | ||
122 | return -EEXIST; | |
11dd41ce LP |
123 | } |
124 | ||
e5b5ae50 LP |
125 | static void manager_merge_and_delete_prospective_job(Manager *m, Job *j, Job *other, JobType t) { |
126 | JobDependency *l, *last; | |
127 | ||
128 | assert(j); | |
129 | assert(other); | |
130 | assert(j->name == other->name); | |
131 | assert(!j->linked); | |
132 | ||
133 | j->type = t; | |
134 | j->state = JOB_WAITING; | |
135 | ||
136 | j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor; | |
137 | ||
138 | /* Patch us in as new owner of the JobDependency objects */ | |
139 | last = NULL; | |
140 | for (l = other->subject_list; l; l = l->subject_next) { | |
141 | assert(l->subject == other); | |
142 | l->subject = j; | |
143 | last = l; | |
144 | } | |
145 | ||
146 | /* Merge both lists */ | |
147 | if (last) { | |
148 | last->subject_next = j->subject_list; | |
149 | if (j->subject_list) | |
150 | j->subject_list->subject_prev = last; | |
151 | j->subject_list = other->subject_list; | |
152 | } | |
153 | ||
154 | /* Patch us in as new owner of the JobDependency objects */ | |
155 | last = NULL; | |
156 | for (l = other->object_list; l; l = l->object_next) { | |
157 | assert(l->object == other); | |
158 | l->object = j; | |
159 | last = l; | |
160 | } | |
161 | ||
162 | /* Merge both lists */ | |
163 | if (last) { | |
164 | last->object_next = j->object_list; | |
165 | if (j->object_list) | |
166 | j->object_list->object_prev = last; | |
167 | j->object_list = other->object_list; | |
168 | } | |
169 | ||
170 | ||
171 | /* Kill the other job */ | |
172 | other->subject_list = NULL; | |
173 | other->object_list = NULL; | |
174 | manager_transaction_delete_job(m, other); | |
175 | } | |
176 | ||
177 | static int transaction_merge_jobs(Manager *m) { | |
11dd41ce | 178 | Job *j; |
e5b5ae50 LP |
179 | void *state; |
180 | int r; | |
181 | ||
182 | assert(m); | |
183 | ||
184 | HASHMAP_FOREACH(j, m->transaction_jobs, state) { | |
185 | JobType t = j->type; | |
186 | Job *k; | |
187 | ||
188 | for (k = j->transaction_next; k; k = k->transaction_next) | |
189 | if ((r = types_merge(&t, k->type)) < 0) | |
190 | return r; | |
191 | ||
192 | while ((k = j->transaction_next)) { | |
193 | if (j->linked) { | |
194 | manager_merge_and_delete_prospective_job(m, k, j, t); | |
195 | j = k; | |
196 | } else | |
197 | manager_merge_and_delete_prospective_job(m, j, k, t); | |
198 | } | |
199 | ||
200 | assert(!j->transaction_next); | |
201 | assert(!j->transaction_prev); | |
202 | } | |
203 | ||
204 | return r; | |
205 | } | |
206 | ||
207 | static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) { | |
208 | void *state; | |
209 | Name *n; | |
11dd41ce | 210 | int r; |
e5b5ae50 LP |
211 | |
212 | assert(m); | |
213 | assert(j); | |
214 | ||
215 | /* Did we find a loop? */ | |
216 | if (j->marker && j->generation == generation) { | |
217 | Job *k; | |
218 | ||
219 | /* So, we already have been here. We have a | |
220 | * loop. Let's try to break it. We go backwards in our | |
221 | * path and try to find a suitable job to remove. */ | |
222 | ||
223 | for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { | |
224 | if (!k->matters_to_anchor) { | |
225 | manager_transaction_delete_job(m, k); | |
226 | return -EAGAIN; | |
227 | } | |
228 | ||
229 | /* Check if this in fact was the beginning of | |
230 | * the loop */ | |
231 | if (k == j) | |
232 | break; | |
233 | } | |
234 | ||
235 | return -ELOOP; | |
236 | } | |
237 | ||
238 | j->marker = from; | |
239 | j->generation = generation; | |
240 | ||
241 | /* We assume that the the dependencies are both-ways, and | |
242 | * hence can ignore NAME_AFTER */ | |
243 | ||
244 | SET_FOREACH(n, j->name->meta.dependencies[NAME_BEFORE], state) { | |
245 | Job *o; | |
246 | ||
247 | if (!(o = hashmap_get(m->transaction_jobs, n))) | |
248 | if (!(o = n->meta.job)) | |
249 | continue; | |
250 | ||
251 | if ((r = transaction_verify_order_one(m, o, j, generation)) < 0) | |
252 | return r; | |
253 | } | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
258 | static int transaction_verify_order(Manager *m, unsigned *generation) { | |
259 | bool again; | |
260 | assert(m); | |
261 | assert(generation); | |
262 | ||
263 | do { | |
264 | Job *j; | |
265 | int r; | |
266 | void *state; | |
267 | ||
268 | again = false; | |
269 | ||
270 | HASHMAP_FOREACH(j, m->transaction_jobs, state) { | |
271 | ||
272 | /* Assume merged */ | |
273 | assert(!j->transaction_next); | |
274 | assert(!j->transaction_prev); | |
275 | ||
276 | if ((r = transaction_verify_order_one(m, j, NULL, (*generation)++)) < 0) { | |
277 | ||
278 | /* There was a loop, but it was fixed, | |
279 | * we need to restart our algorithm */ | |
280 | if (r == -EAGAIN) { | |
281 | again = true; | |
282 | break; | |
283 | } | |
284 | ||
285 | return r; | |
286 | } | |
287 | } | |
288 | } while (again); | |
289 | ||
290 | return 0; | |
291 | } | |
292 | ||
293 | static void transaction_collect_garbage(Manager *m) { | |
294 | bool again; | |
295 | ||
296 | assert(m); | |
297 | ||
298 | do { | |
299 | void *state; | |
300 | Job *j; | |
301 | ||
302 | again = false; | |
303 | ||
304 | HASHMAP_FOREACH(j, m->transaction_jobs, state) { | |
305 | if (j->object_list) | |
306 | continue; | |
307 | ||
308 | manager_transaction_delete_job(m, j); | |
309 | again = true; | |
310 | break; | |
311 | } | |
312 | ||
313 | } while (again); | |
314 | } | |
315 | ||
316 | static int transaction_is_destructive(Manager *m, JobMode mode) { | |
11dd41ce | 317 | void *state; |
e5b5ae50 | 318 | Job *j; |
11dd41ce LP |
319 | |
320 | assert(m); | |
11dd41ce | 321 | |
e5b5ae50 LP |
322 | /* Checks whether applying this transaction means that |
323 | * existing jobs would be replaced */ | |
11dd41ce | 324 | |
e5b5ae50 LP |
325 | HASHMAP_FOREACH(j, m->transaction_jobs, state) |
326 | if (j->name->meta.job && j->name->meta.job != j) | |
327 | return -EEXIST; | |
11dd41ce | 328 | |
e5b5ae50 LP |
329 | return 0; |
330 | } | |
331 | ||
332 | static int transaction_apply(Manager *m, JobMode mode) { | |
333 | void *state; | |
334 | Job *j; | |
335 | int r; | |
336 | ||
337 | HASHMAP_FOREACH(j, m->transaction_jobs, state) { | |
338 | if (j->linked) | |
339 | continue; | |
340 | ||
341 | if ((r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j)) < 0) | |
11dd41ce LP |
342 | goto rollback; |
343 | } | |
344 | ||
e5b5ae50 LP |
345 | while ((j = hashmap_steal_first(m->transaction_jobs))) { |
346 | if (j->linked) | |
347 | continue; | |
348 | ||
349 | if (j->name->meta.job) | |
350 | job_free(j->name->meta.job); | |
11dd41ce | 351 | |
11dd41ce LP |
352 | j->name->meta.job = j; |
353 | j->linked = true; | |
11dd41ce | 354 | |
e5b5ae50 LP |
355 | /* We're fully installed. Now let's free data we don't |
356 | * need anymore. */ | |
357 | ||
358 | assert(!j->transaction_next); | |
359 | assert(!j->transaction_prev); | |
360 | ||
361 | while (j->subject_list) | |
362 | job_dependency_free(j->subject_list); | |
363 | while (j->object_list) | |
364 | job_dependency_free(j->object_list); | |
365 | } | |
11dd41ce LP |
366 | |
367 | return 0; | |
368 | ||
369 | rollback: | |
370 | ||
e5b5ae50 LP |
371 | HASHMAP_FOREACH(j, m->transaction_jobs, state) { |
372 | if (j->linked) | |
373 | continue; | |
374 | ||
375 | hashmap_remove(m->jobs, UINT32_TO_PTR(j->id)); | |
376 | } | |
377 | ||
378 | return r; | |
379 | } | |
380 | ||
381 | ||
382 | static int transaction_activate(Manager *m, JobMode mode) { | |
383 | int r; | |
384 | unsigned generation = 1; | |
385 | ||
386 | assert(m); | |
387 | ||
388 | /* This applies the changes recorded in transaction_jobs to | |
389 | * the actual list of jobs, if possible. */ | |
390 | ||
391 | /* First step: figure out which jobs matter */ | |
392 | transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++); | |
393 | ||
394 | /* Second step: let's merge entries we can merge */ | |
395 | if ((r = transaction_merge_jobs(m)) < 0) | |
396 | goto rollback; | |
397 | ||
398 | /* Third step: verify order makes sense */ | |
399 | if ((r = transaction_verify_order(m, &generation)) < 0) | |
400 | goto rollback; | |
401 | ||
402 | /* Third step: do garbage colletion */ | |
403 | transaction_collect_garbage(m); | |
404 | ||
405 | /* Fourth step: check whether we can actually apply this */ | |
406 | if (mode == JOB_FAIL) | |
407 | if ((r = transaction_is_destructive(m, mode)) < 0) | |
408 | goto rollback; | |
409 | ||
410 | /* Fifth step: apply changes */ | |
411 | if ((r = transaction_apply(m, mode)) < 0) | |
412 | goto rollback; | |
413 | ||
414 | assert(hashmap_isempty(m->transaction_jobs)); | |
415 | assert(!m->transaction_anchor); | |
416 | ||
417 | return 0; | |
11dd41ce | 418 | |
e5b5ae50 | 419 | rollback: |
11dd41ce LP |
420 | transaction_abort(m); |
421 | return r; | |
422 | } | |
423 | ||
e5b5ae50 LP |
424 | static Job* transaction_add_job(Manager *m, JobType type, Name *name, bool *is_new) { |
425 | Job *j, *f; | |
60918275 LP |
426 | int r; |
427 | ||
428 | assert(m); | |
60918275 | 429 | assert(name); |
60918275 | 430 | |
e5b5ae50 LP |
431 | /* Looks for an axisting prospective job and returns that. If |
432 | * it doesn't exist it is created and added to the prospective | |
433 | * jobs list. */ | |
60918275 | 434 | |
e5b5ae50 | 435 | f = hashmap_get(m->transaction_jobs, name); |
60918275 | 436 | |
e5b5ae50 LP |
437 | for (j = f; j; j = j->transaction_next) { |
438 | assert(j->name == name); | |
60918275 | 439 | |
e5b5ae50 LP |
440 | if (j->type == type) { |
441 | if (is_new) | |
442 | *is_new = false; | |
443 | return j; | |
444 | } | |
445 | } | |
60918275 | 446 | |
e5b5ae50 LP |
447 | if (name->meta.job && name->meta.job->type == type) |
448 | j = name->meta.job; | |
449 | else if (!(j = job_new(m, type, name))) | |
450 | return NULL; | |
60918275 | 451 | |
e5b5ae50 LP |
452 | if ((r = hashmap_replace(m->transaction_jobs, name, j)) < 0) { |
453 | job_free(j); | |
454 | return NULL; | |
60918275 LP |
455 | } |
456 | ||
e5b5ae50 | 457 | j->transaction_next = f; |
60918275 | 458 | |
e5b5ae50 LP |
459 | if (f) |
460 | f->transaction_prev = j; | |
11dd41ce | 461 | |
e5b5ae50 LP |
462 | j->generation = 0; |
463 | j->marker = NULL; | |
464 | j->matters_to_anchor = false; | |
60918275 | 465 | |
e5b5ae50 LP |
466 | if (is_new) |
467 | *is_new = true; | |
60918275 | 468 | |
e5b5ae50 LP |
469 | return j; |
470 | } | |
11dd41ce | 471 | |
e5b5ae50 LP |
472 | void manager_transaction_delete_job(Manager *m, Job *j) { |
473 | assert(m); | |
474 | assert(j); | |
11dd41ce | 475 | |
e5b5ae50 LP |
476 | if (j->transaction_prev) |
477 | j->transaction_prev->transaction_next = j->transaction_next; | |
478 | else if (j->transaction_next) | |
479 | hashmap_replace(m->transaction_jobs, j->name, j->transaction_next); | |
480 | else | |
481 | hashmap_remove_value(m->transaction_jobs, j->name, j); | |
482 | ||
483 | if (j->transaction_next) | |
484 | j->transaction_next->transaction_prev = j->transaction_prev; | |
485 | ||
486 | j->transaction_prev = j->transaction_next = NULL; | |
487 | ||
488 | while (j->subject_list) | |
489 | job_dependency_free(j->subject_list); | |
490 | while (j->object_list) | |
491 | job_dependency_free(j->object_list); | |
492 | } | |
493 | ||
494 | static int real_add_job(Manager *m, JobType type, Name *name, Job *by, bool matters, bool force, Job **_ret) { | |
495 | Job *ret; | |
496 | void *state; | |
497 | Name *dep; | |
498 | int r; | |
499 | bool is_new; | |
500 | ||
501 | assert(m); | |
502 | assert(type < _JOB_TYPE_MAX); | |
503 | assert(name); | |
504 | ||
505 | /* First add the job. */ | |
506 | if (!(ret = transaction_add_job(m, type, name, &is_new))) | |
507 | return -ENOMEM; | |
508 | ||
509 | /* Then, add a link to the job. */ | |
510 | if (!job_dependency_new(by, ret, matters)) | |
511 | return -ENOMEM; | |
512 | ||
513 | if (is_new) { | |
514 | /* Finally, recursively add in all dependencies. */ | |
515 | if (type == JOB_START || type == JOB_RELOAD_OR_START) { | |
516 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state) | |
517 | if ((r = real_add_job(m, JOB_START, dep, ret, true, force, NULL)) < 0) | |
518 | goto fail; | |
519 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state) | |
520 | if ((r = real_add_job(m, JOB_START, dep, ret, !force, force, NULL)) < 0) | |
521 | goto fail; | |
522 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state) | |
523 | if ((r = real_add_job(m, JOB_START, dep, ret, false, force, NULL)) < 0) | |
524 | goto fail; | |
525 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state) | |
526 | if ((r = real_add_job(m, JOB_VERIFY_STARTED, dep, ret, true, force, NULL)) < 0) | |
527 | goto fail; | |
528 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state) | |
529 | if ((r = real_add_job(m, JOB_VERIFY_STARTED, dep, ret, !force, force, NULL)) < 0) | |
530 | goto fail; | |
531 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state) | |
532 | if ((r = real_add_job(m, JOB_STOP, dep, ret, true, force, NULL)) < 0) | |
533 | goto fail; | |
534 | ||
535 | } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { | |
536 | ||
537 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state) | |
538 | if ((r = real_add_job(m, type, dep, ret, true, force, NULL)) < 0) | |
539 | goto fail; | |
540 | } | |
541 | ||
542 | /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */ | |
543 | } | |
60918275 LP |
544 | |
545 | return 0; | |
546 | ||
547 | fail: | |
e5b5ae50 LP |
548 | return r; |
549 | } | |
550 | ||
551 | int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret) { | |
552 | int r; | |
553 | Job *ret; | |
554 | ||
555 | assert(m); | |
556 | assert(type < _JOB_TYPE_MAX); | |
557 | assert(name); | |
558 | assert(mode < _JOB_MODE_MAX); | |
60918275 | 559 | |
e5b5ae50 | 560 | if ((r = real_add_job(m, type, name, NULL, true, force, &ret))) { |
11dd41ce | 561 | transaction_abort(m); |
e5b5ae50 LP |
562 | return r; |
563 | } | |
11dd41ce | 564 | |
e5b5ae50 LP |
565 | if ((r = transaction_activate(m, mode)) < 0) |
566 | return r; | |
567 | ||
568 | if (_ret) | |
569 | *_ret = ret; | |
60918275 | 570 | |
e5b5ae50 LP |
571 | return 0; |
572 | } | |
60918275 LP |
573 | |
574 | Job *manager_get_job(Manager *m, uint32_t id) { | |
575 | assert(m); | |
576 | ||
577 | return hashmap_get(m->jobs, UINT32_TO_PTR(id)); | |
578 | } | |
579 | ||
580 | Name *manager_get_name(Manager *m, const char *name) { | |
581 | assert(m); | |
582 | assert(name); | |
583 | ||
584 | return hashmap_get(m->names, name); | |
585 | } | |
586 | ||
87d1515d LP |
587 | static int verify_type(Name *name) { |
588 | char *n; | |
589 | void *state; | |
60918275 LP |
590 | |
591 | assert(name); | |
592 | ||
87d1515d | 593 | /* Checks that all aliases of this name have the same and valid type */ |
60918275 | 594 | |
87d1515d | 595 | SET_FOREACH(n, name->meta.names, state) { |
60918275 LP |
596 | NameType t; |
597 | ||
87d1515d | 598 | if ((t = name_type_from_string(n)) == _NAME_TYPE_INVALID) |
60918275 LP |
599 | return -EINVAL; |
600 | ||
601 | if (name->meta.type == _NAME_TYPE_INVALID) { | |
602 | name->meta.type = t; | |
603 | continue; | |
604 | } | |
605 | ||
606 | if (name->meta.type != t) | |
607 | return -EINVAL; | |
608 | } | |
609 | ||
87d1515d LP |
610 | if (name->meta.type == _NAME_TYPE_INVALID) |
611 | return -EINVAL; | |
612 | ||
60918275 LP |
613 | return 0; |
614 | } | |
615 | ||
223dabab | 616 | static int service_load_sysv(Service *s) { |
60918275 LP |
617 | assert(s); |
618 | ||
223dabab LP |
619 | /* Load service data from SysV init scripts, preferably with |
620 | * LSB headers ... */ | |
60918275 LP |
621 | |
622 | return 0; | |
623 | } | |
624 | ||
223dabab | 625 | static int name_load_fstab(Name *n) { |
60918275 LP |
626 | assert(n); |
627 | assert(n->meta.type == NAME_MOUNT || n->meta.type == NAME_AUTOMOUNT); | |
628 | ||
223dabab | 629 | /* Load mount data from /etc/fstab */ |
60918275 LP |
630 | |
631 | return 0; | |
632 | } | |
633 | ||
634 | static int snapshot_load(Snapshot *s) { | |
635 | assert(s); | |
636 | ||
223dabab LP |
637 | /* Load snapshots from disk */ |
638 | ||
639 | return 0; | |
640 | } | |
641 | ||
642 | static int name_load_dropin(Name *n) { | |
643 | assert(n); | |
644 | ||
645 | /* Load dependencies from drop-in directories */ | |
60918275 LP |
646 | |
647 | return 0; | |
648 | } | |
649 | ||
650 | static int load(Name *name) { | |
651 | int r; | |
652 | ||
653 | assert(name); | |
654 | ||
655 | if (name->meta.state != NAME_STUB) | |
656 | return 0; | |
657 | ||
87d1515d | 658 | if ((r = verify_type(name)) < 0) |
60918275 LP |
659 | return r; |
660 | ||
661 | if (name->meta.type == NAME_SERVICE) { | |
662 | ||
663 | /* Load a .service file */ | |
223dabab | 664 | if ((r = name_load_fragment(name)) == 0) |
60918275 LP |
665 | goto finish; |
666 | ||
667 | /* Load a classic init script */ | |
668 | if (r == -ENOENT) | |
223dabab | 669 | if ((r = service_load_sysv(SERVICE(name))) == 0) |
60918275 LP |
670 | goto finish; |
671 | ||
672 | } else if (name->meta.type == NAME_MOUNT || | |
673 | name->meta.type == NAME_AUTOMOUNT) { | |
674 | ||
223dabab | 675 | if ((r = name_load_fstab(name)) == 0) |
60918275 LP |
676 | goto finish; |
677 | ||
678 | } else if (name->meta.type == NAME_SNAPSHOT) { | |
679 | ||
680 | if ((r = snapshot_load(SNAPSHOT(name))) == 0) | |
681 | goto finish; | |
682 | ||
683 | } else { | |
223dabab | 684 | if ((r = name_load_fragment(name)) == 0) |
60918275 LP |
685 | goto finish; |
686 | } | |
687 | ||
688 | name->meta.state = NAME_FAILED; | |
689 | return r; | |
690 | ||
691 | finish: | |
223dabab LP |
692 | if ((r = name_load_dropin(name)) < 0) |
693 | return r; | |
694 | ||
e5b5ae50 | 695 | if ((r = name_link_names(name, true)) < 0) |
11dd41ce LP |
696 | return r; |
697 | ||
60918275 LP |
698 | name->meta.state = NAME_LOADED; |
699 | return 0; | |
700 | } | |
701 | ||
702 | static int dispatch_load_queue(Manager *m) { | |
703 | Meta *meta; | |
704 | ||
705 | assert(m); | |
706 | ||
223dabab LP |
707 | /* Make sure we are not run recursively */ |
708 | if (m->dispatching_load_queue) | |
709 | return 0; | |
710 | ||
711 | m->dispatching_load_queue = true; | |
712 | ||
60918275 LP |
713 | /* Dispatches the load queue. Takes a name from the queue and |
714 | * tries to load its data until the queue is empty */ | |
715 | ||
716 | while ((meta = m->load_queue)) { | |
717 | load(NAME(meta)); | |
718 | LIST_REMOVE(Meta, m->load_queue, meta); | |
719 | } | |
720 | ||
223dabab LP |
721 | m->dispatching_load_queue = false; |
722 | ||
60918275 LP |
723 | return 0; |
724 | } | |
725 | ||
60918275 LP |
726 | int manager_load_name(Manager *m, const char *name, Name **_ret) { |
727 | Name *ret; | |
728 | NameType t; | |
729 | int r; | |
87d1515d | 730 | char *n; |
60918275 LP |
731 | |
732 | assert(m); | |
733 | assert(name); | |
734 | assert(_ret); | |
60918275 | 735 | |
223dabab LP |
736 | if (!name_is_valid(name)) |
737 | return -EINVAL; | |
738 | ||
739 | /* This will load the service information files, but not actually | |
740 | * start any services or anything */ | |
60918275 LP |
741 | |
742 | if ((ret = manager_get_name(m, name))) | |
743 | goto finish; | |
744 | ||
745 | if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID) | |
746 | return -EINVAL; | |
747 | ||
748 | if (!(ret = name_new(m))) | |
749 | return -ENOMEM; | |
750 | ||
751 | ret->meta.type = t; | |
752 | ||
87d1515d LP |
753 | if (!(n = strdup(name))) { |
754 | name_free(ret); | |
755 | return -ENOMEM; | |
756 | } | |
757 | ||
758 | if (set_put(ret->meta.names, n) < 0) { | |
60918275 | 759 | name_free(ret); |
87d1515d | 760 | free(n); |
60918275 LP |
761 | return -ENOMEM; |
762 | } | |
763 | ||
764 | if ((r = name_link(ret)) < 0) { | |
765 | name_free(ret); | |
766 | return r; | |
767 | } | |
768 | ||
769 | /* At this point the new entry is created and linked. However, | |
770 | * not loaded. Now load this entry and all its dependencies | |
771 | * recursively */ | |
772 | ||
773 | dispatch_load_queue(m); | |
774 | ||
775 | finish: | |
776 | ||
777 | *_ret = ret; | |
778 | return 0; | |
779 | } | |
a66d02c3 LP |
780 | |
781 | void manager_dump_jobs(Manager *s, FILE *f) { | |
782 | void *state; | |
783 | Job *j; | |
784 | ||
785 | assert(s); | |
786 | assert(f); | |
787 | ||
788 | HASHMAP_FOREACH(j, s->jobs, state) | |
789 | job_dump(j, f); | |
790 | } | |
791 | ||
792 | void manager_dump_names(Manager *s, FILE *f) { | |
793 | void *state; | |
794 | Name *n; | |
11dd41ce | 795 | const char *t; |
a66d02c3 LP |
796 | |
797 | assert(s); | |
798 | assert(f); | |
799 | ||
11dd41ce LP |
800 | HASHMAP_FOREACH_KEY(n, t, s->names, state) |
801 | if (name_id(n) == t) | |
802 | name_dump(n, f); | |
a66d02c3 | 803 | } |