chiark / gitweb /
first attempt in implementinging execution logic
[elogind.git] / manager.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
2
3 #include <assert.h>
4 #include <errno.h>
5 #include <string.h>
6
7 #include "manager.h"
8 #include "hashmap.h"
9 #include "macro.h"
10 #include "strv.h"
11 #include "log.h"
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
25         if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
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;
37         Job *j;
38
39         assert(m);
40
41         while ((n = hashmap_first(m->names)))
42                 name_free(n);
43
44         while ((j = hashmap_steal_first(m->transaction_jobs)))
45                 job_free(j);
46
47         hashmap_free(m->names);
48         hashmap_free(m->jobs);
49         hashmap_free(m->transaction_jobs);
50
51         free(m);
52 }
53
54 static void transaction_delete_job(Manager *m, Job *j) {
55         assert(m);
56         assert(j);
57
58         /* Deletes one job from the transaction */
59
60         manager_transaction_unlink_job(m, j);
61
62         if (!j->linked)
63                 job_free(j);
64 }
65
66 static void transaction_delete_name(Manager *m, Name *n) {
67         Job *j;
68
69         /* Deletes all jobs associated with a certain name from the
70          * transaction */
71
72         while ((j = hashmap_get(m->transaction_jobs, n)))
73                 transaction_delete_job(m, j);
74 }
75
76 static void transaction_abort(Manager *m) {
77         Job *j;
78
79         assert(m);
80
81         while ((j = hashmap_first(m->transaction_jobs)))
82                 if (j->linked)
83                         transaction_delete_job(m, j);
84                 else
85                         job_free(j);
86
87         assert(hashmap_isempty(m->transaction_jobs));
88         assert(!m->transaction_anchor);
89 }
90
91 static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) {
92         JobDependency *l;
93
94         assert(m);
95
96         /* A recursive sweep through the graph that marks all names
97          * that matter to the anchor job, i.e. are directly or
98          * indirectly a dependency of the anchor job via paths that
99          * are fully marked as mattering. */
100
101         for (l = j ? j->subject_list : m->transaction_anchor; l; l = l->subject_next) {
102
103                 /* This link does not matter */
104                 if (!l->matters)
105                         continue;
106
107                 /* This name has already been marked */
108                 if (l->object->generation == generation)
109                         continue;
110
111                 l->object->matters_to_anchor = true;
112                 l->object->generation = generation;
113
114                 transaction_find_jobs_that_matter_to_anchor(m, l->object, generation);
115         }
116 }
117
118 static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) {
119         JobDependency *l, *last;
120
121         assert(j);
122         assert(other);
123         assert(j->name == other->name);
124         assert(!j->linked);
125
126         /* Merges 'other' into 'j' and then deletes j. */
127
128         j->type = t;
129         j->state = JOB_WAITING;
130         j->forced = j->forced || other->forced;
131
132         j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
133
134         /* Patch us in as new owner of the JobDependency objects */
135         last = NULL;
136         for (l = other->subject_list; l; l = l->subject_next) {
137                 assert(l->subject == other);
138                 l->subject = j;
139                 last = l;
140         }
141
142         /* Merge both lists */
143         if (last) {
144                 last->subject_next = j->subject_list;
145                 if (j->subject_list)
146                         j->subject_list->subject_prev = last;
147                 j->subject_list = other->subject_list;
148         }
149
150         /* Patch us in as new owner of the JobDependency objects */
151         last = NULL;
152         for (l = other->object_list; l; l = l->object_next) {
153                 assert(l->object == other);
154                 l->object = j;
155                 last = l;
156         }
157
158         /* Merge both lists */
159         if (last) {
160                 last->object_next = j->object_list;
161                 if (j->object_list)
162                         j->object_list->object_prev = last;
163                 j->object_list = other->object_list;
164         }
165
166         /* Kill the other job */
167         other->subject_list = NULL;
168         other->object_list = NULL;
169         transaction_delete_job(m, other);
170 }
171
172 static int delete_one_unmergeable_job(Manager *m, Job *j) {
173         Job *k;
174
175         assert(j);
176
177         /* Tries to delete one item in the linked list
178          * j->transaction_next->transaction_next->... that conflicts
179          * whith another one, in an attempt to make an inconsistent
180          * transaction work. */
181
182         /* We rely here on the fact that if a merged with b does not
183          * merge with c, either a or b merge with c neither */
184         for (; j; j = j->transaction_next)
185                 for (k = j->transaction_next; k; k = k->transaction_next) {
186                         Job *d;
187
188                         /* Is this one mergeable? Then skip it */
189                         if (job_type_is_mergeable(j->type, k->type))
190                                 continue;
191
192                         /* Ok, we found two that conflict, let's see if we can
193                          * drop one of them */
194                         if (!j->matters_to_anchor)
195                                 d = j;
196                         else if (!k->matters_to_anchor)
197                                 d = k;
198                         else
199                                 return -ENOEXEC;
200
201                         /* Ok, we can drop one, so let's do so. */
202                         log_debug("Try to fix job merging by deleting job %s/%s", name_id(d->name), job_type_to_string(d->type));
203                         transaction_delete_job(m, d);
204                         return 0;
205                 }
206
207         return -EINVAL;
208 }
209
210 static int transaction_merge_jobs(Manager *m) {
211         Job *j;
212         void *state;
213         int r;
214
215         assert(m);
216
217         /* First step, check whether any of the jobs for one specific
218          * task conflict. If so, try to drop one of them. */
219         HASHMAP_FOREACH(j, m->transaction_jobs, state) {
220                 JobType t;
221                 Job *k;
222
223                 t = j->type;
224                 for (k = j->transaction_next; k; k = k->transaction_next) {
225                         if ((r = job_type_merge(&t, k->type)) >= 0)
226                                 continue;
227
228                         /* OK, we could not merge all jobs for this
229                          * action. Let's see if we can get rid of one
230                          * of them */
231
232                         if ((r = delete_one_unmergeable_job(m, j)) >= 0)
233                                 /* Ok, we managed to drop one, now
234                                  * let's ask our callers to call us
235                                  * again after garbage collecting */
236                                 return -EAGAIN;
237
238                         /* We couldn't merge anything. Failure */
239                         return r;
240                 }
241         }
242
243         /* Second step, merge the jobs. */
244         HASHMAP_FOREACH(j, m->transaction_jobs, state) {
245                 JobType t = j->type;
246                 Job *k;
247
248                 /* Merge all transactions */
249                 for (k = j->transaction_next; k; k = k->transaction_next)
250                         assert_se(job_type_merge(&t, k->type) == 0);
251
252                 /* If an active job is mergeable, merge it too */
253                 if (j->name->meta.job)
254                         job_type_merge(&t, j->name->meta.job->type); /* Might fail. Which is OK */
255
256                 while ((k = j->transaction_next)) {
257                         if (j->linked) {
258                                 transaction_merge_and_delete_job(m, k, j, t);
259                                 j = k;
260                         } else
261                                 transaction_merge_and_delete_job(m, j, k, t);
262                 }
263
264                 assert(!j->transaction_next);
265                 assert(!j->transaction_prev);
266         }
267
268         return 0;
269 }
270
271 static bool name_matters_to_anchor(Name *n, Job *j) {
272         assert(n);
273         assert(!j->transaction_prev);
274
275         /* Checks whether at least one of the jobs for this name
276          * matters to the anchor. */
277
278         for (; j; j = j->transaction_next)
279                 if (j->matters_to_anchor)
280                         return true;
281
282         return false;
283 }
284
285 static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) {
286         void *state;
287         Name *n;
288         int r;
289
290         assert(m);
291         assert(j);
292         assert(!j->transaction_prev);
293
294         /* Does a recursive sweep through the ordering graph, looking
295          * for a cycle. If we find cycle we try to break it. */
296
297         /* Did we find a cycle? */
298         if (j->marker && j->generation == generation) {
299                 Job *k;
300
301                 /* So, we already have been here. We have a
302                  * cycle. Let's try to break it. We go backwards in
303                  * our path and try to find a suitable job to
304                  * remove. We use the marker to find our way back,
305                  * since smart how we are we stored our way back in
306                  * there. */
307
308                 for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
309
310                         if (!k->linked &&
311                             !name_matters_to_anchor(k->name, k)) {
312                                 /* Ok, we can drop this one, so let's
313                                  * do so. */
314                                 log_debug("Breaking order cycle by deleting job %s/%s", name_id(k->name), job_type_to_string(k->type));
315                                 transaction_delete_name(m, k->name);
316                                 return -EAGAIN;
317                         }
318
319                         /* Check if this in fact was the beginning of
320                          * the cycle */
321                         if (k == j)
322                                 break;
323                 }
324
325                 return -ENOEXEC;
326         }
327
328         /* Make the marker point to where we come from, so that we can
329          * find our way backwards if we want to break a cycle */
330         j->marker = from;
331         j->generation = generation;
332
333         /* We assume that the the dependencies are bidirectional, and
334          * hence can ignore NAME_AFTER */
335         SET_FOREACH(n, j->name->meta.dependencies[NAME_BEFORE], state) {
336                 Job *o;
337
338                 /* Is there a job for this name? */
339                 if (!(o = hashmap_get(m->transaction_jobs, n)))
340
341                         /* Ok, there is no job for this in the
342                          * transaction, but maybe there is already one
343                          * running? */
344                         if (!(o = n->meta.job))
345                                 continue;
346
347                 if ((r = transaction_verify_order_one(m, o, j, generation)) < 0)
348                         return r;
349         }
350
351         return 0;
352 }
353
354 static int transaction_verify_order(Manager *m, unsigned *generation) {
355         Job *j;
356         int r;
357         void *state;
358
359         assert(m);
360         assert(generation);
361
362         /* Check if the ordering graph is cyclic. If it is, try to fix
363          * that up by dropping one of the jobs. */
364
365         HASHMAP_FOREACH(j, m->transaction_jobs, state)
366                 if ((r = transaction_verify_order_one(m, j, NULL, (*generation)++)) < 0)
367                         return r;
368
369         return 0;
370 }
371
372 static void transaction_collect_garbage(Manager *m) {
373         bool again;
374
375         assert(m);
376
377         /* Drop jobs that are not required by any other job */
378
379         do {
380                 void *state;
381                 Job *j;
382
383                 again = false;
384
385                 HASHMAP_FOREACH(j, m->transaction_jobs, state) {
386                         if (j->object_list)
387                                 continue;
388
389                         log_debug("Garbage collecting job %s/%s", name_id(j->name), job_type_to_string(j->type));
390                         transaction_delete_job(m, j);
391                         again = true;
392                         break;
393                 }
394
395         } while (again);
396 }
397
398 static int transaction_is_destructive(Manager *m, JobMode mode) {
399         void *state;
400         Job *j;
401
402         assert(m);
403
404         /* Checks whether applying this transaction means that
405          * existing jobs would be replaced */
406
407         HASHMAP_FOREACH(j, m->transaction_jobs, state) {
408
409                 /* Assume merged */
410                 assert(!j->transaction_prev);
411                 assert(!j->transaction_next);
412
413                 if (j->name->meta.job &&
414                     j->name->meta.job != j &&
415                     !job_type_is_superset(j->type, j->name->meta.job->type))
416                         return -EEXIST;
417         }
418
419         return 0;
420 }
421
422 static void transaction_minimize_impact(Manager *m) {
423         bool again;
424         assert(m);
425
426         /* Drops all unnecessary jobs that reverse already active jobs
427          * or that stop a running service. */
428
429         do {
430                 Job *j;
431                 void *state;
432
433                 again = false;
434
435                 HASHMAP_FOREACH(j, m->transaction_jobs, state) {
436                         for (; j; j = j->transaction_next) {
437
438                                 /* If it matters, we shouldn't drop it */
439                                 if (j->matters_to_anchor)
440                                         continue;
441
442                                 /* Would this stop a running service?
443                                  * Would this change an existing job?
444                                  * If so, let's drop this entry */
445                                 if ((j->type != JOB_STOP || NAME_IS_INACTIVE_OR_DEACTIVATING(name_active_state(j->name))) &&
446                                     (!j->name->meta.job  || job_type_is_conflicting(j->type, j->name->meta.job->state)))
447                                         continue;
448
449                                 /* Ok, let's get rid of this */
450                                 log_debug("Deleting %s/%s to minimize impact", name_id(j->name), job_type_to_string(j->type));
451                                 transaction_delete_job(m, j);
452                                 again = true;
453                                 break;
454                         }
455
456                         if (again)
457                                 break;
458                 }
459
460         } while (again);
461 }
462
463 static int transaction_apply(Manager *m, JobMode mode) {
464         void *state;
465         Job *j;
466         int r;
467
468         /* Moves the transaction jobs to the set of active jobs */
469
470         HASHMAP_FOREACH(j, m->transaction_jobs, state) {
471                 /* Assume merged */
472                 assert(!j->transaction_prev);
473                 assert(!j->transaction_next);
474
475                 if (j->linked)
476                         continue;
477
478                 if ((r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j)) < 0)
479                         goto rollback;
480         }
481
482         while ((j = hashmap_steal_first(m->transaction_jobs))) {
483                 if (j->linked)
484                         continue;
485
486                 if (j->name->meta.job)
487                         job_free(j->name->meta.job);
488
489                 j->name->meta.job = j;
490                 j->linked = true;
491
492                 /* We're fully installed. Now let's free data we don't
493                  * need anymore. */
494
495                 assert(!j->transaction_next);
496                 assert(!j->transaction_prev);
497
498                 while (j->subject_list)
499                         job_dependency_free(j->subject_list);
500                 while (j->object_list)
501                         job_dependency_free(j->object_list);
502         }
503
504         m->transaction_anchor = NULL;
505
506         return 0;
507
508 rollback:
509
510         HASHMAP_FOREACH(j, m->transaction_jobs, state) {
511                 if (j->linked)
512                         continue;
513
514                 hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
515         }
516
517         return r;
518 }
519
520 static int transaction_activate(Manager *m, JobMode mode) {
521         int r;
522         unsigned generation = 1;
523
524         assert(m);
525
526         /* This applies the changes recorded in transaction_jobs to
527          * the actual list of jobs, if possible. */
528
529         /* First step: figure out which jobs matter */
530         transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++);
531
532         /* Second step: Try not to stop any running services if
533          * we don't have to. Don't try to reverse running
534          * jobs if we don't have to. */
535         transaction_minimize_impact(m);
536
537         for (;;) {
538                 /* Third step: Let's remove unneeded jobs that might
539                  * be lurking. */
540                 transaction_collect_garbage(m);
541
542                 /* Fourth step: verify order makes sense and correct
543                  * cycles if necessary and possible */
544                 if ((r = transaction_verify_order(m, &generation)) >= 0)
545                         break;
546
547                 if (r != -EAGAIN)
548                         goto rollback;
549
550                 /* Let's see if the resulting transaction ordering
551                  * graph is still cyclic... */
552         }
553
554         for (;;) {
555                 /* Fifth step: let's drop unmergeable entries if
556                  * necessary and possible, merge entries we can
557                  * merge */
558                 if ((r = transaction_merge_jobs(m)) >= 0)
559                         break;
560
561                 if (r != -EAGAIN)
562                         goto rollback;
563
564                 /* Sixth step: an entry got dropped, let's garbage
565                  * collect its dependencies. */
566                 transaction_collect_garbage(m);
567
568                 /* Let's see if the resulting transaction still has
569                  * unmergeable entries ... */
570         }
571
572         /* Seventh step: check whether we can actually apply this */
573         if (mode == JOB_FAIL)
574                 if ((r = transaction_is_destructive(m, mode)) < 0)
575                         goto rollback;
576
577         /* Eights step: apply changes */
578         if ((r = transaction_apply(m, mode)) < 0)
579                 goto rollback;
580
581         assert(hashmap_isempty(m->transaction_jobs));
582         assert(!m->transaction_anchor);
583
584         return 0;
585
586 rollback:
587         transaction_abort(m);
588         return r;
589 }
590
591 static Job* transaction_add_one_job(Manager *m, JobType type, Name *name, bool force, bool *is_new) {
592         Job *j, *f;
593         int r;
594
595         assert(m);
596         assert(name);
597
598         /* Looks for an axisting prospective job and returns that. If
599          * it doesn't exist it is created and added to the prospective
600          * jobs list. */
601
602         f = hashmap_get(m->transaction_jobs, name);
603
604         for (j = f; j; j = j->transaction_next) {
605                 assert(j->name == name);
606
607                 if (j->type == type) {
608                         if (is_new)
609                                 *is_new = false;
610                         return j;
611                 }
612         }
613
614         if (name->meta.job && name->meta.job->type == type)
615                 j = name->meta.job;
616         else if (!(j = job_new(m, type, name)))
617                 return NULL;
618
619         if ((r = hashmap_replace(m->transaction_jobs, name, j)) < 0) {
620                 job_free(j);
621                 return NULL;
622         }
623
624         j->transaction_next = f;
625
626         if (f)
627                 f->transaction_prev = j;
628
629         j->generation = 0;
630         j->marker = NULL;
631         j->matters_to_anchor = false;
632         j->forced = force;
633
634         if (is_new)
635                 *is_new = true;
636
637         return j;
638 }
639
640 void manager_transaction_unlink_job(Manager *m, Job *j) {
641         assert(m);
642         assert(j);
643
644         if (j->transaction_prev)
645                 j->transaction_prev->transaction_next = j->transaction_next;
646         else if (j->transaction_next)
647                 hashmap_replace(m->transaction_jobs, j->name, j->transaction_next);
648         else
649                 hashmap_remove_value(m->transaction_jobs, j->name, j);
650
651         if (j->transaction_next)
652                 j->transaction_next->transaction_prev = j->transaction_prev;
653
654         j->transaction_prev = j->transaction_next = NULL;
655
656         while (j->subject_list)
657                 job_dependency_free(j->subject_list);
658
659         while (j->object_list) {
660                 Job *other = j->object_list->matters ? j->object_list->subject : NULL;
661
662                 job_dependency_free(j->object_list);
663
664                 if (other) {
665                         log_debug("Deleting job %s/%s as dependency of job %s/%s",
666                                   name_id(other->name), job_type_to_string(other->type),
667                                   name_id(j->name), job_type_to_string(j->type));
668                         transaction_delete_job(m, other);
669                 }
670         }
671 }
672
673 static int transaction_add_job_and_dependencies(Manager *m, JobType type, Name *name, Job *by, bool matters, bool force, Job **_ret) {
674         Job *ret;
675         void *state;
676         Name *dep;
677         int r;
678         bool is_new;
679
680         assert(m);
681         assert(type < _JOB_TYPE_MAX);
682         assert(name);
683
684         if (name->meta.load_state != NAME_LOADED)
685                 return -EINVAL;
686
687         if (!job_type_is_applicable(type, name->meta.type))
688                 return -EBADR;
689
690         /* First add the job. */
691         if (!(ret = transaction_add_one_job(m, type, name, force, &is_new)))
692                 return -ENOMEM;
693
694         /* Then, add a link to the job. */
695         if (!job_dependency_new(by, ret, matters))
696                 return -ENOMEM;
697
698         if (is_new) {
699                 /* Finally, recursively add in all dependencies. */
700                 if (type == JOB_START || type == JOB_RELOAD_OR_START) {
701                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRES], state)
702                                 if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL))  != -EBADR)
703                                         goto fail;
704                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUIRES], state)
705                                 if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) != -EBADR)
706                                         goto fail;
707                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_WANTS], state)
708                                 if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) != -EBADR)
709                                         goto fail;
710                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUISITE], state)
711                                 if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) != -EBADR)
712                                         goto fail;
713                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_SOFT_REQUISITE], state)
714                                 if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) != -EBADR)
715                                         goto fail;
716                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_CONFLICTS], state)
717                                 if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) != -EBADR)
718                                         goto fail;
719
720                 } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {
721
722                         SET_FOREACH(dep, ret->name->meta.dependencies[NAME_REQUIRED_BY], state)
723                                 if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL))  != -EBADR)
724                                         goto fail;
725                 }
726
727                 /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
728         }
729
730         return 0;
731
732 fail:
733         return r;
734 }
735
736 int manager_add_job(Manager *m, JobType type, Name *name, JobMode mode, bool force, Job **_ret) {
737         int r;
738         Job *ret;
739
740         assert(m);
741         assert(type < _JOB_TYPE_MAX);
742         assert(name);
743         assert(mode < _JOB_MODE_MAX);
744
745         if ((r = transaction_add_job_and_dependencies(m, type, name, NULL, true, force, &ret))) {
746                 transaction_abort(m);
747                 return r;
748         }
749
750         if ((r = transaction_activate(m, mode)) < 0)
751                 return r;
752
753         if (_ret)
754                 *_ret = ret;
755
756         return 0;
757 }
758
759 Job *manager_get_job(Manager *m, uint32_t id) {
760         assert(m);
761
762         return hashmap_get(m->jobs, UINT32_TO_PTR(id));
763 }
764
765 Name *manager_get_name(Manager *m, const char *name) {
766         assert(m);
767         assert(name);
768
769         return hashmap_get(m->names, name);
770 }
771
772 static int dispatch_load_queue(Manager *m) {
773         Meta *meta;
774
775         assert(m);
776
777         /* Make sure we are not run recursively */
778         if (m->dispatching_load_queue)
779                 return 0;
780
781         m->dispatching_load_queue = true;
782
783         /* Dispatches the load queue. Takes a name from the queue and
784          * tries to load its data until the queue is empty */
785
786         while ((meta = m->load_queue)) {
787                 name_load(NAME(meta));
788                 LIST_REMOVE(Meta, m->load_queue, meta);
789         }
790
791         m->dispatching_load_queue = false;
792
793         return 0;
794 }
795
796 int manager_load_name(Manager *m, const char *name, Name **_ret) {
797         Name *ret;
798         NameType t;
799         int r;
800         char *n;
801
802         assert(m);
803         assert(name);
804         assert(_ret);
805
806         if (!name_is_valid(name))
807                 return -EINVAL;
808
809         /* This will load the service information files, but not actually
810          * start any services or anything */
811
812         if ((ret = manager_get_name(m, name)))
813                 goto finish;
814
815         if ((t = name_type_from_string(name)) == _NAME_TYPE_INVALID)
816                 return -EINVAL;
817
818         if (!(ret = name_new(m)))
819                 return -ENOMEM;
820
821         ret->meta.type = t;
822
823         if (!(n = strdup(name))) {
824                 name_free(ret);
825                 return -ENOMEM;
826         }
827
828         if ((r = set_put(ret->meta.names, n)) < 0) {
829                 name_free(ret);
830                 free(n);
831                 return r;
832         }
833
834         if ((r = name_link(ret)) < 0) {
835                 name_free(ret);
836                 return r;
837         }
838
839         /* At this point the new entry is created and linked. However,
840          * not loaded. Now load this entry and all its dependencies
841          * recursively */
842
843         dispatch_load_queue(m);
844
845 finish:
846
847         *_ret = ret;
848         return 0;
849 }
850
851 void manager_dump_jobs(Manager *s, FILE *f, const char *prefix) {
852         void *state;
853         Job *j;
854
855         assert(s);
856         assert(f);
857
858         HASHMAP_FOREACH(j, s->jobs, state)
859                 job_dump(j, f, prefix);
860 }
861
862 void manager_dump_names(Manager *s, FILE *f, const char *prefix) {
863         void *state;
864         Name *n;
865         const char *t;
866
867         assert(s);
868         assert(f);
869
870         HASHMAP_FOREACH_KEY(n, t, s->names, state)
871                 if (name_id(n) == t)
872                         name_dump(n, f, prefix);
873 }
874
875 void manager_clear_jobs(Manager *m) {
876         Job *j;
877
878         assert(m);
879
880         transaction_abort(m);
881
882         while ((j = hashmap_first(m->jobs)))
883                 job_free(j);
884 }