manager.c 51.2 KB
Newer Older
Lennart Poettering's avatar
Lennart Poettering committed
1
2
/*-*- Mode: C; c-basic-offset: 8 -*-*/

3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/***
  This file is part of systemd.

  Copyright 2010 Lennart Poettering

  systemd is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  systemd is distributed in the hope that it will be useful, but
  WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/

Lennart Poettering's avatar
Lennart Poettering committed
22
23
#include <assert.h>
#include <errno.h>
24
#include <string.h>
Lennart Poettering's avatar
Lennart Poettering committed
25
26
27
28
29
30
#include <sys/epoll.h>
#include <signal.h>
#include <sys/signalfd.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/poll.h>
31
32
33
#include <sys/reboot.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
34
#include <libcgroup.h>
Lennart Poettering's avatar
Lennart Poettering committed
35
36
37
38
39

#include "manager.h"
#include "hashmap.h"
#include "macro.h"
#include "strv.h"
40
#include "log.h"
41
#include "util.h"
42
#include "ratelimit.h"
43
44
#include "cgroup.h"
#include "mount-setup.h"
Lennart Poettering's avatar
Lennart Poettering committed
45

46
static int manager_setup_signals(Manager *m) {
Lennart Poettering's avatar
Lennart Poettering committed
47
48
        sigset_t mask;
        struct epoll_event ev;
Lennart Poettering's avatar
Lennart Poettering committed
49

50
51
52
53
54
55
56
57
58
59
60
61
62
        assert(m);

        assert_se(reset_all_signal_handlers() == 0);

        assert_se(sigemptyset(&mask) == 0);
        assert_se(sigaddset(&mask, SIGCHLD) == 0);
        assert_se(sigaddset(&mask, SIGINT) == 0);   /* Kernel sends us this on control-alt-del */
        assert_se(sigaddset(&mask, SIGWINCH) == 0); /* Kernel sends us this on kbrequest (alt-arrowup) */
        assert_se(sigaddset(&mask, SIGTERM) == 0);
        assert_se(sigaddset(&mask, SIGHUP) == 0);
        assert_se(sigaddset(&mask, SIGUSR1) == 0);
        assert_se(sigaddset(&mask, SIGUSR2) == 0);
        assert_se(sigaddset(&mask, SIGPIPE) == 0);
63
64
        assert_se(sigaddset(&mask, SIGPWR) == 0);
        assert_se(sigaddset(&mask, SIGTTIN) == 0);
65
66
        assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);

Lennart Poettering's avatar
Lennart Poettering committed
67
        m->signal_watch.type = WATCH_SIGNAL;
68
69
70
71
72
73
74
75
76
77
        if ((m->signal_watch.fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0)
                return -errno;

        zero(ev);
        ev.events = EPOLLIN;
        ev.data.ptr = &m->signal_watch;

        if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->signal_watch.fd, &ev) < 0)
                return -errno;

78
79
80
81
82
83
84
85
86
87
        if (m->running_as == MANAGER_INIT) {
                /* Enable that we get SIGINT on control-alt-del */
                if (reboot(RB_DISABLE_CAD) < 0)
                        log_warning("Failed to enable ctrl-alt-del handling: %s", strerror(errno));

                /* Enable that we get SIGWINCH on kbrequest */
                if (ioctl(0, KDSIGACCEPT, SIGWINCH) < 0)
                        log_warning("Failed to enable kbrequest handling: %s", strerror(errno));
        }

88
89
90
        return 0;
}

91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
static char** session_dirs(void) {
        const char *home, *e;
        char *config_home = NULL, *data_home = NULL;
        char **config_dirs = NULL, **data_dirs = NULL;
        char **r = NULL, **t;

        /* Implement the mechanisms defined in
         *
         * http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
         *
         * We look in both the config and the data dirs because we
         * want to encourage that distributors ship their unit files
         * as data, and allow overriding as configuration.
         */

        home = getenv("HOME");

        if ((e = getenv("XDG_CONFIG_HOME"))) {
                if (asprintf(&config_home, "%s/systemd/session", e) < 0)
                        goto fail;

        } else if (home) {
                if (asprintf(&config_home, "%s/.config/systemd/session", home) < 0)
                        goto fail;
        }

        if ((e = getenv("XDG_CONFIG_DIRS")))
                config_dirs = strv_split(e, ":");
        else
                config_dirs = strv_new("/etc/xdg", NULL);

        if (!config_dirs)
                goto fail;

        if ((e = getenv("XDG_DATA_HOME"))) {
                if (asprintf(&data_home, "%s/systemd/session", e) < 0)
                        goto fail;

        } else if (home) {
                if (asprintf(&data_home, "%s/.local/share/systemd/session", home) < 0)
                        goto fail;
        }

        if ((e = getenv("XDG_DATA_DIRS")))
                data_dirs = strv_split(e, ":");
        else
                data_dirs = strv_new("/usr/local/share", "/usr/share", NULL);

        if (!data_dirs)
                goto fail;

        /* Now merge everything we found. */
        if (config_home) {
                if (!(t = strv_append(r, config_home)))
                        goto fail;
                strv_free(r);
                r = t;
        }

        if (!(t = strv_merge_concat(r, config_dirs, "/systemd/session")))
                goto finish;
        strv_free(r);
        r = t;

        if (!(t = strv_append(r, SESSION_CONFIG_UNIT_PATH)))
                goto fail;
        strv_free(r);
        r = t;

        if (data_home) {
                if (!(t = strv_append(r, data_home)))
                        goto fail;
                strv_free(r);
                r = t;
        }

        if (!(t = strv_merge_concat(r, data_dirs, "/systemd/session")))
                goto fail;
        strv_free(r);
        r = t;

        if (!(t = strv_append(r, SESSION_DATA_UNIT_PATH)))
                goto fail;
        strv_free(r);
        r = t;

        if (!strv_path_make_absolute_cwd(r))
            goto fail;

finish:
        free(config_home);
        strv_free(config_dirs);
        free(data_home);
        strv_free(data_dirs);

        return r;

fail:
        strv_free(r);
        r = NULL;
        goto finish;
}

static int manager_find_paths(Manager *m) {
        const char *e;
        char *t;
197

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
        assert(m);

        /* First priority is whatever has been passed to us via env
         * vars */
        if ((e = getenv("SYSTEMD_UNIT_PATH")))
                if (!(m->unit_path = split_path_and_make_absolute(e)))
                        return -ENOMEM;

        if (strv_isempty(m->unit_path)) {

                /* Nothing is set, so let's figure something out. */
                strv_free(m->unit_path);

                if (m->running_as == MANAGER_SESSION) {
                        if (!(m->unit_path = session_dirs()))
                                return -ENOMEM;
                } else
                        if (!(m->unit_path = strv_new(
                                              SYSTEM_CONFIG_UNIT_PATH,  /* /etc/systemd/system/ */
                                              SYSTEM_DATA_UNIT_PATH,    /* /lib/systemd/system/ */
                                              NULL)))
                                return -ENOMEM;
        }

222
        if (m->running_as == MANAGER_INIT) {
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
                /* /etc/init.d/ compativility does not matter to users */

                if ((e = getenv("SYSTEMD_SYSVINIT_PATH")))
                        if (!(m->sysvinit_path = split_path_and_make_absolute(e)))
                                return -ENOMEM;

                if (strv_isempty(m->sysvinit_path)) {
                        strv_free(m->sysvinit_path);

                        if (!(m->sysvinit_path = strv_new(
                                              SYSTEM_SYSVINIT_PATH,     /* /etc/init.d/ */
                                              NULL)))
                                return -ENOMEM;
                }
        }

        strv_uniq(m->unit_path);
        strv_uniq(m->sysvinit_path);

        assert(!strv_isempty(m->unit_path));
        if (!(t = strv_join(m->unit_path, "\n\t")))
                return -ENOMEM;
        log_debug("Looking for unit files in:\n\t%s", t);
        free(t);

        if (!strv_isempty(m->sysvinit_path)) {

                if (!(t = strv_join(m->sysvinit_path, "\n\t")))
                        return -ENOMEM;

                log_debug("Looking for SysV init scripts in:\n\t%s", t);
                free(t);
        } else
                log_debug("Ignoring SysV init scripts.");

        return 0;
}

261
int manager_new(Manager **_m) {
262
        Manager *m;
263
264
265
        int r = -ENOMEM;

        assert(_m);
266

Lennart Poettering's avatar
Lennart Poettering committed
267
        if (!(m = new0(Manager, 1)))
268
                return -ENOMEM;
Lennart Poettering's avatar
Lennart Poettering committed
269

270
        m->signal_watch.fd = m->mount_watch.fd = m->udev_watch.fd = m->epoll_fd = -1;
271
        m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */
Lennart Poettering's avatar
Lennart Poettering committed
272

Lennart Poettering's avatar
Lennart Poettering committed
273
        if (!(m->units = hashmap_new(string_hash_func, string_compare_func)))
Lennart Poettering's avatar
Lennart Poettering committed
274
275
276
277
278
                goto fail;

        if (!(m->jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
                goto fail;

279
        if (!(m->transaction_jobs = hashmap_new(trivial_hash_func, trivial_compare_func)))
Lennart Poettering's avatar
Lennart Poettering committed
280
281
                goto fail;

Lennart Poettering's avatar
Lennart Poettering committed
282
283
284
        if (!(m->watch_pids = hashmap_new(trivial_hash_func, trivial_compare_func)))
                goto fail;

285
286
287
        if (!(m->cgroup_bondings = hashmap_new(string_hash_func, string_compare_func)))
                goto fail;

Lennart Poettering's avatar
Lennart Poettering committed
288
289
290
        if ((m->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0)
                goto fail;

291
292
293
294
295
296
297
298
299
        if (getpid() == 1)
                m->running_as = MANAGER_INIT;
        else if (getuid() == 0)
                m->running_as = MANAGER_SYSTEM;
        else
                m->running_as = MANAGER_SESSION;

        log_debug("systemd running in %s mode.", manager_running_as_to_string(m->running_as));

300
        if ((r = manager_find_paths(m)) < 0)
301
302
303
304
305
306
307
308
                goto fail;

        if (chdir("/") < 0)
                log_warning("Failed to chdir to /: %s", strerror(errno));

        /* Become a session leader if we aren't one yet. */
        setsid();

309
310
311
312
313
314
315
        if ((r = manager_setup_signals(m)) < 0)
                goto fail;

        if ((r = mount_setup()) < 0)
                goto fail;

        if ((r = manager_setup_cgroup(m)) < 0)
Lennart Poettering's avatar
Lennart Poettering committed
316
317
                goto fail;

318
319
320
321
322
        dbus_connection_set_change_sigpipe(FALSE);

        /* Try to connect to the busses, if possible. */
        if ((r = bus_init_system(m)) < 0 ||
            (r = bus_init_api(m)) < 0)
323
324
                goto fail;

325
326
        *_m = m;
        return 0;
Lennart Poettering's avatar
Lennart Poettering committed
327
328
329

fail:
        manager_free(m);
330
        return r;
Lennart Poettering's avatar
Lennart Poettering committed
331
332
}

333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
static unsigned manager_dispatch_cleanup_queue(Manager *m) {
        Meta *meta;
        unsigned n = 0;

        assert(m);

        while ((meta = m->cleanup_queue)) {
                assert(meta->in_cleanup_queue);

                unit_free(UNIT(meta));
                n++;
        }

        return n;
}

Lennart Poettering's avatar
Lennart Poettering committed
349
void manager_free(Manager *m) {
350
        UnitType c;
Lennart Poettering's avatar
Lennart Poettering committed
351
        Unit *u;
352
        Job *j;
Lennart Poettering's avatar
Lennart Poettering committed
353
354
355

        assert(m);

Lennart Poettering's avatar
Lennart Poettering committed
356
        while ((j = hashmap_first(m->transaction_jobs)))
357
358
                job_free(j);

Lennart Poettering's avatar
Lennart Poettering committed
359
360
361
        while ((u = hashmap_first(m->units)))
                unit_free(u);

362
363
        manager_dispatch_cleanup_queue(m);

364
365
366
367
        for (c = 0; c < _UNIT_TYPE_MAX; c++)
                if (unit_vtable[c]->shutdown)
                        unit_vtable[c]->shutdown(m);

368
369
        manager_shutdown_cgroup(m);

370
371
        bus_done_api(m);
        bus_done_system(m);
372

Lennart Poettering's avatar
Lennart Poettering committed
373
        hashmap_free(m->units);
Lennart Poettering's avatar
Lennart Poettering committed
374
        hashmap_free(m->jobs);
375
        hashmap_free(m->transaction_jobs);
Lennart Poettering's avatar
Lennart Poettering committed
376
377
378
379
        hashmap_free(m->watch_pids);

        if (m->epoll_fd >= 0)
                close_nointr(m->epoll_fd);
380
381
        if (m->signal_watch.fd >= 0)
                close_nointr(m->signal_watch.fd);
Lennart Poettering's avatar
Lennart Poettering committed
382

383
384
385
        strv_free(m->unit_path);
        strv_free(m->sysvinit_path);

386
387
388
389
390
391
        free(m->cgroup_controller);
        free(m->cgroup_hierarchy);

        assert(hashmap_isempty(m->cgroup_bondings));
        hashmap_free(m->cgroup_bondings);

Lennart Poettering's avatar
Lennart Poettering committed
392
393
394
        free(m);
}

Lennart Poettering's avatar
Lennart Poettering committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
int manager_coldplug(Manager *m) {
        int r;
        UnitType c;
        Iterator i;
        Unit *u;
        char *k;

        assert(m);

        /* First, let's ask every type to load all units from
         * disk/kernel that it might know */
        for (c = 0; c < _UNIT_TYPE_MAX; c++)
                if (unit_vtable[c]->enumerate)
                        if ((r = unit_vtable[c]->enumerate(m)) < 0)
                                return r;

        manager_dispatch_load_queue(m);

        /* Then, let's set up their initial state. */
        HASHMAP_FOREACH_KEY(u, k, m->units, i) {

                /* ignore aliases */
                if (unit_id(u) != k)
                        continue;

                if (UNIT_VTABLE(u)->coldplug)
                        if ((r = UNIT_VTABLE(u)->coldplug(u)) < 0)
                                return r;
        }

        return 0;
}

428
static void transaction_delete_job(Manager *m, Job *j, bool delete_dependencies) {
Lennart Poettering's avatar
Lennart Poettering committed
429
430
431
        assert(m);
        assert(j);

Lennart Poettering's avatar
Lennart Poettering committed
432
433
        /* Deletes one job from the transaction */

434
        manager_transaction_unlink_job(m, j, delete_dependencies);
Lennart Poettering's avatar
Lennart Poettering committed
435

436
        if (!j->installed)
Lennart Poettering's avatar
Lennart Poettering committed
437
438
439
                job_free(j);
}

Lennart Poettering's avatar
Lennart Poettering committed
440
static void transaction_delete_unit(Manager *m, Unit *u) {
Lennart Poettering's avatar
Lennart Poettering committed
441
442
        Job *j;

Lennart Poettering's avatar
Lennart Poettering committed
443
        /* Deletes all jobs associated with a certain unit from the
Lennart Poettering's avatar
Lennart Poettering committed
444
445
         * transaction */

Lennart Poettering's avatar
Lennart Poettering committed
446
        while ((j = hashmap_get(m->transaction_jobs, u)))
447
                transaction_delete_job(m, j, true);
Lennart Poettering's avatar
Lennart Poettering committed
448
449
}

450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
static void transaction_clean_dependencies(Manager *m) {
        Iterator i;
        Job *j;

        assert(m);

        /* Drops all dependencies of all installed jobs */

        HASHMAP_FOREACH(j, m->jobs, i) {
                while (j->subject_list)
                        job_dependency_free(j->subject_list);
                while (j->object_list)
                        job_dependency_free(j->object_list);
        }

        assert(!m->transaction_anchor);
}

468
469
470
471
472
static void transaction_abort(Manager *m) {
        Job *j;

        assert(m);

473
        while ((j = hashmap_first(m->transaction_jobs)))
474
                if (j->installed)
475
                        transaction_delete_job(m, j, true);
476
477
478
479
                else
                        job_free(j);

        assert(hashmap_isempty(m->transaction_jobs));
480
481

        transaction_clean_dependencies(m);
482
483
484
485
486
487
488
}

static void transaction_find_jobs_that_matter_to_anchor(Manager *m, Job *j, unsigned generation) {
        JobDependency *l;

        assert(m);

Lennart Poettering's avatar
Lennart Poettering committed
489
        /* A recursive sweep through the graph that marks all units
Lennart Poettering's avatar
Lennart Poettering committed
490
491
492
493
         * that matter to the anchor job, i.e. are directly or
         * indirectly a dependency of the anchor job via paths that
         * are fully marked as mattering. */

Lennart Poettering's avatar
Lennart Poettering committed
494
495
496
497
498
499
        if (j)
                l = j->subject_list;
        else
                l = m->transaction_anchor;

        LIST_FOREACH(subject, l, l) {
500
501
502
503
504

                /* This link does not matter */
                if (!l->matters)
                        continue;

Lennart Poettering's avatar
Lennart Poettering committed
505
                /* This unit has already been marked */
506
507
508
509
510
511
512
513
514
515
                if (l->object->generation == generation)
                        continue;

                l->object->matters_to_anchor = true;
                l->object->generation = generation;

                transaction_find_jobs_that_matter_to_anchor(m, l->object, generation);
        }
}

516
static void transaction_merge_and_delete_job(Manager *m, Job *j, Job *other, JobType t) {
517
518
519
520
        JobDependency *l, *last;

        assert(j);
        assert(other);
Lennart Poettering's avatar
Lennart Poettering committed
521
        assert(j->unit == other->unit);
522
        assert(!j->installed);
523

Lennart Poettering's avatar
Lennart Poettering committed
524
525
        /* Merges 'other' into 'j' and then deletes j. */

526
527
        j->type = t;
        j->state = JOB_WAITING;
528
        j->forced = j->forced || other->forced;
529
530
531
532
533

        j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;

        /* Patch us in as new owner of the JobDependency objects */
        last = NULL;
Lennart Poettering's avatar
Lennart Poettering committed
534
        LIST_FOREACH(subject, l, other->subject_list) {
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
                assert(l->subject == other);
                l->subject = j;
                last = l;
        }

        /* Merge both lists */
        if (last) {
                last->subject_next = j->subject_list;
                if (j->subject_list)
                        j->subject_list->subject_prev = last;
                j->subject_list = other->subject_list;
        }

        /* Patch us in as new owner of the JobDependency objects */
        last = NULL;
Lennart Poettering's avatar
Lennart Poettering committed
550
        LIST_FOREACH(object, l, other->object_list) {
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
                assert(l->object == other);
                l->object = j;
                last = l;
        }

        /* Merge both lists */
        if (last) {
                last->object_next = j->object_list;
                if (j->object_list)
                        j->object_list->object_prev = last;
                j->object_list = other->object_list;
        }

        /* Kill the other job */
        other->subject_list = NULL;
        other->object_list = NULL;
567
        transaction_delete_job(m, other, true);
568
569
}

570
static int delete_one_unmergeable_job(Manager *m, Job *j) {
Lennart Poettering's avatar
Lennart Poettering committed
571
572
573
574
575
576
577
578
579
580
581
        Job *k;

        assert(j);

        /* Tries to delete one item in the linked list
         * j->transaction_next->transaction_next->... that conflicts
         * whith another one, in an attempt to make an inconsistent
         * transaction work. */

        /* We rely here on the fact that if a merged with b does not
         * merge with c, either a or b merge with c neither */
582
583
        LIST_FOREACH(transaction, j, j)
                LIST_FOREACH(transaction, k, j->transaction_next) {
Lennart Poettering's avatar
Lennart Poettering committed
584
585
586
                        Job *d;

                        /* Is this one mergeable? Then skip it */
587
                        if (job_type_is_mergeable(j->type, k->type))
Lennart Poettering's avatar
Lennart Poettering committed
588
589
590
591
592
593
594
595
596
597
598
599
                                continue;

                        /* Ok, we found two that conflict, let's see if we can
                         * drop one of them */
                        if (!j->matters_to_anchor)
                                d = j;
                        else if (!k->matters_to_anchor)
                                d = k;
                        else
                                return -ENOEXEC;

                        /* Ok, we can drop one, so let's do so. */
600
601
                        log_debug("Trying to fix job merging by deleting job %s/%s", unit_id(d->unit), job_type_to_string(d->type));
                        transaction_delete_job(m, d, true);
Lennart Poettering's avatar
Lennart Poettering committed
602
603
604
605
606
607
                        return 0;
                }

        return -EINVAL;
}

608
static int transaction_merge_jobs(Manager *m) {
609
        Job *j;
610
        Iterator i;
611
612
613
614
        int r;

        assert(m);

Lennart Poettering's avatar
Lennart Poettering committed
615
616
        /* First step, check whether any of the jobs for one specific
         * task conflict. If so, try to drop one of them. */
617
        HASHMAP_FOREACH(j, m->transaction_jobs, i) {
Lennart Poettering's avatar
Lennart Poettering committed
618
619
620
621
                JobType t;
                Job *k;

                t = j->type;
622
                LIST_FOREACH(transaction, k, j->transaction_next) {
Lennart Poettering's avatar
Lennart Poettering committed
623
624
625
626
627
628
629
                        if ((r = job_type_merge(&t, k->type)) >= 0)
                                continue;

                        /* OK, we could not merge all jobs for this
                         * action. Let's see if we can get rid of one
                         * of them */

630
                        if ((r = delete_one_unmergeable_job(m, j)) >= 0)
Lennart Poettering's avatar
Lennart Poettering committed
631
632
633
634
635
636
637
638
639
640
641
                                /* Ok, we managed to drop one, now
                                 * let's ask our callers to call us
                                 * again after garbage collecting */
                                return -EAGAIN;

                        /* We couldn't merge anything. Failure */
                        return r;
                }
        }

        /* Second step, merge the jobs. */
642
        HASHMAP_FOREACH(j, m->transaction_jobs, i) {
643
644
645
                JobType t = j->type;
                Job *k;

646
                /* Merge all transactions */
647
                LIST_FOREACH(transaction, k, j->transaction_next)
Lennart Poettering's avatar
Lennart Poettering committed
648
                        assert_se(job_type_merge(&t, k->type) == 0);
649

650
                /* If an active job is mergeable, merge it too */
Lennart Poettering's avatar
Lennart Poettering committed
651
652
                if (j->unit->meta.job)
                        job_type_merge(&t, j->unit->meta.job->type); /* Might fail. Which is OK */
653

654
                while ((k = j->transaction_next)) {
655
                        if (j->installed) {
656
                                transaction_merge_and_delete_job(m, k, j, t);
657
658
                                j = k;
                        } else
659
                                transaction_merge_and_delete_job(m, j, k, t);
660
661
662
663
664
665
                }

                assert(!j->transaction_next);
                assert(!j->transaction_prev);
        }

666
        return 0;
667
668
}

669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
static void transaction_drop_redundant(Manager *m) {
        bool again;

        assert(m);

        /* Goes through the transaction and removes all jobs that are
         * a noop */

        do {
                Job *j;
                Iterator i;

                again = false;

                HASHMAP_FOREACH(j, m->transaction_jobs, i) {
                        bool changes_something = false;
                        Job *k;

                        LIST_FOREACH(transaction, k, j) {

                                if (!job_is_anchor(k) &&
                                    job_type_is_redundant(k->type, unit_active_state(k->unit)))
                                        continue;

                                changes_something = true;
                                break;
                        }

                        if (changes_something)
                                continue;

                        log_debug("Found redundant job %s/%s, dropping.", unit_id(j->unit), job_type_to_string(j->type));
                        transaction_delete_job(m, j, false);
                        again = true;
                        break;
                }

        } while (again);
}

Lennart Poettering's avatar
Lennart Poettering committed
709
710
static bool unit_matters_to_anchor(Unit *u, Job *j) {
        assert(u);
Lennart Poettering's avatar
Lennart Poettering committed
711
712
        assert(!j->transaction_prev);

Lennart Poettering's avatar
Lennart Poettering committed
713
        /* Checks whether at least one of the jobs for this unit
Lennart Poettering's avatar
Lennart Poettering committed
714
715
         * matters to the anchor. */

716
        LIST_FOREACH(transaction, j, j)
Lennart Poettering's avatar
Lennart Poettering committed
717
718
719
720
721
722
                if (j->matters_to_anchor)
                        return true;

        return false;
}

723
static int transaction_verify_order_one(Manager *m, Job *j, Job *from, unsigned generation) {
724
        Iterator i;
Lennart Poettering's avatar
Lennart Poettering committed
725
        Unit *u;
726
        int r;
727
728
729

        assert(m);
        assert(j);
Lennart Poettering's avatar
Lennart Poettering committed
730
731
732
733
        assert(!j->transaction_prev);

        /* Does a recursive sweep through the ordering graph, looking
         * for a cycle. If we find cycle we try to break it. */
734

735
        /* Did we find a cycle? */
736
737
738
739
        if (j->marker && j->generation == generation) {
                Job *k;

                /* So, we already have been here. We have a
Lennart Poettering's avatar
Lennart Poettering committed
740
741
742
743
744
                 * cycle. Let's try to break it. We go backwards in
                 * our path and try to find a suitable job to
                 * remove. We use the marker to find our way back,
                 * since smart how we are we stored our way back in
                 * there. */
745

746
                log_debug("Found ordering cycle on %s/%s", unit_id(j->unit), job_type_to_string(j->type));
747

748
                for (k = from; k; k = (k->generation == generation ? k->marker : NULL)) {
Lennart Poettering's avatar
Lennart Poettering committed
749

750
                        log_debug("Walked on cycle path to %s/%s", unit_id(k->unit), job_type_to_string(k->type));
751

752
                        if (!k->installed &&
Lennart Poettering's avatar
Lennart Poettering committed
753
                            !unit_matters_to_anchor(k->unit, k)) {
Lennart Poettering's avatar
Lennart Poettering committed
754
755
                                /* Ok, we can drop this one, so let's
                                 * do so. */
Lennart Poettering's avatar
Lennart Poettering committed
756
757
                                log_debug("Breaking order cycle by deleting job %s/%s", unit_id(k->unit), job_type_to_string(k->type));
                                transaction_delete_unit(m, k->unit);
758
759
760
761
                                return -EAGAIN;
                        }

                        /* Check if this in fact was the beginning of
762
                         * the cycle */
763
764
765
766
                        if (k == j)
                                break;
                }

767
768
                log_debug("Unable to break cycle");

Lennart Poettering's avatar
Lennart Poettering committed
769
                return -ENOEXEC;
770
771
        }

Lennart Poettering's avatar
Lennart Poettering committed
772
773
        /* Make the marker point to where we come from, so that we can
         * find our way backwards if we want to break a cycle */
774
775
776
        j->marker = from;
        j->generation = generation;

Lennart Poettering's avatar
Lennart Poettering committed
777
        /* We assume that the the dependencies are bidirectional, and
Lennart Poettering's avatar
Lennart Poettering committed
778
779
         * hence can ignore UNIT_AFTER */
        SET_FOREACH(u, j->unit->meta.dependencies[UNIT_BEFORE], i) {
780
781
                Job *o;

Lennart Poettering's avatar
Lennart Poettering committed
782
783
                /* Is there a job for this unit? */
                if (!(o = hashmap_get(m->transaction_jobs, u)))
Lennart Poettering's avatar
Lennart Poettering committed
784
785
786
787

                        /* Ok, there is no job for this in the
                         * transaction, but maybe there is already one
                         * running? */
Lennart Poettering's avatar
Lennart Poettering committed
788
                        if (!(o = u->meta.job))
789
790
791
792
793
794
                                continue;

                if ((r = transaction_verify_order_one(m, o, j, generation)) < 0)
                        return r;
        }

795
796
797
798
        /* Ok, let's backtrack, and remember that this entry is not on
         * our path anymore. */
        j->marker = NULL;

799
800
801
802
        return 0;
}

static int transaction_verify_order(Manager *m, unsigned *generation) {
Lennart Poettering's avatar
Lennart Poettering committed
803
804
        Job *j;
        int r;
805
        Iterator i;
Lennart Poettering's avatar
Lennart Poettering committed
806

807
808
809
        assert(m);
        assert(generation);

Lennart Poettering's avatar
Lennart Poettering committed
810
811
        /* Check if the ordering graph is cyclic. If it is, try to fix
         * that up by dropping one of the jobs. */
812

813
        HASHMAP_FOREACH(j, m->transaction_jobs, i)
Lennart Poettering's avatar
Lennart Poettering committed
814
815
                if ((r = transaction_verify_order_one(m, j, NULL, (*generation)++)) < 0)
                        return r;
816
817
818
819
820
821
822
823
824

        return 0;
}

static void transaction_collect_garbage(Manager *m) {
        bool again;

        assert(m);

Lennart Poettering's avatar
Lennart Poettering committed
825
826
        /* Drop jobs that are not required by any other job */

827
        do {
828
                Iterator i;
829
830
831
832
                Job *j;

                again = false;

833
                HASHMAP_FOREACH(j, m->transaction_jobs, i) {
834
835
836
                        if (j->object_list)
                                continue;

Lennart Poettering's avatar
Lennart Poettering committed
837
                        log_debug("Garbage collecting job %s/%s", unit_id(j->unit), job_type_to_string(j->type));
838
                        transaction_delete_job(m, j, true);
839
840
841
842
843
844
845
846
                        again = true;
                        break;
                }

        } while (again);
}

static int transaction_is_destructive(Manager *m, JobMode mode) {
847
        Iterator i;
848
        Job *j;
849
850
851

        assert(m);

852
853
        /* Checks whether applying this transaction means that
         * existing jobs would be replaced */
854

855
        HASHMAP_FOREACH(j, m->transaction_jobs, i) {
856
857
858
859
860

                /* Assume merged */
                assert(!j->transaction_prev);
                assert(!j->transaction_next);

Lennart Poettering's avatar
Lennart Poettering committed
861
862
863
                if (j->unit->meta.job &&
                    j->unit->meta.job != j &&
                    !job_type_is_superset(j->type, j->unit->meta.job->type))
864
                        return -EEXIST;
865
        }
866

867
868
869
        return 0;
}

870
871
872
873
874
875
876
877
878
static void transaction_minimize_impact(Manager *m) {
        bool again;
        assert(m);

        /* Drops all unnecessary jobs that reverse already active jobs
         * or that stop a running service. */

        do {
                Job *j;
879
                Iterator i;
880
881
882

                again = false;

883
884
                HASHMAP_FOREACH(j, m->transaction_jobs, i) {
                        LIST_FOREACH(transaction, j, j) {
885
                                bool stops_running_service, changes_existing_job;
886
887
888
889
890
891
892
893

                                /* If it matters, we shouldn't drop it */
                                if (j->matters_to_anchor)
                                        continue;

                                /* Would this stop a running service?
                                 * Would this change an existing job?
                                 * If so, let's drop this entry */
894
895
896
897
898
899
900
901

                                stops_running_service =
                                        j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));

                                changes_existing_job =
                                        j->unit->meta.job && job_type_is_conflicting(j->type, j->unit->meta.job->state);

                                if (!stops_running_service && !changes_existing_job)
902
903
                                        continue;

904
905
906
907
908
909
                                if (stops_running_service)
                                        log_debug("%s/%s would stop a running service.", unit_id(j->unit), job_type_to_string(j->type));

                                if (changes_existing_job)
                                        log_debug("%s/%s would change existing job.", unit_id(j->unit), job_type_to_string(j->type));

910
                                /* Ok, let's get rid of this */
911
912
                                log_debug("Deleting %s/%s to minimize impact.", unit_id(j->unit), job_type_to_string(j->type));

913
                                transaction_delete_job(m, j, true);
914
915
916
917
918
919
920
921
922
923
924
                                again = true;
                                break;
                        }

                        if (again)
                                break;
                }

        } while (again);
}

925
static int transaction_apply(Manager *m, JobMode mode) {
926
        Iterator i;
927
928
929
        Job *j;
        int r;

Lennart Poettering's avatar
Lennart Poettering committed
930
931
        /* Moves the transaction jobs to the set of active jobs */

932
        HASHMAP_FOREACH(j, m->transaction_jobs, i) {
933
934
935
936
                /* Assume merged */
                assert(!j->transaction_prev);
                assert(!j->transaction_next);

937
                if (j->installed)
938
939
940
                        continue;

                if ((r = hashmap_put(m->jobs, UINT32_TO_PTR(j->id), j)) < 0)
941
942
943
                        goto rollback;
        }

944
        while ((j = hashmap_steal_first(m->transaction_jobs))) {
945
                if (j->installed)
946
947
                        continue;

Lennart Poettering's avatar
Lennart Poettering committed
948
949
                if (j->unit->meta.job)
                        job_free(j->unit->meta.job);
950

Lennart Poettering's avatar
Lennart Poettering committed
951
                j->unit->meta.job = j;
952
                j->installed = true;
953

954
955
956
957
958
959
                /* We're fully installed. Now let's free data we don't
                 * need anymore. */

                assert(!j->transaction_next);
                assert(!j->transaction_prev);

960
961
                job_add_to_run_queue(j);
                job_add_to_dbus_queue(j);
962
963
964
        }

        /* As last step, kill all remaining job dependencies. */
965
        transaction_clean_dependencies(m);
Lennart Poettering's avatar
Lennart Poettering committed
966

967
968
969
970
        return 0;

rollback:

971
        HASHMAP_FOREACH(j, m->transaction_jobs, i) {
972
                if (j->installed)
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
                        continue;

                hashmap_remove(m->jobs, UINT32_TO_PTR(j->id));
        }

        return r;
}

static int transaction_activate(Manager *m, JobMode mode) {
        int r;
        unsigned generation = 1;

        assert(m);

        /* This applies the changes recorded in transaction_jobs to
         * the actual list of jobs, if possible. */

        /* First step: figure out which jobs matter */
        transaction_find_jobs_that_matter_to_anchor(m, NULL, generation++);

993
994
995
996
997
        /* Second step: Try not to stop any running services if
         * we don't have to. Don't try to reverse running
         * jobs if we don't have to. */
        transaction_minimize_impact(m);

998
999
1000
        /* Third step: Drop redundant jobs */
        transaction_drop_redundant(m);

Lennart Poettering's avatar
Lennart Poettering committed
1001
        for (;;) {
1002
                /* Fourth step: Let's remove unneeded jobs that might
Lennart Poettering's avatar
Lennart Poettering committed
1003
1004
                 * be lurking. */
                transaction_collect_garbage(m);
1005

1006
                /* Fifth step: verify order makes sense and correct
Lennart Poettering's avatar
Lennart Poettering committed
1007
1008
1009
                 * cycles if necessary and possible */
                if ((r = transaction_verify_order(m, &generation)) >= 0)
                        break;
1010

1011
1012
                if (r != -EAGAIN) {
                        log_debug("Requested transaction contains an unfixable cyclic ordering dependency: %s", strerror(-r));
Lennart Poettering's avatar
Lennart Poettering committed
1013
                        goto rollback;
1014
                }
1015

Lennart Poettering's avatar
Lennart Poettering committed
1016
1017
1018
1019
1020
                /* Let's see if the resulting transaction ordering
                 * graph is still cyclic... */
        }

        for (;;) {
1021
                /* Sixth step: let's drop unmergeable entries if
Lennart Poettering's avatar
Lennart Poettering committed
1022
1023
1024
1025
1026
                 * necessary and possible, merge entries we can
                 * merge */
                if ((r = transaction_merge_jobs(m)) >= 0)
                        break;

1027
1028
                if (r != -EAGAIN) {
                        log_debug("Requested transaction contains unmergable jobs: %s", strerror(-r));
Lennart Poettering's avatar
Lennart Poettering committed
1029
                        goto rollback;
1030
                }
Lennart Poettering's avatar
Lennart Poettering committed
1031

1032
                /* Seventh step: an entry got dropped, let's garbage
Lennart Poettering's avatar
Lennart Poettering committed
1033
1034
1035
1036
                 * collect its dependencies. */
                transaction_collect_garbage(m);

                /* Let's see if the resulting transaction still has
1037
                 * unmergeable entries ... */
Lennart Poettering's avatar
Lennart Poettering committed
1038
1039
        }

1040
1041
1042
1043
        /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
        transaction_drop_redundant(m);

        /* Ninth step: check whether we can actually apply this */
1044
        if (mode == JOB_FAIL)
1045
1046
                if ((r = transaction_is_destructive(m, mode)) < 0) {
                        log_debug("Requested transaction contradicts existing jobs: %s", strerror(-r));
1047
                        goto rollback;
1048
                }
1049

1050
        /* Tenth step: apply changes */
1051
1052
        if ((r = transaction_apply(m, mode)) < 0) {
                log_debug("Failed to apply transaction: %s", strerror(-r));
1053
                goto rollback;
1054
        }
1055
1056
1057
1058
1059

        assert(hashmap_isempty(m->transaction_jobs));
        assert(!m->transaction_anchor);

        return 0;
1060

1061
rollback:
1062
1063
1064
1065
        transaction_abort(m);
        return r;
}

Lennart Poettering's avatar
Lennart Poettering committed
1066
static Job* transaction_add_one_job(Manager *m, JobType type, Unit *unit, bool force, bool *is_new) {
1067
        Job *j, *f;
Lennart Poettering's avatar
Lennart Poettering committed
1068
1069
1070
        int r;

        assert(m);
Lennart Poettering's avatar
Lennart Poettering committed
1071
        assert(unit);
Lennart Poettering's avatar
Lennart Poettering committed
1072

1073
1074
1075
        /* Looks for an axisting prospective job and returns that. If
         * it doesn't exist it is created and added to the prospective
         * jobs list. */
Lennart Poettering's avatar
Lennart Poettering committed
1076

Lennart Poettering's avatar
Lennart Poettering committed
1077
        f = hashmap_get(m->transaction_jobs, unit);
Lennart Poettering's avatar
Lennart Poettering committed
1078

1079
        LIST_FOREACH(transaction, j, f) {
Lennart Poettering's avatar
Lennart Poettering committed
1080
                assert(j->unit == unit);
Lennart Poettering's avatar
Lennart Poettering committed
1081

1082
1083
1084
1085
1086
1087
                if (j->type == type) {
                        if (is_new)
                                *is_new = false;
                        return j;
                }
        }
Lennart Poettering's avatar
Lennart Poettering committed
1088

Lennart Poettering's avatar
Lennart Poettering committed
1089
1090
1091
        if (unit->meta.job && unit->meta.job->type == type)
                j = unit->meta.job;
        else if (!(j = job_new(m, type, unit)))
1092
                return NULL;
Lennart Poettering's avatar
Lennart Poettering committed
1093

1094
1095
1096
        j->generation = 0;
        j->marker = NULL;
        j->matters_to_anchor = false;
1097
        j->forced = force;
Lennart Poettering's avatar
Lennart Poettering committed
1098

1099
1100
        LIST_PREPEND(Job, transaction, f, j);

Lennart Poettering's avatar
Lennart Poettering committed
1101
        if ((r = hashmap_replace(m->transaction_jobs, unit, f)) < 0) {
1102
1103
1104
1105
                job_free(j);
                return NULL;
        }

1106
1107
        if (is_new)
                *is_new = true;
Lennart Poettering's avatar
Lennart Poettering committed
1108

1109
1110
        log_debug("Added job %s/%s to transaction.", unit_id(unit), job_type_to_string(type));

1111
1112
        return j;
}
1113

1114
void manager_transaction_unlink_job(Manager *m, Job *j, bool delete_dependencies) {
1115
1116
        assert(m);
        assert(j);
1117

1118
1119
1120
        if (j->transaction_prev)
                j->transaction_prev->transaction_next = j->transaction_next;
        else if (j->transaction_next)
Lennart Poettering's avatar
Lennart Poettering committed
1121
                hashmap_replace(m->transaction_jobs, j->unit, j->transaction_next);
1122
        else
Lennart Poettering's avatar
Lennart Poettering committed
1123
                hashmap_remove_value(m->transaction_jobs, j->unit, j);
1124
1125
1126
1127
1128
1129
1130
1131

        if (j->transaction_next)
                j->transaction_next->transaction_prev = j->transaction_prev;

        j->transaction_prev = j->transaction_next = NULL;

        while (j->subject_list)
                job_dependency_free(j->subject_list);
1132
1133
1134
1135

        while (j->object_list) {
                Job *other = j->object_list->matters ? j->object_list->subject : NULL;

1136
                job_dependency_free(j->object_list);
1137

1138
                if (other && delete_dependencies) {
1139
                        log_debug("Deleting job %s/%s as dependency of job %s/%s",
Lennart Poettering's avatar
Lennart Poettering committed
1140
1141
                                  unit_id(other->unit), job_type_to_string(other->type),
                                  unit_id(j->unit), job_type_to_string(j->type));
1142
                        transaction_delete_job(m, other, delete_dependencies);
1143
1144
                }
        }
1145
1146
}

Lennart Poettering's avatar
Lennart Poettering committed
1147
static int transaction_add_job_and_dependencies(Manager *m, JobType type, Unit *unit, Job *by, bool matters, bool force, Job **_ret) {
1148
        Job *ret;
1149
        Iterator i;
Lennart Poettering's avatar
Lennart Poettering committed
1150
        Unit *dep;
1151
1152
1153
1154
1155
        int r;
        bool is_new;

        assert(m);
        assert(type < _JOB_TYPE_MAX);
Lennart Poettering's avatar
Lennart Poettering committed
1156
        assert(unit);
1157

Lennart Poettering's avatar
Lennart Poettering committed
1158
        if (unit->meta.load_state != UNIT_LOADED)
1159
1160
                return -EINVAL;

Lennart Poettering's avatar
Lennart Poettering committed
1161
        if (!unit_job_is_applicable(unit, type))
1162
1163
                return -EBADR;

1164
        /* First add the job. */
Lennart Poettering's avatar
Lennart Poettering committed
1165
        if (!(ret = transaction_add_one_job(m, type, unit, force, &is_new)))
1166
1167
1168
1169
1170
1171
1172
1173
1174
                return -ENOMEM;

        /* Then, add a link to the job. */
        if (!job_dependency_new(by, ret, matters))
                return -ENOMEM;

        if (is_new) {
                /* Finally, recursively add in all dependencies. */
                if (type == JOB_START || type == JOB_RELOAD_OR_START) {
Lennart Poettering's avatar
Lennart Poettering committed
1175
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRES], i)
Lennart Poettering's avatar
Lennart Poettering committed
1176
                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
1177
                                        goto fail;
Lennart Poettering's avatar
Lennart Poettering committed
1178
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUIRES], i)
Lennart Poettering's avatar
Lennart Poettering committed
1179
                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
1180
                                        goto fail;
Lennart Poettering's avatar
Lennart Poettering committed
1181
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_WANTS], i)
1182
1183
                                if ((r = transaction_add_job_and_dependencies(m, JOB_START, dep, ret, false, force, NULL)) < 0)
                                        log_warning("Cannot add dependency job for unit %s, ignoring: %s", unit_id(dep), strerror(-r));
Lennart Poettering's avatar
Lennart Poettering committed
1184
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUISITE], i)
Lennart Poettering's avatar
Lennart Poettering committed
1185
                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
1186
                                        goto fail;
Lennart Poettering's avatar
Lennart Poettering committed
1187
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_SOFT_REQUISITE], i)
Lennart Poettering's avatar
Lennart Poettering committed
1188
                                if ((r = transaction_add_job_and_dependencies(m, JOB_VERIFY_ACTIVE, dep, ret, !force, force, NULL)) < 0 && r != -EBADR)
1189
                                        goto fail;
Lennart Poettering's avatar
Lennart Poettering committed
1190
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_CONFLICTS], i)
Lennart Poettering's avatar
Lennart Poettering committed
1191
                                if ((r = transaction_add_job_and_dependencies(m, JOB_STOP, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
1192
1193
1194
1195
                                        goto fail;

                } else if (type == JOB_STOP || type == JOB_RESTART || type == JOB_TRY_RESTART) {

Lennart Poettering's avatar
Lennart Poettering committed
1196
                        SET_FOREACH(dep, ret->unit->meta.dependencies[UNIT_REQUIRED_BY], i)
Lennart Poettering's avatar
Lennart Poettering committed
1197
                                if ((r = transaction_add_job_and_dependencies(m, type, dep, ret, true, force, NULL)) < 0 && r != -EBADR)
1198
1199
1200
1201
1202
                                        goto fail;
                }

                /* JOB_VERIFY_STARTED, JOB_RELOAD require no dependency handling */
        }
Lennart Poettering's avatar
Lennart Poettering committed
1203

1204
1205
1206
        if (_ret)
                *_ret = ret;

Lennart Poettering's avatar
Lennart Poettering committed
1207
1208
1209
        return 0;

fail:
1210
1211
1212
        return r;
}

Lennart Poettering's avatar
Lennart Poettering committed
1213
int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, bool force, Job **_ret) {
1214
1215
1216
1217
1218
        int r;
        Job *ret;

        assert(m);
        assert(type < _JOB_TYPE_MAX);
Lennart Poettering's avatar
Lennart Poettering committed
1219
        assert(unit);
1220
        assert(mode < _JOB_MODE_MAX);
Lennart Poettering's avatar
Lennart Poettering committed
1221

1222
1223
        log_debug("Trying to enqueue job %s/%s", unit_id(unit), job_type_to_string(type));

1224
        if ((r = transaction_add_job_and_dependencies(m, type, unit, NULL, true, force, &ret)) < 0) {
1225
                transaction_abort(m);
1226
1227
                return r;
        }
1228

1229
1230
1231
        if ((r = transaction_activate(m, mode)) < 0)
                return r;

1232
        log_debug("Enqueued job %s/%s as %u", unit_id(unit), job_type_to_string(type), (unsigned) ret->id);
Lennart Poettering's avatar
Lennart Poettering committed
1233

1234
1235
        if (_ret)
                *_ret = ret;
Lennart Poettering's avatar
Lennart Poettering committed
1236

1237
1238
        return 0;
}
Lennart Poettering's avatar
Lennart Poettering committed
1239
1240
1241
1242
1243
1244
1245

Job *manager_get_job(Manager *m, uint32_t id) {
        assert(m);

        return hashmap_get(m->jobs, UINT32_TO_PTR(id));
}