• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 14630481637

23 Apr 2025 07:04PM UTC coverage: 72.178% (-0.002%) from 72.18%
14630481637

push

github

DaanDeMeyer
mkosi: Run clangd within the tools tree instead of the build container

Running within the build sandbox has a number of disadvantages:
- We have a separate clangd cache for each distribution/release combo
- It requires to build the full image before clangd can be used
- It breaks every time the image becomes out of date and requires a
  rebuild
- We can't look at system headers as we don't have the knowledge to map
  them from inside the build sandbox to the corresponding path on the host

Instead, let's have mkosi.clangd run clangd within the tools tree. We
already require building systemd for both the host and the target anyway,
and all the dependencies to build systemd are installed in the tools tree
already for that, as well as clangd since it's installed together with the
other clang tooling we install in the tools tree. Unlike the previous approach,
this approach only requires the mkosi tools tree to be built upfront, which has
a much higher chance of not invalidating its cache. We can also trivially map
system header lookups from within the sandbox to the path within mkosi.tools
on the host so that starts working as well.

297054 of 411557 relevant lines covered (72.18%)

686269.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.75
/src/core/transaction.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <unistd.h>
5

6
#include "alloc-util.h"
7
#include "ansi-color.h"
8
#include "bus-common-errors.h"
9
#include "bus-error.h"
10
#include "dbus-unit.h"
11
#include "manager.h"
12
#include "slice.h"
13
#include "strv.h"
14
#include "terminal-util.h"
15
#include "transaction.h"
16

17
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies);
18

19
static void transaction_delete_job(Transaction *tr, Job *j, bool delete_dependencies) {
21,160✔
20
        assert(tr);
21,160✔
21
        assert(j);
21,160✔
22

23
        /* Deletes one job from the transaction */
24

25
        transaction_unlink_job(tr, j, delete_dependencies);
21,160✔
26

27
        job_free(j);
21,160✔
28
}
21,160✔
29

30
static void transaction_delete_unit(Transaction *tr, Unit *u) {
2✔
31
        Job *j;
2✔
32

33
        /* Deletes all jobs associated with a certain unit from the
34
         * transaction */
35

36
        while ((j = hashmap_get(tr->jobs, u)))
4✔
37
                transaction_delete_job(tr, j, true);
2✔
38
}
2✔
39

40
static void transaction_abort(Transaction *tr) {
12✔
41
        Job *j;
12✔
42

43
        assert(tr);
12✔
44

45
        while ((j = hashmap_first(tr->jobs)))
42✔
46
                transaction_delete_job(tr, j, false);
30✔
47

48
        assert(hashmap_isempty(tr->jobs));
12✔
49
}
12✔
50

51
static void transaction_find_jobs_that_matter_to_anchor(Job *j, unsigned generation) {
19,143✔
52
        assert(j);
19,143✔
53

54
        /* A recursive sweep through the graph that marks all units
55
         * that matter to the anchor job, i.e. are directly or
56
         * indirectly a dependency of the anchor job via paths that
57
         * are fully marked as mattering. */
58

59
        j->matters_to_anchor = true;
19,143✔
60
        j->generation = generation;
19,143✔
61

62
        LIST_FOREACH(subject, l, j->subject_list) {
46,257✔
63

64
                /* This link does not matter */
65
                if (!l->matters)
27,114✔
66
                        continue;
19,703✔
67

68
                /* This unit has already been marked */
69
                if (l->object->generation == generation)
7,411✔
70
                        continue;
1,655✔
71

72
                transaction_find_jobs_that_matter_to_anchor(l->object, generation);
5,756✔
73
        }
74
}
19,143✔
75

76
static void transaction_merge_and_delete_job(Transaction *tr, Job *j, Job *other, JobType t) {
×
77
        JobDependency *last;
×
78

79
        assert(j);
×
80
        assert(other);
×
81
        assert(j->unit == other->unit);
×
82
        assert(!j->installed);
×
83

84
        /* Merges 'other' into 'j' and then deletes 'other'. */
85

86
        j->type = t;
×
87
        j->state = JOB_WAITING;
×
88
        j->irreversible = j->irreversible || other->irreversible;
×
89
        j->matters_to_anchor = j->matters_to_anchor || other->matters_to_anchor;
×
90

91
        /* Patch us in as new owner of the JobDependency objects */
92
        last = NULL;
×
93
        LIST_FOREACH(subject, l, other->subject_list) {
×
94
                assert(l->subject == other);
×
95
                l->subject = j;
×
96
                last = l;
×
97
        }
98

99
        /* Merge both lists */
100
        if (last) {
×
101
                last->subject_next = j->subject_list;
×
102
                if (j->subject_list)
×
103
                        j->subject_list->subject_prev = last;
×
104
                j->subject_list = other->subject_list;
×
105
        }
106

107
        /* Patch us in as new owner of the JobDependency objects */
108
        last = NULL;
×
109
        LIST_FOREACH(object, l, other->object_list) {
×
110
                assert(l->object == other);
×
111
                l->object = j;
×
112
                last = l;
×
113
        }
114

115
        /* Merge both lists */
116
        if (last) {
×
117
                last->object_next = j->object_list;
×
118
                if (j->object_list)
×
119
                        j->object_list->object_prev = last;
×
120
                j->object_list = other->object_list;
×
121
        }
122

123
        /* Kill the other job */
124
        other->subject_list = NULL;
×
125
        other->object_list = NULL;
×
126
        transaction_delete_job(tr, other, true);
×
127
}
×
128

129
static bool job_is_conflicted_by(Job *j) {
6✔
130
        assert(j);
6✔
131

132
        /* Returns true if this job is pulled in by a least one
133
         * ConflictedBy dependency. */
134

135
        LIST_FOREACH(object, l, j->object_list)
12✔
136
                if (l->conflicts)
6✔
137
                        return true;
138

139
        return false;
140
}
141

142
static int delete_one_unmergeable_job(Transaction *tr, Job *job) {
13✔
143
        assert(job);
13✔
144

145
        /* Tries to delete one item in the linked list
146
         * j->transaction_next->transaction_next->... that conflicts
147
         * with another one, in an attempt to make an inconsistent
148
         * transaction work. */
149

150
        /* We rely here on the fact that if a merged with b does not
151
         * merge with c, either a or b merge with c neither */
152
        LIST_FOREACH(transaction, j, job)
13✔
153
                LIST_FOREACH(transaction, k, j->transaction_next) {
13✔
154
                        Job *d;
13✔
155

156
                        /* Is this one mergeable? Then skip it */
157
                        if (job_type_is_mergeable(j->type, k->type))
13✔
158
                                continue;
×
159

160
                        /* Ok, we found two that conflict, let's see if we can
161
                         * drop one of them */
162
                        if (!j->matters_to_anchor && !k->matters_to_anchor) {
13✔
163

164
                                /* Both jobs don't matter, so let's
165
                                 * find the one that is smarter to
166
                                 * remove. Let's think positive and
167
                                 * rather remove stops then starts --
168
                                 * except if something is being
169
                                 * stopped because it is conflicted by
170
                                 * another unit in which case we
171
                                 * rather remove the start. */
172

173
                                log_unit_debug(j->unit,
6✔
174
                                               "Looking at job %s/%s conflicted_by=%s",
175
                                               j->unit->id, job_type_to_string(j->type),
176
                                               yes_no(j->type == JOB_STOP && job_is_conflicted_by(j)));
177
                                log_unit_debug(k->unit,
3✔
178
                                               "Looking at job %s/%s conflicted_by=%s",
179
                                               k->unit->id, job_type_to_string(k->type),
180
                                               yes_no(k->type == JOB_STOP && job_is_conflicted_by(k)));
181

182
                                if (j->type == JOB_STOP) {
3✔
183

184
                                        if (job_is_conflicted_by(j))
3✔
185
                                                d = k;
×
186
                                        else
187
                                                d = j;
188

189
                                } else if (k->type == JOB_STOP) {
×
190

191
                                        if (job_is_conflicted_by(k))
×
192
                                                d = j;
193
                                        else
194
                                                d = k;
×
195
                                } else
196
                                        d = j;
197

198
                        } else if (!j->matters_to_anchor)
10✔
199
                                d = j;
200
                        else if (!k->matters_to_anchor)
×
201
                                d = k;
202
                        else
203
                                return -ENOEXEC;
204

205
                        /* Ok, we can drop one, so let's do so. */
206
                        log_unit_debug(d->unit,
13✔
207
                                       "Fixing conflicting jobs %s/%s,%s/%s by deleting job %s/%s",
208
                                       j->unit->id, job_type_to_string(j->type),
209
                                       k->unit->id, job_type_to_string(k->type),
210
                                       d->unit->id, job_type_to_string(d->type));
211
                        transaction_delete_job(tr, d, true);
13✔
212
                        return 0;
13✔
213
                }
214

215
        return -EINVAL;
216
}
217

218
static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) {
13,398✔
219
        Job *j;
13,398✔
220
        int r;
13,398✔
221

222
        assert(tr);
13,398✔
223

224
        /* First step, check whether any of the jobs for one specific
225
         * task conflict. If so, try to drop one of them. */
226
        HASHMAP_FOREACH(j, tr->jobs) {
42,054✔
227
                JobType t;
28,669✔
228

229
                t = j->type;
28,669✔
230
                LIST_FOREACH(transaction, k, j->transaction_next) {
28,669✔
231
                        if (job_type_merge_and_collapse(&t, k->type, j->unit) >= 0)
13✔
232
                                continue;
×
233

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

238
                        r = delete_one_unmergeable_job(tr, j);
13✔
239
                        if (r >= 0)
13✔
240
                                /* Ok, we managed to drop one, now
241
                                 * let's ask our callers to call us
242
                                 * again after garbage collecting */
243
                                return -EAGAIN;
13✔
244

245
                        /* We couldn't merge anything. Failure */
246
                        return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_JOBS_CONFLICTING,
×
247
                                                 "Transaction contains conflicting jobs '%s' and '%s' for %s. "
248
                                                 "Probably contradicting requirement dependencies configured.",
249
                                                 job_type_to_string(t),
250
                                                 job_type_to_string(k->type),
251
                                                 k->unit->id);
×
252
                }
253
        }
254

255
        /* Second step, merge the jobs. */
256
        HASHMAP_FOREACH(j, tr->jobs) {
41,747✔
257
                JobType t = j->type;
28,362✔
258

259
                /* Merge all transaction jobs for j->unit */
260
                LIST_FOREACH(transaction, k, j->transaction_next)
28,362✔
261
                        assert_se(job_type_merge_and_collapse(&t, k->type, j->unit) == 0);
×
262

263
                Job *k;
264
                while ((k = j->transaction_next)) {
28,362✔
265
                        if (tr->anchor_job == k) {
×
266
                                transaction_merge_and_delete_job(tr, k, j, t);
×
267
                                j = k;
×
268
                        } else
269
                                transaction_merge_and_delete_job(tr, j, k, t);
×
270
                }
271

272
                assert(!j->transaction_next);
28,362✔
273
                assert(!j->transaction_prev);
28,362✔
274
        }
275

276
        return 0;
13,385✔
277
}
278

279
static void transaction_drop_redundant(Transaction *tr) {
26,772✔
280
        bool again;
26,772✔
281

282
        /* Goes through the transaction and removes all jobs of the units whose jobs are all noops. If not
283
         * all of a unit's jobs are redundant, they are kept. */
284

285
        assert(tr);
26,772✔
286

287
        do {
42,393✔
288
                Job *j;
42,393✔
289

290
                again = false;
42,393✔
291

292
                HASHMAP_FOREACH(j, tr->jobs) {
384,663✔
293
                        bool keep = false;
357,891✔
294

295
                        LIST_FOREACH(transaction, k, j)
374,546✔
296
                                if (tr->anchor_job == k ||
683,569✔
297
                                    !job_type_is_redundant(k->type, unit_active_state(k->unit)) ||
324,644✔
298
                                    (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) {
16,693✔
299
                                        keep = true;
300
                                        break;
301
                                }
302

303
                        if (!keep) {
357,891✔
304
                                log_trace("Found redundant job %s/%s, dropping from transaction.",
15,621✔
305
                                          j->unit->id, job_type_to_string(j->type));
306
                                transaction_delete_job(tr, j, false);
15,621✔
307
                                again = true;
308
                                break;
309
                        }
310
                }
311
        } while (again);
42,393✔
312
}
26,772✔
313

314
static bool job_matters_to_anchor(Job *job) {
9✔
315
        assert(job);
9✔
316
        assert(!job->transaction_prev);
9✔
317

318
        /* Checks whether at least one of the jobs for this transaction matters to the anchor. */
319

320
        LIST_FOREACH(transaction, j, job)
11✔
321
                if (j->matters_to_anchor)
9✔
322
                        return true;
323

324
        return false;
325
}
326

327
static char* merge_unit_ids(const char* unit_log_field, char * const* pairs) {
4✔
328
        _cleanup_free_ char *ans = NULL;
4✔
329
        size_t size = 0;
4✔
330

331
        assert(unit_log_field);
4✔
332

333
        STRV_FOREACH_PAIR(unit_id, job_type, pairs) {
15✔
334
                size_t next;
11✔
335

336
                if (size > 0)
11✔
337
                        ans[size - 1] = '\n';
7✔
338

339
                next = strlen(unit_log_field) + strlen(*unit_id);
11✔
340
                if (!GREEDY_REALLOC(ans, size + next + 1))
11✔
341
                        return NULL;
342

343
                sprintf(ans + size, "%s%s", unit_log_field, *unit_id);
11✔
344
                size += next + 1;
11✔
345
        }
346

347
        if (!ans)
4✔
348
                return strdup("");
×
349

350
        return TAKE_PTR(ans);
4✔
351
}
352

353
static int transaction_verify_order_one(Transaction *tr, Job *j, Job *from, unsigned generation, sd_bus_error *e) {
67,398✔
354

355
        static const UnitDependencyAtom directions[] = {
67,398✔
356
                UNIT_ATOM_BEFORE,
357
                UNIT_ATOM_AFTER,
358
        };
359

360
        int r;
67,398✔
361

362
        assert(tr);
67,398✔
363
        assert(j);
67,398✔
364
        assert(!j->transaction_prev);
67,398✔
365

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

369
        /* Have we seen this before? */
370
        if (j->generation == generation) {
67,398✔
371
                Job *k, *delete = NULL;
36,733✔
372
                _cleanup_free_ char **array = NULL, *unit_ids = NULL;
36,733✔
373

374
                /* If the marker is NULL we have been here already and decided the job was loop-free from
375
                 * here. Hence shortcut things and return right-away. */
376
                if (!j->marker)
36,733✔
377
                        return 0;
378

379
                /* So, the marker is not NULL and we already have been here. We have a cycle. Let's try to
380
                 * break it. We go backwards in our path and try to find a suitable job to remove. We use the
381
                 * marker to find our way back, since smart how we are we stored our way back in there. */
382
                for (k = from; k; k = ((k->generation == generation && k->marker != k) ? k->marker : NULL)) {
18✔
383

384
                        /* For logging below */
385
                        if (strv_push_pair(&array, k->unit->id, (char*) job_type_to_string(k->type)) < 0)
11✔
386
                                log_oom();
×
387

388
                        if (!delete && hashmap_contains(tr->jobs, k->unit) && !job_matters_to_anchor(k))
11✔
389
                                /* Ok, we can drop this one, so let's do so. */
390
                                delete = k;
2✔
391

392
                        /* Check if this in fact was the beginning of the cycle */
393
                        if (k == j)
11✔
394
                                break;
395
                }
396

397
                unit_ids = merge_unit_ids(unit_log_field(j->unit), array); /* ignore error */
4✔
398

399
                STRV_FOREACH_PAIR(unit_id, job_type, array)
15✔
400
                        /* logging for j not k here to provide a consistent narrative */
401
                        log_struct(LOG_WARNING,
18✔
402
                                   LOG_UNIT_MESSAGE(j->unit,
403
                                                    "Found %s on %s/%s",
404
                                                    unit_id == array ? "ordering cycle" : "dependency",
405
                                                    *unit_id, *job_type),
406
                                   LOG_ITEM("%s", strna(unit_ids)));
407

408
                if (delete) {
4✔
409
                        const char *status;
2✔
410
                        /* logging for j not k here to provide a consistent narrative */
411
                        log_struct(LOG_ERR,
2✔
412
                                   LOG_UNIT_MESSAGE(j->unit,
413
                                                    "Job %s/%s deleted to break ordering cycle starting with %s/%s",
414
                                                    delete->unit->id, job_type_to_string(delete->type),
415
                                                    j->unit->id, job_type_to_string(j->type)),
416
                                   LOG_ITEM("%s", strna(unit_ids)));
417

418
                        if (log_get_show_color())
2✔
419
                                status = ANSI_HIGHLIGHT_RED " SKIP " ANSI_NORMAL;
420
                        else
421
                                status = " SKIP ";
2✔
422

423
                        unit_status_printf(delete->unit,
2✔
424
                                           STATUS_TYPE_NOTICE,
425
                                           status,
426
                                           "Ordering cycle found, skipping %s",
427
                                           unit_status_string(delete->unit, NULL));
428
                        transaction_delete_unit(tr, delete->unit);
2✔
429
                        return -EAGAIN;
430
                }
431

432
                log_struct(LOG_ERR,
2✔
433
                           LOG_UNIT_MESSAGE(j->unit, "Unable to break cycle starting with %s/%s",
434
                                            j->unit->id, job_type_to_string(j->type)),
435
                           LOG_ITEM("%s", strna(unit_ids)));
436

437
                return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_ORDER_IS_CYCLIC,
2✔
438
                                         "Transaction order is cyclic. See system logs for details.");
439
        }
440

441
        /* Make the marker point to where we come from, so that we can
442
         * find our way backwards if we want to break a cycle. We use
443
         * a special marker for the beginning: we point to
444
         * ourselves. */
445
        j->marker = from ?: j;
30,665✔
446
        j->generation = generation;
30,665✔
447

448
        /* Actual ordering of jobs depends on the unit ordering dependency and job types. We need to traverse
449
         * the graph over 'before' edges in the actual job execution order. We traverse over both unit
450
         * ordering dependencies and we test with job_compare() whether it is the 'before' edge in the job
451
         * execution ordering. */
452
        FOREACH_ELEMENT(d, directions) {
91,973✔
453
                Unit *u;
61,319✔
454

455
                UNIT_FOREACH_DEPENDENCY(u, j->unit, *d) {
307,045✔
456
                        Job *o;
149,680✔
457

458
                        /* Is there a job for this unit? */
459
                        o = hashmap_get(tr->jobs, u);
149,680✔
460
                        if (!o) {
149,680✔
461
                                /* Ok, there is no job for this in the transaction, but maybe there is
462
                                 * already one running? */
463
                                o = u->job;
81,518✔
464
                                if (!o)
81,518✔
465
                                        continue;
69,015✔
466
                        }
467

468
                        /* Cut traversing if the job j is not really *before* o. */
469
                        if (job_compare(j, o, *d) >= 0)
80,665✔
470
                                continue;
41,637✔
471

472
                        r = transaction_verify_order_one(tr, o, j, generation, e);
39,028✔
473
                        if (r < 0)
39,028✔
474
                                return r;
11✔
475
                }
476
        }
477

478
        /* Ok, let's backtrack, and remember that this entry is not on
479
         * our path anymore. */
480
        j->marker = NULL;
30,654✔
481

482
        return 0;
30,654✔
483
}
484

485
static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bus_error *e) {
13,389✔
486
        Job *j;
13,389✔
487
        int r;
13,389✔
488
        unsigned g;
13,389✔
489

490
        assert(tr);
13,389✔
491
        assert(generation);
13,389✔
492

493
        /* Check if the ordering graph is cyclic. If it is, try to fix
494
         * that up by dropping one of the jobs. */
495

496
        g = (*generation)++;
13,389✔
497

498
        HASHMAP_FOREACH(j, tr->jobs) {
41,755✔
499
                r = transaction_verify_order_one(tr, j, NULL, g, e);
28,370✔
500
                if (r < 0)
28,370✔
501
                        return r;
4✔
502
        }
503

504
        return 0;
13,385✔
505
}
506

507
static void transaction_collect_garbage(Transaction *tr) {
13,199✔
508
        bool again;
13,199✔
509

510
        assert(tr);
13,199✔
511

512
        /* Drop jobs that are not required by any other job */
513

514
        do {
18,675✔
515
                Job *j;
18,675✔
516

517
                again = false;
18,675✔
518

519
                HASHMAP_FOREACH(j, tr->jobs) {
73,777✔
520
                        if (tr->anchor_job == j)
41,903✔
521
                                continue;
15,719✔
522

523
                        if (!j->object_list) {
26,184✔
524
                                log_trace("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type));
5,476✔
525
                                transaction_delete_job(tr, j, true);
5,476✔
526
                                again = true;
527
                                break;
528
                        }
529

530
                        log_trace("Keeping job %s/%s because of %s/%s",
55,102✔
531
                                  j->unit->id, job_type_to_string(j->type),
532
                                  j->object_list->subject ? j->object_list->subject->unit->id : "root",
533
                                  j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root");
534
                }
535

536
        } while (again);
18,675✔
537
}
13,199✔
538

539
static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) {
13,385✔
540
        Job *j;
13,385✔
541

542
        assert(tr);
13,385✔
543

544
        /* Checks whether applying this transaction means that
545
         * existing jobs would be replaced */
546

547
        HASHMAP_FOREACH(j, tr->jobs) {
41,730✔
548

549
                /* Assume merged */
550
                assert(!j->transaction_prev);
28,347✔
551
                assert(!j->transaction_next);
28,347✔
552

553
                if (j->unit->job && (mode == JOB_FAIL || j->unit->job->irreversible) &&
28,384✔
554
                    job_type_is_conflicting(j->unit->job->type, j->type))
37✔
555
                        return sd_bus_error_setf(e, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE,
4✔
556
                                                 "Transaction for %s/%s is destructive (%s has '%s' job queued, but '%s' is included in transaction).",
557
                                                 tr->anchor_job->unit->id, job_type_to_string(tr->anchor_job->type),
2✔
558
                                                 j->unit->id, job_type_to_string(j->unit->job->type), job_type_to_string(j->type));
559
        }
560

561
        return 0;
13,383✔
562
}
563

564
static void transaction_minimize_impact(Transaction *tr) {
227✔
565
        Job *head;
227✔
566

567
        assert(tr);
227✔
568

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

572
rescan:
228✔
573
        HASHMAP_FOREACH(head, tr->jobs) {
9,734✔
574
                LIST_FOREACH(transaction, j, head) {
19,013✔
575
                        bool stops_running_service, changes_existing_job;
9,507✔
576

577
                        /* If it matters, we shouldn't drop it */
578
                        if (j->matters_to_anchor)
9,507✔
579
                                continue;
1,856✔
580

581
                        /* Would this stop a running service?
582
                         * Would this change an existing job?
583
                         * If so, let's drop this entry */
584

585
                        stops_running_service =
15,302✔
586
                                j->type == JOB_STOP && UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(j->unit));
7,651✔
587

588
                        changes_existing_job =
15,302✔
589
                                j->unit->job &&
7,669✔
590
                                job_type_is_conflicting(j->type, j->unit->job->type);
18✔
591

592
                        if (!stops_running_service && !changes_existing_job)
7,651✔
593
                                continue;
7,650✔
594

595
                        if (stops_running_service)
1✔
596
                                log_unit_debug(j->unit,
×
597
                                               "%s/%s would stop a running service.",
598
                                               j->unit->id, job_type_to_string(j->type));
599

600
                        if (changes_existing_job)
1✔
601
                                log_unit_debug(j->unit,
1✔
602
                                               "%s/%s would change existing job.",
603
                                               j->unit->id, job_type_to_string(j->type));
604

605
                        /* Ok, let's get rid of this */
606
                        log_unit_debug(j->unit,
1✔
607
                                       "Deleting %s/%s to minimize impact.",
608
                                       j->unit->id, job_type_to_string(j->type));
609

610
                        transaction_delete_job(tr, j, true);
1✔
611
                        goto rescan;
1✔
612
                }
613
        }
614
}
227✔
615

616
static int transaction_apply(
13,383✔
617
                Transaction *tr,
618
                Manager *m,
619
                JobMode mode,
620
                Set *affected_jobs) {
621

622
        Job *j;
13,383✔
623
        int r;
13,383✔
624

625
        assert(tr);
13,383✔
626
        assert(m);
13,383✔
627

628
        /* Moves the transaction jobs to the set of active jobs */
629

630
        if (IN_SET(mode, JOB_ISOLATE, JOB_FLUSH)) {
13,383✔
631

632
                /* When isolating first kill all installed jobs which
633
                 * aren't part of the new transaction */
634
                HASHMAP_FOREACH(j, m->jobs) {
206✔
635
                        assert(j->installed);
15✔
636

637
                        if (j->unit->ignore_on_isolate)
15✔
638
                                continue;
×
639

640
                        if (hashmap_contains(tr->jobs, j->unit))
15✔
641
                                continue;
15✔
642

643
                        /* Not invalidating recursively. Avoids triggering
644
                         * OnFailure= actions of dependent jobs. Also avoids
645
                         * invalidating our iterator. */
646
                        job_finish_and_invalidate(j, JOB_CANCELED, false, false);
×
647
                }
648
        }
649

650
        HASHMAP_FOREACH(j, tr->jobs) {
41,728✔
651
                /* Assume merged */
652
                assert(!j->transaction_prev);
28,345✔
653
                assert(!j->transaction_next);
28,345✔
654

655
                r = hashmap_ensure_put(&m->jobs, NULL, UINT32_TO_PTR(j->id), j);
28,345✔
656
                if (r < 0)
28,345✔
657
                        goto rollback;
×
658
        }
659

660
        while ((j = hashmap_steal_first(tr->jobs))) {
55,111✔
661
                Job *installed_job;
28,345✔
662

663
                /* Clean the job dependencies */
664
                transaction_unlink_job(tr, j, false);
28,345✔
665

666
                installed_job = job_install(j);
28,345✔
667
                if (installed_job != j) {
28,345✔
668
                        /* j has been merged into a previously installed job */
669
                        if (tr->anchor_job == j)
349✔
670
                                tr->anchor_job = installed_job;
289✔
671

672
                        hashmap_remove_value(m->jobs, UINT32_TO_PTR(j->id), j);
349✔
673
                        free_and_replace_full(j, installed_job, job_free);
349✔
674
                }
675

676
                job_add_to_run_queue(j);
28,345✔
677
                job_add_to_dbus_queue(j);
28,345✔
678
                job_start_timer(j, false);
28,345✔
679
                job_shutdown_magic(j);
28,345✔
680

681
                /* When 'affected' is specified, let's track all in it all jobs that were touched because of
682
                 * this transaction. */
683
                if (affected_jobs)
28,345✔
684
                        (void) set_put(affected_jobs, j);
×
685
        }
686

687
        return 0;
13,383✔
688

689
rollback:
×
690

691
        HASHMAP_FOREACH(j, tr->jobs)
×
692
                hashmap_remove_value(m->jobs, UINT32_TO_PTR(j->id), j);
×
693

694
        return r;
×
695
}
696

697
int transaction_activate(
13,387✔
698
                Transaction *tr,
699
                Manager *m,
700
                JobMode mode,
701
                Set *affected_jobs,
702
                sd_bus_error *e) {
703

704
        Job *j;
13,387✔
705
        int r;
13,387✔
706
        unsigned generation = 1;
13,387✔
707

708
        /* This applies the changes recorded in tr->jobs to the actual list of jobs, if possible. */
709

710
        assert(tr);
13,387✔
711
        assert(m);
13,387✔
712

713
        /* Reset the generation counter of all installed jobs. The detection of cycles
714
         * looks at installed jobs. If they had a non-zero generation from some previous
715
         * walk of the graph, the algorithm would break. */
716
        HASHMAP_FOREACH(j, m->jobs)
115,356✔
717
                j->generation = 0;
101,969✔
718

719
        /* First step: figure out which jobs matter */
720
        transaction_find_jobs_that_matter_to_anchor(tr->anchor_job, generation++);
13,387✔
721

722
        /* Second step: Try not to stop any running services if
723
         * we don't have to. Don't try to reverse running
724
         * jobs if we don't have to. */
725
        if (mode == JOB_FAIL)
13,387✔
726
                transaction_minimize_impact(tr);
227✔
727

728
        /* Third step: Drop redundant jobs */
729
        transaction_drop_redundant(tr);
13,387✔
730

731
        for (;;) {
13,389✔
732
                /* Fourth step: Let's remove unneeded jobs that might
733
                 * be lurking. */
734
                if (mode != JOB_ISOLATE)
13,389✔
735
                        transaction_collect_garbage(tr);
13,198✔
736

737
                /* Fifth step: verify order makes sense and correct
738
                 * cycles if necessary and possible */
739
                r = transaction_verify_order(tr, &generation, e);
13,389✔
740
                if (r >= 0)
13,389✔
741
                        break;
742
                if (r != -EAGAIN)
4✔
743
                        return log_warning_errno(r, "Requested transaction contains an unfixable cyclic ordering dependency: %s", bus_error_message(e, r));
2✔
744

745
                /* Let's see if the resulting transaction ordering
746
                 * graph is still cyclic... */
747
        }
748

749
        for (;;) {
13,398✔
750
                /* Sixth step: let's drop unmergeable entries if
751
                 * necessary and possible, merge entries we can
752
                 * merge */
753
                r = transaction_merge_jobs(tr, e);
13,398✔
754
                if (r >= 0)
13,398✔
755
                        break;
756
                if (r != -EAGAIN)
13✔
757
                        return log_warning_errno(r, "Requested transaction contains unmergeable jobs: %s", bus_error_message(e, r));
×
758

759
                /* Seventh step: an entry got dropped, let's garbage
760
                 * collect its dependencies. */
761
                if (mode != JOB_ISOLATE)
13✔
762
                        transaction_collect_garbage(tr);
1✔
763

764
                /* Let's see if the resulting transaction still has
765
                 * unmergeable entries ... */
766
        }
767

768
        /* Eights step: Drop redundant jobs again, if the merging now allows us to drop more. */
769
        transaction_drop_redundant(tr);
13,385✔
770

771
        /* Ninth step: check whether we can actually apply this */
772
        r = transaction_is_destructive(tr, mode, e);
13,385✔
773
        if (r < 0)
13,385✔
774
                return log_notice_errno(r, "Requested transaction contradicts existing jobs: %s", bus_error_message(e, r));
2✔
775

776
        /* Tenth step: apply changes */
777
        r = transaction_apply(tr, m, mode, affected_jobs);
13,383✔
778
        if (r < 0)
13,383✔
779
                return log_warning_errno(r, "Failed to apply transaction: %m");
×
780

781
        assert(hashmap_isempty(tr->jobs));
13,383✔
782

783
        /* Are there any jobs now? Then make sure we have the idle pipe around. We don't really care too much
784
         * whether this works or not, as the idle pipe is a feature for cosmetics, not actually useful for
785
         * anything beyond that. */
786
        if (!hashmap_isempty(m->jobs))
13,383✔
787
                (void) manager_allocate_idle_pipe(m);
13,383✔
788

789
        return 0;
790
}
791

792
static Job* transaction_add_one_job(Transaction *tr, JobType type, Unit *unit, bool *is_new) {
98,750✔
793
        Job *j, *f;
98,750✔
794

795
        assert(tr);
98,750✔
796
        assert(unit);
98,750✔
797

798
        /* Looks for an existing prospective job and returns that. If
799
         * it doesn't exist it is created and added to the prospective
800
         * jobs list. */
801

802
        f = hashmap_get(tr->jobs, unit);
98,750✔
803

804
        LIST_FOREACH(transaction, i, f) {
98,783✔
805
                assert(i->unit == unit);
49,278✔
806

807
                if (i->type == type) {
49,278✔
808
                        if (is_new)
49,245✔
809
                                *is_new = false;
49,245✔
810
                        return i;
49,245✔
811
                }
812
        }
813

814
        j = job_new(unit, type);
49,505✔
815
        if (!j)
49,505✔
816
                return NULL;
817

818
        j->irreversible = tr->irreversible;
49,505✔
819

820
        LIST_PREPEND(transaction, f, j);
49,505✔
821

822
        if (hashmap_replace(tr->jobs, unit, f) < 0) {
49,505✔
823
                LIST_REMOVE(transaction, f, j);
×
824
                job_free(j);
×
825
                return NULL;
×
826
        }
827

828
        if (is_new)
49,505✔
829
                *is_new = true;
49,505✔
830

831
        log_trace("Added job %s/%s to transaction.", unit->id, job_type_to_string(type));
832

833
        return j;
834
}
835

836
static void transaction_unlink_job(Transaction *tr, Job *j, bool delete_dependencies) {
49,505✔
837
        assert(tr);
49,505✔
838
        assert(j);
49,505✔
839

840
        if (j->transaction_prev)
49,505✔
841
                j->transaction_prev->transaction_next = j->transaction_next;
8✔
842
        else if (j->transaction_next)
49,497✔
843
                hashmap_replace(tr->jobs, j->unit, j->transaction_next);
22✔
844
        else
845
                hashmap_remove_value(tr->jobs, j->unit, j);
49,475✔
846

847
        if (j->transaction_next)
49,505✔
848
                j->transaction_next->transaction_prev = j->transaction_prev;
22✔
849

850
        j->transaction_prev = j->transaction_next = NULL;
49,505✔
851

852
        while (j->subject_list)
80,443✔
853
                job_dependency_free(j->subject_list);
30,938✔
854

855
        while (j->object_list) {
103,927✔
856
                Job *other = j->object_list->matters ? j->object_list->subject : NULL;
54,422✔
857

858
                job_dependency_free(j->object_list);
54,422✔
859

860
                if (other && delete_dependencies) {
54,422✔
861
                        log_unit_debug(other->unit,
14✔
862
                                       "Deleting job %s/%s as dependency of job %s/%s",
863
                                       other->unit->id, job_type_to_string(other->type),
864
                                       j->unit->id, job_type_to_string(j->type));
865
                        transaction_delete_job(tr, other, delete_dependencies);
14✔
866
                }
867
        }
868
}
49,505✔
869

870
void transaction_add_propagate_reload_jobs(
12,115✔
871
                Transaction *tr,
872
                Unit *unit,
873
                Job *by,
874
                TransactionAddFlags flags) {
875

876
        JobType nt;
12,115✔
877
        Unit *dep;
12,115✔
878
        int r;
12,115✔
879

880
        assert(tr);
12,115✔
881
        assert(unit);
12,115✔
882

883
        UNIT_FOREACH_DEPENDENCY(dep, unit, UNIT_ATOM_PROPAGATES_RELOAD_TO) {
24,230✔
884
                _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
×
885

886
                nt = job_type_collapse(JOB_TRY_RELOAD, dep);
×
887
                if (nt == JOB_NOP)
×
888
                        continue;
×
889

890
                r = transaction_add_job_and_dependencies(tr, nt, dep, by, flags, &e);
×
891
                if (r < 0)
×
892
                        log_unit_warning(dep,
×
893
                                         "Cannot add dependency reload job, ignoring: %s",
894
                                         bus_error_message(&e, r));
895
        }
896
}
12,115✔
897

898
static JobType job_type_propagate_stop_graceful(Job *j) {
×
899
        JobType type;
×
900

901
        if (!j)
×
902
                return JOB_STOP;
903

904
        type = JOB_STOP;
905

906
        LIST_FOREACH(transaction, i, j)
×
907
                switch (i->type) {
×
908

909
                case JOB_STOP:
910
                case JOB_RESTART:
911
                        /* Nothing to worry about, an appropriate job is in-place */
912
                        return JOB_NOP;
913

914
                case JOB_START:
×
915
                        /* This unit is pulled in by other dependency types in this transaction. We will run
916
                         * into job type conflict if we enqueue a stop job, so let's enqueue a restart job
917
                         * instead. */
918
                        type = JOB_RESTART;
×
919
                        break;
×
920

921
                default: /* We don't care about others */
922
                        ;
923

924
                }
925

926
        return type;
927
}
928

929
int transaction_add_job_and_dependencies(
99,575✔
930
                Transaction *tr,
931
                JobType type,
932
                Unit *unit,
933
                Job *by,
934
                TransactionAddFlags flags,
935
                sd_bus_error *e) {
936

937
        bool is_new;
99,575✔
938
        Job *ret;
99,575✔
939
        int r;
99,575✔
940

941
        assert(tr);
99,575✔
942
        assert(type >= 0);
99,575✔
943
        assert(type < _JOB_TYPE_MAX);
99,575✔
944
        assert(type < _JOB_TYPE_MAX_IN_TRANSACTION);
99,575✔
945
        assert(unit);
99,575✔
946

947
        /* Before adding jobs for this unit, let's ensure that its state has been loaded This matters when
948
         * jobs are spawned as part of coldplugging itself (see e. g. path_coldplug()).  This way, we
949
         * "recursively" coldplug units, ensuring that we do not look at state of not-yet-coldplugged
950
         * units. */
951
        if (MANAGER_IS_RELOADING(unit->manager))
99,575✔
952
                unit_coldplug(unit);
×
953

954
        if (by)
99,575✔
955
                log_trace("Pulling in %s/%s from %s/%s", unit->id, job_type_to_string(type), by->unit->id, job_type_to_string(by->type));
99,575✔
956

957
        /* Safety check that the unit is a valid state, i.e. not in UNIT_STUB or UNIT_MERGED which should only be set
958
         * temporarily. */
959
        if (!UNIT_IS_LOAD_COMPLETE(unit->load_state))
99,575✔
960
                return sd_bus_error_setf(e, BUS_ERROR_LOAD_FAILED, "Unit %s is not loaded properly.", unit->id);
99,575✔
961

962
        if (type != JOB_STOP) {
99,575✔
963
                r = bus_unit_validate_load_state(unit, e);
61,621✔
964
                /* The time-based cache allows new units to be started without daemon-reload, but if they are
965
                 * already referenced (because of dependencies or ordering) then we have to force a load of
966
                 * the fragment. As an optimization, check first if anything in the usual paths was modified
967
                 * since the last time the cache was loaded. Also check if the last time an attempt to load
968
                 * the unit was made was before the most recent cache refresh, so that we know we need to try
969
                 * again — even if the cache is current, it might have been updated in a different context
970
                 * before we had a chance to retry loading this particular unit.
971
                 *
972
                 * Given building up the transaction is a synchronous operation, attempt
973
                 * to load the unit immediately. */
974
                if (r < 0 && manager_unit_cache_should_retry_load(unit)) {
61,621✔
975
                        sd_bus_error_free(e);
×
976
                        unit->load_state = UNIT_STUB;
×
977
                        r = unit_load(unit);
×
978
                        if (r < 0 || unit->load_state == UNIT_STUB)
×
979
                                unit->load_state = UNIT_NOT_FOUND;
×
980
                        r = bus_unit_validate_load_state(unit, e);
×
981
                }
982
                if (r < 0)
823✔
983
                        return r;
984
        }
985

986
        if (!unit_job_is_applicable(unit, type))
98,752✔
987
                return sd_bus_error_setf(e, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
×
988
                                         "Job type %s is not applicable for unit %s.",
989
                                         job_type_to_string(type), unit->id);
990

991
        if (type == JOB_START) {
98,752✔
992
                /* The hard concurrency limit for slice units we already enforce when a job is enqueued */
993
                Slice *slice = SLICE(UNIT_GET_SLICE(unit));
48,665✔
994
                if (slice && slice_concurrency_hard_max_reached(slice, unit))
35,634✔
995
                        return sd_bus_error_setf(
4✔
996
                                        e, BUS_ERROR_CONCURRENCY_LIMIT_REACHED,
997
                                        "Concurrency limit of the slice unit '%s' (or any of its parents) the unit '%s' is contained in has been reached, refusing start job.",
998
                                        UNIT(slice)->id, unit->id);
2✔
999
        }
1000

1001
        /* First add the job. */
1002
        ret = transaction_add_one_job(tr, type, unit, &is_new);
98,750✔
1003
        if (!ret)
98,750✔
1004
                return -ENOMEM;
1005

1006
        if (FLAGS_SET(flags, TRANSACTION_IGNORE_ORDER))
98,750✔
1007
                ret->ignore_order = true;
12,112✔
1008

1009
        /* Then, add a link to the job. */
1010
        if (by) {
98,750✔
1011
                if (!job_dependency_new(by, ret, FLAGS_SET(flags, TRANSACTION_MATTERS), FLAGS_SET(flags, TRANSACTION_CONFLICTS)))
85,360✔
1012
                        return -ENOMEM;
1013
        } else {
1014
                /* If the job has no parent job, it is the anchor job. */
1015
                assert(!tr->anchor_job);
13,390✔
1016
                tr->anchor_job = ret;
13,390✔
1017

1018
                if (FLAGS_SET(flags, TRANSACTION_REENQUEUE_ANCHOR))
13,390✔
1019
                        ret->refuse_late_merge = true;
5✔
1020
        }
1021

1022
        if (!is_new || FLAGS_SET(flags, TRANSACTION_IGNORE_REQUIREMENTS) || type == JOB_NOP)
98,750✔
1023
                return 0;
1024

1025
        _cleanup_set_free_ Set *following = NULL;
37,393✔
1026
        Unit *dep;
37,393✔
1027

1028
        /* If we are following some other unit, make sure we add all dependencies of everybody following. */
1029
        if (unit_following_set(ret->unit, &following) > 0)
37,393✔
1030
                SET_FOREACH(dep, following) {
2,800✔
1031
                        r = transaction_add_job_and_dependencies(tr, type, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
2,410✔
1032
                        if (r < 0) {
2,410✔
1033
                                log_unit_full_errno(dep, r == -ERFKILL ? LOG_INFO : LOG_WARNING, r,
×
1034
                                                    "Cannot add dependency job, ignoring: %s",
1035
                                                    bus_error_message(e, r));
1036
                                sd_bus_error_free(e);
×
1037
                        }
1038
                }
1039

1040
        /* Finally, recursively add in all dependencies. */
1041
        if (IN_SET(type, JOB_START, JOB_RESTART)) {
37,393✔
1042
                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START) {
291,847✔
1043
                        r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
22,929✔
1044
                        if (r < 0) {
22,929✔
1045
                                if (r != -EBADR) /* job type not applicable */
3✔
1046
                                        goto fail;
3✔
1047

1048
                                sd_bus_error_free(e);
×
1049
                        }
1050
                }
1051

1052
                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_START_IGNORED) {
277,244✔
1053
                        r = transaction_add_job_and_dependencies(tr, JOB_START, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
22,933✔
1054
                        if (r < 0) {
22,933✔
1055
                                /* unit masked, job type not applicable and unit not found are not considered
1056
                                 * as errors. */
1057
                                log_unit_full_errno(dep,
817✔
1058
                                                    IN_SET(r, -ERFKILL, -EBADR, -ENOENT) ? LOG_DEBUG : LOG_WARNING,
1059
                                                    r, "Cannot add dependency job, ignoring: %s",
1060
                                                    bus_error_message(e, r));
1061
                                sd_bus_error_free(e);
817✔
1062
                        }
1063
                }
1064

1065
                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_VERIFY) {
51,988✔
1066
                        r = transaction_add_job_and_dependencies(tr, JOB_VERIFY_ACTIVE, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
×
1067
                        if (r < 0) {
×
1068
                                if (r != -EBADR) /* job type not applicable */
×
1069
                                        goto fail;
×
1070

1071
                                sd_bus_error_free(e);
×
1072
                        }
1073
                }
1074

1075
                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP) {
109,523✔
1076
                        r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, TRANSACTION_MATTERS | TRANSACTION_CONFLICTS | (flags & TRANSACTION_IGNORE_ORDER), e);
21,367✔
1077
                        if (r < 0) {
21,367✔
1078
                                if (r != -EBADR) /* job type not applicable */
×
1079
                                        goto fail;
×
1080

1081
                                sd_bus_error_free(e);
×
1082
                        }
1083
                }
1084

1085
                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PULL_IN_STOP_IGNORED) {
62,438✔
1086
                        r = transaction_add_job_and_dependencies(tr, JOB_STOP, dep, ret, flags & TRANSACTION_IGNORE_ORDER, e);
7,892✔
1087
                        if (r < 0) {
7,892✔
1088
                                log_unit_warning(dep,
×
1089
                                                 "Cannot add dependency job, ignoring: %s",
1090
                                                 bus_error_message(e, r));
1091
                                sd_bus_error_free(e);
×
1092
                        }
1093
                }
1094
        }
1095

1096
        if (IN_SET(type, JOB_RESTART, JOB_STOP) || (type == JOB_START && FLAGS_SET(flags, TRANSACTION_PROPAGATE_START_AS_RESTART))) {
37,390✔
1097
                bool is_stop = type == JOB_STOP;
11,416✔
1098

1099
                UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP) {
114,838✔
1100
                        /* We propagate RESTART only as TRY_RESTART, in order not to start dependencies that
1101
                         * are not around. */
1102
                        JobType nt;
8,244✔
1103

1104
                        nt = job_type_collapse(is_stop ? JOB_STOP : JOB_TRY_RESTART, dep);
8,244✔
1105
                        if (nt == JOB_NOP)
8,244✔
1106
                                continue;
×
1107

1108
                        r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER), e);
8,244✔
1109
                        if (r < 0) {
8,244✔
1110
                                if (r != -EBADR) /* job type not applicable */
×
1111
                                        return r;
×
1112

1113
                                sd_bus_error_free(e);
×
1114
                        }
1115
                }
1116

1117
                /* Process UNIT_ATOM_PROPAGATE_STOP_GRACEFUL (PropagatesStopTo=) units. We need to wait until
1118
                 * all other dependencies are processed, i.e. we're the anchor job or already in the recursion
1119
                 * that handles it. */
1120
                if (!by || FLAGS_SET(flags, TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL))
11,416✔
1121
                        UNIT_FOREACH_DEPENDENCY(dep, ret->unit, UNIT_ATOM_PROPAGATE_STOP_GRACEFUL) {
138✔
1122
                                JobType nt;
×
1123
                                Job *j;
×
1124

1125
                                j = hashmap_get(tr->jobs, dep);
×
1126
                                nt = job_type_propagate_stop_graceful(j);
×
1127

1128
                                if (nt == JOB_NOP)
×
1129
                                        continue;
×
1130

1131
                                r = transaction_add_job_and_dependencies(tr, nt, dep, ret, TRANSACTION_MATTERS | (flags & TRANSACTION_IGNORE_ORDER) | TRANSACTION_PROCESS_PROPAGATE_STOP_GRACEFUL, e);
×
1132
                                if (r < 0) {
×
1133
                                        if (r != -EBADR) /* job type not applicable */
×
1134
                                                return r;
×
1135

1136
                                        sd_bus_error_free(e);
×
1137
                                }
1138
                        }
1139
        }
1140

1141
        if (type == JOB_RELOAD)
37,390✔
1142
                transaction_add_propagate_reload_jobs(tr, ret->unit, ret, flags & TRANSACTION_IGNORE_ORDER);
3✔
1143

1144
        /* JOB_VERIFY_ACTIVE requires no dependency handling */
1145

1146
        return 0;
1147

1148
fail:
3✔
1149
        /* Recursive call failed to add required jobs so let's drop top level job as well. */
1150
        log_unit_debug_errno(unit, r, "Cannot add dependency job to transaction, deleting job %s/%s again: %s",
3✔
1151
                             unit->id, job_type_to_string(type), bus_error_message(e, r));
1152

1153
        transaction_delete_job(tr, ret, /* delete_dependencies= */ false);
3✔
1154
        return r;
1155
}
1156

1157
static bool shall_stop_on_isolate(Transaction *tr, Unit *u) {
10,805✔
1158
        assert(tr);
10,805✔
1159
        assert(u);
10,805✔
1160

1161
        if (u->ignore_on_isolate)
10,805✔
1162
                return false;
1163

1164
        /* Is there already something listed for this? */
1165
        if (hashmap_contains(tr->jobs, u))
622✔
1166
                return false;
207✔
1167

1168
        return true;
1169
}
1170

1171
int transaction_add_isolate_jobs(Transaction *tr, Manager *m) {
191✔
1172
        Unit *u;
191✔
1173
        char *k;
191✔
1174
        int r;
191✔
1175

1176
        assert(tr);
191✔
1177
        assert(m);
191✔
1178

1179
        HASHMAP_FOREACH_KEY(u, k, m->units) {
24,470✔
1180
                _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
24,279✔
1181
                Unit *o;
24,279✔
1182

1183
                /* Ignore aliases */
1184
                if (u->id != k)
24,279✔
1185
                        continue;
353✔
1186

1187
                /* No need to stop inactive units */
1188
                if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)) && !u->job)
23,926✔
1189
                        continue;
13,131✔
1190

1191
                if (!shall_stop_on_isolate(tr, u))
10,795✔
1192
                        continue;
10,390✔
1193

1194
                /* Keep units that are triggered by units we want to keep around. */
1195
                bool keep = false;
405✔
1196
                UNIT_FOREACH_DEPENDENCY(o, u, UNIT_ATOM_TRIGGERED_BY)
830✔
1197
                        if (!shall_stop_on_isolate(tr, o)) {
10✔
1198
                                keep = true;
1199
                                break;
1200
                        }
1201
                if (keep)
405✔
1202
                        continue;
×
1203

1204
                r = transaction_add_job_and_dependencies(tr, JOB_STOP, u, tr->anchor_job, TRANSACTION_MATTERS, &e);
405✔
1205
                if (r < 0)
405✔
1206
                        log_unit_warning_errno(u, r, "Cannot add isolate job, ignoring: %s", bus_error_message(&e, r));
×
1207
        }
1208

1209
        return 0;
191✔
1210
}
1211

1212
int transaction_add_triggering_jobs(Transaction *tr, Unit *u) {
×
1213
        Unit *trigger;
×
1214
        int r;
×
1215

1216
        assert(tr);
×
1217
        assert(u);
×
1218

1219
        UNIT_FOREACH_DEPENDENCY(trigger, u, UNIT_ATOM_TRIGGERED_BY) {
×
1220
                _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
×
1221

1222
                /* No need to stop inactive jobs */
1223
                if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(trigger)) && !trigger->job)
×
1224
                        continue;
×
1225

1226
                /* Is there already something listed for this? */
1227
                if (hashmap_contains(tr->jobs, trigger))
×
1228
                        continue;
×
1229

1230
                r = transaction_add_job_and_dependencies(tr, JOB_STOP, trigger, tr->anchor_job, TRANSACTION_MATTERS, &e);
×
1231
                if (r < 0)
×
1232
                        log_unit_warning_errno(u, r, "Cannot add triggered by job, ignoring: %s", bus_error_message(&e, r));
×
1233
        }
1234

1235
        return 0;
×
1236
}
1237

1238
Transaction *transaction_new(bool irreversible) {
13,395✔
1239
        Transaction *tr;
13,395✔
1240

1241
        tr = new0(Transaction, 1);
13,395✔
1242
        if (!tr)
13,395✔
1243
                return NULL;
1244

1245
        tr->jobs = hashmap_new(NULL);
13,395✔
1246
        if (!tr->jobs)
13,395✔
1247
                return mfree(tr);
×
1248

1249
        tr->irreversible = irreversible;
13,395✔
1250

1251
        return tr;
13,395✔
1252
}
1253

1254
Transaction *transaction_free(Transaction *tr) {
13,395✔
1255
        if (!tr)
13,395✔
1256
                return NULL;
1257

1258
        assert(hashmap_isempty(tr->jobs));
13,395✔
1259
        hashmap_free(tr->jobs);
13,395✔
1260

1261
        return mfree(tr);
13,395✔
1262
}
1263

1264
Transaction *transaction_abort_and_free(Transaction *tr) {
12✔
1265
        if (!tr)
12✔
1266
                return NULL;
1267

1268
        transaction_abort(tr);
12✔
1269

1270
        return transaction_free(tr);
12✔
1271
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc