]>
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" | |
16354eff | 11 | #include "log.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 | ||
7fad411c | 125 | static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) { |
e5b5ae50 LP |
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 | ||
e5b5ae50 LP |
170 | /* Kill the other job */ |
171 | other->subject_list = NULL; | |
172 | other->object_list = NULL; | |
173 | manager_transaction_delete_job(m, other); | |
174 | } | |
175 | ||
176 | static int transaction_merge_jobs(Manager *m) { | |
11dd41ce | 177 | Job *j; |
e5b5ae50 LP |
178 | void *state; |
179 | int r; | |
180 | ||
181 | assert(m); | |
182 | ||
183 | HASHMAP_FOREACH(j, m->transaction_jobs, state) { | |
184 | JobType t = j->type; | |
185 | Job *k; | |
186 | ||
187 | for (k = j->transaction_next; k; k = k->transaction_next) | |
188 | if ((r = types_merge(&t, k->type)) < 0) | |
189 | return r; | |
190 | ||
191 | while ((k = j->transaction_next)) { | |
192 | if (j->linked) { | |
7fad411c | 193 | transaction_merge_and_delete_job(m, k, j, t); |
e5b5ae50 LP |
194 | j = k; |
195 | } else | |
7fad411c | 196 | transaction_merge_and_delete_job(m, j, k, t); |
e5b5ae50 LP |
197 | } |
198 | ||
199 | assert(!j->transaction_next); | |
200 | assert(!j->transaction_prev); | |
201 | } | |
202 | ||
7fad411c | 203 | return 0; |
e5b5ae50 LP |
204 | } |
205 | ||
206 | static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) { | |
207 | void *state; | |
208 | Name *n; | |
11dd41ce | 209 | int r; |
e5b5ae50 LP |
210 | |
211 | assert(m); | |
212 | assert(j); | |
213 | ||
7fad411c | 214 | /* Did we find a cycle? */ |
e5b5ae50 LP |
215 | if (j->marker && j->generation == generation) { |
216 | Job *k; | |
217 | ||
218 | /* So, we already have been here. We have a | |
7fad411c | 219 | * cycle. Let's try to break it. We go backwards in our |
e5b5ae50 LP |
220 | * path and try to find a suitable job to remove. */ |
221 | ||
222 | for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) { | |
223 | if (!k->matters_to_anchor) { | |
16354eff | 224 | log_debug("Breaking order cycle by deleting job %s", name_id(k->name)); |
e5b5ae50 LP |
225 | manager_transaction_delete_job(m, k); |
226 | return -EAGAIN; | |
227 | } | |
228 | ||
229 | /* Check if this in fact was the beginning of | |
7fad411c | 230 | * the cycle */ |
e5b5ae50 LP |
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 | ||
7fad411c | 278 | /* There was a cycleq, but it was fixed, |
e5b5ae50 LP |
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 | ||
ceed3570 | 424 | static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool *is_new) { |
e5b5ae50 | 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); | |
1e198baf LP |
490 | |
491 | while (j->object_list) { | |
492 | Job *other = j->object_list->matters ? j->object_list->subject : NULL; | |
493 | ||
e5b5ae50 | 494 | job_dependency_free(j->object_list); |
1e198baf LP |
495 | |
496 | if (other) { | |
497 | log_debug("Deleting job %s, as dependency of job %s", name_id(j->name), name_id(other->name)); | |
498 | manager_transaction_delete_job(m, other); | |
499 | } | |
500 | } | |
e5b5ae50 LP |
501 | } |
502 | ||
ceed3570 | 503 | static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *name, Job *by, bool matters, bool force, Job **_ret) { |
e5b5ae50 LP |
504 | Job *ret; |
505 | void *state; | |
506 | Name *dep; | |
507 | int r; | |
508 | bool is_new; | |
509 | ||
510 | assert(m); | |
511 | assert(type < _JOB_TYPE_MAX); | |
512 | assert(name); | |
513 | ||
514 | /* First add the job. */ | |
ceed3570 | 515 | if (!(ret = transaction_add_one_job(m, type, name, &is_new))) |
e5b5ae50 LP |
516 | return -ENOMEM; |
517 | ||
518 | /* Then, add a link to the job. */ | |
519 | if (!job_dependency_new(by, ret, matters)) | |
520 | return -ENOMEM; | |
521 | ||
522 | if (is_new) { | |
523 | /* Finally, recursively add in all dependencies. */ | |
524 | if (type == JOB_START || type == JOB_RELOAD_OR_START) { | |
525 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state) | |
ceed3570 | 526 | if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0) |
e5b5ae50 LP |
527 | goto fail; |
528 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state) | |
ceed3570 | 529 | if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0) |
e5b5ae50 LP |
530 | goto fail; |
531 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state) | |
ceed3570 | 532 | if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0) |
e5b5ae50 LP |
533 | goto fail; |
534 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state) | |
ceed3570 | 535 | if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_STARTED, dep, ret, true, force, NULL)) < 0) |
e5b5ae50 LP |
536 | goto fail; |
537 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state) | |
ceed3570 | 538 | if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_STARTED, dep, ret, !force, force, NULL)) < 0) |
e5b5ae50 LP |
539 | goto fail; |
540 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state) | |
ceed3570 | 541 | if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0) |
e5b5ae50 LP |
542 | goto fail; |
543 | ||
544 | } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) { | |
545 | ||
546 | SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state) | |
ceed3570 | 547 | if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0) |
e5b5ae50 LP |
548 | goto fail; |
549 | } | |
550 | ||
551 | /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */ | |
552 | } | |
60918275 LP |
553 | |
554 | return 0; | |
555 | ||
556 | fail: | |
e5b5ae50 LP |
557 | return r; |
558 | } | |
559 | ||
560 | int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret) { | |
561 | int r; | |
562 | Job *ret; | |
563 | ||
564 | assert(m); | |
565 | assert(type < _JOB_TYPE_MAX); | |
566 | assert(name); | |
567 | assert(mode < _JOB_MODE_MAX); | |
60918275 | 568 | |
ceed3570 | 569 | if ((r = transaction_add_job_and_dependencies(m, type, name, NULL, true, force, &ret))) { |
11dd41ce | 570 | transaction_abort(m); |
e5b5ae50 LP |
571 | return r; |
572 | } | |
11dd41ce | 573 | |
e5b5ae50 LP |
574 | if ((r = transaction_activate(m, mode)) < 0) |
575 | return r; | |
576 | ||
577 | if (_ret) | |
578 | *_ret = ret; | |
60918275 | 579 | |
e5b5ae50 LP |
580 | return 0; |
581 | } | |
60918275 LP |
582 | |
583 | Job *manager_get_job(Manager *m, uint32_t id) { | |
584 | assert(m); | |
585 | ||
586 | return hashmap_get(m->jobs, UINT32_TO_PTR(id)); | |
587 | } | |
588 | ||
589 | Name *manager_get_name(Manager *m, const char *name) { | |
590 | assert(m); | |
591 | assert(name); | |
592 | ||
593 | return hashmap_get(m->names, name); | |
594 | } | |
595 | ||
60918275 LP |
596 | static int dispatch_load_queue(Manager *m) { |
597 | Meta *meta; | |
598 | ||
599 | assert(m); | |
600 | ||
223dabab LP |
601 | /* Make sure we are not run recursively */ |
602 | if (m->dispatching_load_queue) | |
603 | return 0; | |
604 | ||
605 | m->dispatching_load_queue = true; | |
606 | ||
60918275 LP |
607 | /* Dispatches the load queue. Takes a name from the queue and |
608 | * tries to load its data until the queue is empty */ | |
609 | ||
610 | while ((meta = m->load_queue)) { | |
7fad411c | 611 | name_load(NAME(meta)); |
60918275 LP |
612 | LIST_REMOVE(Meta, m->load_queue, meta); |
613 | } | |
614 | ||
223dabab LP |
615 | m->dispatching_load_queue = false; |
616 | ||
60918275 LP |
617 | return 0; |
618 | } | |
619 | ||
60918275 LP |
620 | int manager_load_name(Manager *m, const char *name, Name **_ret) { |
621 | Name *ret; | |
622 | NameType t; | |
623 | int r; | |
87d1515d | 624 | char *n; |
60918275 LP |
625 | |
626 | assert(m); | |
627 | assert(name); | |
628 | assert(_ret); | |
60918275 | 629 | |
223dabab LP |
630 | if (!name_is_valid(name)) |
631 | return -EINVAL; | |
632 | ||
633 | /* This will load the service information files, but not actually | |
634 | * start any services or anything */ | |
60918275 LP |
635 | |
636 | if ((ret = manager_get_name(m, name))) | |
637 | goto finish; | |
638 | ||
639 | if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID) | |
640 | return -EINVAL; | |
641 | ||
642 | if (!(ret = name_new(m))) | |
643 | return -ENOMEM; | |
644 | ||
645 | ret->meta.type = t; | |
646 | ||
87d1515d LP |
647 | if (!(n = strdup(name))) { |
648 | name_free(ret); | |
649 | return -ENOMEM; | |
650 | } | |
651 | ||
652 | if (set_put(ret->meta.names, n) < 0) { | |
60918275 | 653 | name_free(ret); |
87d1515d | 654 | free(n); |
60918275 LP |
655 | return -ENOMEM; |
656 | } | |
657 | ||
658 | if ((r = name_link(ret)) < 0) { | |
659 | name_free(ret); | |
660 | return r; | |
661 | } | |
662 | ||
663 | /* At this point the new entry is created and linked. However, | |
664 | * not loaded. Now load this entry and all its dependencies | |
665 | * recursively */ | |
666 | ||
667 | dispatch_load_queue(m); | |
668 | ||
669 | finish: | |
670 | ||
671 | *_ret = ret; | |
672 | return 0; | |
673 | } | |
a66d02c3 | 674 | |
cea8e32e | 675 | void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) { |
a66d02c3 LP |
676 | void *state; |
677 | Job *j; | |
678 | ||
679 | assert(s); | |
680 | assert(f); | |
681 | ||
682 | HASHMAP_FOREACH(j, s->jobs, state) | |
cea8e32e | 683 | job_dump(j, f, prefix); |
a66d02c3 LP |
684 | } |
685 | ||
cea8e32e | 686 | void manager_dump_names(Manager *s, FILE *f, const char *prefix) { |
a66d02c3 LP |
687 | void *state; |
688 | Name *n; | |
11dd41ce | 689 | const char *t; |
a66d02c3 LP |
690 | |
691 | assert(s); | |
692 | assert(f); | |
693 | ||
11dd41ce LP |
694 | HASHMAP_FOREACH_KEY(n, t, s->names, state) |
695 | if (name_id(n) == t) | |
cea8e32e | 696 | name_dump(n, f, prefix); |
a66d02c3 | 697 | } |
7fad411c LP |
698 | |
699 | void manager_clear_jobs(Manager *m) { | |
700 | Job *j; | |
701 | ||
702 | assert(m); | |
703 | ||
704 | transaction_abort(m); | |
705 | ||
706 | while ((j = hashmap_first(m->jobs))) | |
707 | job_free(j); | |
708 | } |