]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/target.c
880f858481923d421ae21c3191f7ffbc3be2d151
[thirdparty/systemd.git] / src / core / target.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <stdio.h>
4
5 #include "dbus-unit.h"
6 #include "serialize.h"
7 #include "special.h"
8 #include "string-util.h"
9 #include "target.h"
10 #include "unit.h"
11
12 static const UnitActiveState state_translation_table[_TARGET_STATE_MAX] = {
13 [TARGET_DEAD] = UNIT_INACTIVE,
14 [TARGET_ACTIVE] = UNIT_ACTIVE,
15 };
16
17 static void target_set_state(Target *t, TargetState state) {
18 TargetState old_state;
19
20 assert(t);
21
22 if (t->state != state)
23 bus_unit_send_pending_change_signal(UNIT(t), false);
24
25 old_state = t->state;
26 t->state = state;
27
28 if (state != old_state)
29 log_unit_debug(UNIT(t), "Changed %s -> %s",
30 target_state_to_string(old_state), target_state_to_string(state));
31
32 unit_notify(UNIT(t), state_translation_table[old_state], state_translation_table[state], /* reload_success = */ true);
33 }
34
35 static int target_add_default_dependencies(Target *t) {
36 _cleanup_free_ Unit **others = NULL;
37 int r, n_others;
38
39 assert(t);
40
41 if (!UNIT(t)->default_dependencies)
42 return 0;
43
44 /* Imply ordering for requirement dependencies on target units. Note that when the user created a
45 * contradicting ordering manually we won't add anything in here to make sure we don't create a
46 * loop.
47 *
48 * Note that quite likely iterating through these dependencies will add new dependencies, which
49 * conflicts with the hashmap-based iteration logic. Hence, instead of iterating through the
50 * dependencies and acting on them as we go, first take an "atomic snapshot" of sorts and iterate
51 * through that. */
52
53 n_others = unit_get_dependency_array(UNIT(t), UNIT_ATOM_ADD_DEFAULT_TARGET_DEPENDENCY_QUEUE, &others);
54 if (n_others < 0)
55 return n_others;
56
57 FOREACH_ARRAY(i, others, n_others) {
58 r = unit_add_default_target_dependency(*i, UNIT(t));
59 if (r < 0)
60 return r;
61 }
62
63 if (unit_has_name(UNIT(t), SPECIAL_SHUTDOWN_TARGET))
64 return 0;
65
66 /* Make sure targets are unloaded on shutdown */
67 return unit_add_two_dependencies_by_name(UNIT(t), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, true, UNIT_DEPENDENCY_DEFAULT);
68 }
69
70 static int target_load(Unit *u) {
71 Target *t = ASSERT_PTR(TARGET(u));
72 int r;
73
74 r = unit_load_fragment_and_dropin(u, true);
75 if (r < 0)
76 return r;
77
78 if (u->load_state != UNIT_LOADED)
79 return 0;
80
81 /* This is a new unit? Then let's add in some extras */
82 return target_add_default_dependencies(t);
83 }
84
85 static int target_coldplug(Unit *u) {
86 Target *t = ASSERT_PTR(TARGET(u));
87
88 assert(t->state == TARGET_DEAD);
89
90 if (t->deserialized_state != t->state)
91 target_set_state(t, t->deserialized_state);
92
93 return 0;
94 }
95
96 static void target_dump(Unit *u, FILE *f, const char *prefix) {
97 Target *t = ASSERT_PTR(TARGET(u));
98
99 assert(f);
100 assert(prefix);
101
102 fprintf(f,
103 "%sTarget State: %s\n",
104 prefix, target_state_to_string(t->state));
105 }
106
107 static int target_start(Unit *u) {
108 Target *t = ASSERT_PTR(TARGET(u));
109 int r;
110
111 assert(t->state == TARGET_DEAD);
112
113 r = unit_acquire_invocation_id(u);
114 if (r < 0)
115 return r;
116
117 target_set_state(t, TARGET_ACTIVE);
118 return 1;
119 }
120
121 static int target_stop(Unit *u) {
122 Target *t = ASSERT_PTR(TARGET(u));
123
124 assert(t->state == TARGET_ACTIVE);
125
126 target_set_state(t, TARGET_DEAD);
127 return 1;
128 }
129
130 static int target_serialize(Unit *u, FILE *f, FDSet *fds) {
131 Target *t = ASSERT_PTR(TARGET(u));
132
133 assert(f);
134 assert(fds);
135
136 (void) serialize_item(f, "state", target_state_to_string(t->state));
137 return 0;
138 }
139
140 static int target_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
141 Target *t = ASSERT_PTR(TARGET(u));
142
143 assert(key);
144 assert(value);
145 assert(fds);
146
147 if (streq(key, "state")) {
148 TargetState state;
149
150 state = target_state_from_string(value);
151 if (state < 0)
152 log_unit_debug(u, "Failed to parse state: %s", value);
153 else
154 t->deserialized_state = state;
155
156 } else
157 log_unit_debug(u, "Unknown serialization key: %s", key);
158
159 return 0;
160 }
161
162 static UnitActiveState target_active_state(Unit *u) {
163 Target *t = ASSERT_PTR(TARGET(u));
164
165 return state_translation_table[t->state];
166 }
167
168 static const char *target_sub_state_to_string(Unit *u) {
169 Target *t = ASSERT_PTR(TARGET(u));
170
171 return target_state_to_string(t->state);
172 }
173
174 const UnitVTable target_vtable = {
175 .object_size = sizeof(Target),
176
177 .sections =
178 "Unit\0"
179 "Target\0"
180 "Install\0",
181
182 .can_fail = true,
183
184 .load = target_load,
185 .coldplug = target_coldplug,
186
187 .dump = target_dump,
188
189 .start = target_start,
190 .stop = target_stop,
191
192 .serialize = target_serialize,
193 .deserialize_item = target_deserialize_item,
194
195 .active_state = target_active_state,
196 .sub_state_to_string = target_sub_state_to_string,
197
198 .status_message_formats = {
199 .finished_start_job = {
200 [JOB_DONE] = "Reached target %s.",
201 },
202 .finished_stop_job = {
203 [JOB_DONE] = "Stopped target %s.",
204 },
205 },
206
207 .notify_supervisor = true,
208 };