]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/snapshot.c
Revert "core: do not spawn jobs or touch other units during coldplugging"
[thirdparty/systemd.git] / src / core / snapshot.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23
24 #include "unit.h"
25 #include "snapshot.h"
26 #include "unit-name.h"
27 #include "dbus-snapshot.h"
28 #include "bus-common-errors.h"
29
30 static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
31 [SNAPSHOT_DEAD] = UNIT_INACTIVE,
32 [SNAPSHOT_ACTIVE] = UNIT_ACTIVE
33 };
34
35 static void snapshot_init(Unit *u) {
36 Snapshot *s = SNAPSHOT(u);
37
38 assert(s);
39 assert(UNIT(s)->load_state == UNIT_STUB);
40
41 UNIT(s)->ignore_on_isolate = true;
42 UNIT(s)->ignore_on_snapshot = true;
43 UNIT(s)->allow_isolate = true;
44 }
45
46 static void snapshot_set_state(Snapshot *s, SnapshotState state) {
47 SnapshotState old_state;
48 assert(s);
49
50 old_state = s->state;
51 s->state = state;
52
53 if (state != old_state)
54 log_unit_debug(UNIT(s)->id,
55 "%s changed %s -> %s",
56 UNIT(s)->id,
57 snapshot_state_to_string(old_state),
58 snapshot_state_to_string(state));
59
60 unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
61 }
62
63 static int snapshot_load(Unit *u) {
64 Snapshot *s = SNAPSHOT(u);
65
66 assert(u);
67 assert(u->load_state == UNIT_STUB);
68
69 /* Make sure that only snapshots created via snapshot_create()
70 * can be loaded */
71 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
72 return -ENOENT;
73
74 u->load_state = UNIT_LOADED;
75 return 0;
76 }
77
78 static int snapshot_coldplug(Unit *u) {
79 Snapshot *s = SNAPSHOT(u);
80
81 assert(s);
82 assert(s->state == SNAPSHOT_DEAD);
83
84 if (s->deserialized_state != s->state)
85 snapshot_set_state(s, s->deserialized_state);
86
87 return 0;
88 }
89
90 static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
91 Snapshot *s = SNAPSHOT(u);
92
93 assert(s);
94 assert(f);
95
96 fprintf(f,
97 "%sSnapshot State: %s\n"
98 "%sClean Up: %s\n",
99 prefix, snapshot_state_to_string(s->state),
100 prefix, yes_no(s->cleanup));
101 }
102
103 static int snapshot_start(Unit *u) {
104 Snapshot *s = SNAPSHOT(u);
105
106 assert(s);
107 assert(s->state == SNAPSHOT_DEAD);
108
109 snapshot_set_state(s, SNAPSHOT_ACTIVE);
110
111 if (s->cleanup)
112 unit_add_to_cleanup_queue(u);
113
114 return 1;
115 }
116
117 static int snapshot_stop(Unit *u) {
118 Snapshot *s = SNAPSHOT(u);
119
120 assert(s);
121 assert(s->state == SNAPSHOT_ACTIVE);
122
123 snapshot_set_state(s, SNAPSHOT_DEAD);
124 return 1;
125 }
126
127 static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
128 Snapshot *s = SNAPSHOT(u);
129 Unit *other;
130 Iterator i;
131
132 assert(s);
133 assert(f);
134 assert(fds);
135
136 unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
137 unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
138 SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
139 unit_serialize_item(u, f, "wants", other->id);
140
141 return 0;
142 }
143
144 static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
145 Snapshot *s = SNAPSHOT(u);
146 int r;
147
148 assert(u);
149 assert(key);
150 assert(value);
151 assert(fds);
152
153 if (streq(key, "state")) {
154 SnapshotState state;
155
156 state = snapshot_state_from_string(value);
157 if (state < 0)
158 log_unit_debug(u->id, "Failed to parse state value %s", value);
159 else
160 s->deserialized_state = state;
161
162 } else if (streq(key, "cleanup")) {
163
164 r = parse_boolean(value);
165 if (r < 0)
166 log_unit_debug(u->id, "Failed to parse cleanup value %s", value);
167 else
168 s->cleanup = r;
169
170 } else if (streq(key, "wants")) {
171
172 r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true);
173 if (r < 0)
174 return r;
175 } else
176 log_unit_debug(u->id, "Unknown serialization key '%s'", key);
177
178 return 0;
179 }
180
181 _pure_ static UnitActiveState snapshot_active_state(Unit *u) {
182 assert(u);
183
184 return state_translation_table[SNAPSHOT(u)->state];
185 }
186
187 _pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
188 assert(u);
189
190 return snapshot_state_to_string(SNAPSHOT(u)->state);
191 }
192
193 int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **_s) {
194 _cleanup_free_ char *n = NULL;
195 Unit *other, *u = NULL;
196 Iterator i;
197 int r;
198 const char *k;
199
200 assert(m);
201 assert(_s);
202
203 if (name) {
204 if (!unit_name_is_valid(name, TEMPLATE_INVALID))
205 return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
206
207 if (unit_name_to_type(name) != UNIT_SNAPSHOT)
208 return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name);
209
210 if (manager_get_unit(m, name))
211 return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
212
213 } else {
214
215 for (;;) {
216 if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
217 return -ENOMEM;
218
219 if (!manager_get_unit(m, n)) {
220 name = n;
221 break;
222 }
223
224 free(n);
225 n = NULL;
226 }
227 }
228
229 r = manager_load_unit_prepare(m, name, NULL, e, &u);
230 if (r < 0)
231 goto fail;
232
233 u->transient = true;
234 manager_dispatch_load_queue(m);
235 assert(u->load_state == UNIT_LOADED);
236
237 HASHMAP_FOREACH_KEY(other, k, m->units, i) {
238
239 if (other->ignore_on_snapshot ||
240 other->transient)
241 continue;
242
243 if (k != other->id)
244 continue;
245
246 if (UNIT_VTABLE(other)->check_snapshot)
247 if (!UNIT_VTABLE(other)->check_snapshot(other))
248 continue;
249
250 if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
251 continue;
252
253 r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
254 if (r < 0)
255 goto fail;
256 }
257
258 SNAPSHOT(u)->cleanup = cleanup;
259 *_s = SNAPSHOT(u);
260
261 log_unit_info(u->id, "Created snapshot %s.", u->id);
262
263 return 0;
264
265 fail:
266 if (u)
267 unit_add_to_cleanup_queue(u);
268
269 return r;
270 }
271
272 void snapshot_remove(Snapshot *s) {
273 assert(s);
274
275 log_unit_info(UNIT(s)->id, "Removing snapshot %s.", UNIT(s)->id);
276
277 unit_add_to_cleanup_queue(UNIT(s));
278 }
279
280 static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
281 [SNAPSHOT_DEAD] = "dead",
282 [SNAPSHOT_ACTIVE] = "active"
283 };
284
285 DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
286
287 const UnitVTable snapshot_vtable = {
288 .object_size = sizeof(Snapshot),
289
290 .no_alias = true,
291 .no_instances = true,
292 .no_gc = true,
293
294 .init = snapshot_init,
295 .load = snapshot_load,
296
297 .coldplug = snapshot_coldplug,
298
299 .dump = snapshot_dump,
300
301 .start = snapshot_start,
302 .stop = snapshot_stop,
303
304 .serialize = snapshot_serialize,
305 .deserialize_item = snapshot_deserialize_item,
306
307 .active_state = snapshot_active_state,
308 .sub_state_to_string = snapshot_sub_state_to_string,
309
310 .bus_interface = "org.freedesktop.systemd1.Snapshot",
311 .bus_vtable = bus_snapshot_vtable
312 };