]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/snapshot.c
snapshot: snapshots are just a special kind of transient units now
[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-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_debug("%s changed %s -> %s",
55 UNIT(s)->id,
56 snapshot_state_to_string(old_state),
57 snapshot_state_to_string(state));
58
59 unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
60 }
61
62 static int snapshot_load(Unit *u) {
63 Snapshot *s = SNAPSHOT(u);
64
65 assert(u);
66 assert(u->load_state == UNIT_STUB);
67
68 /* Make sure that only snapshots created via snapshot_create()
69 * can be loaded */
70 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
71 return -ENOENT;
72
73 u->load_state = UNIT_LOADED;
74 return 0;
75 }
76
77 static int snapshot_coldplug(Unit *u) {
78 Snapshot *s = SNAPSHOT(u);
79
80 assert(s);
81 assert(s->state == SNAPSHOT_DEAD);
82
83 if (s->deserialized_state != s->state)
84 snapshot_set_state(s, s->deserialized_state);
85
86 return 0;
87 }
88
89 static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
90 Snapshot *s = SNAPSHOT(u);
91
92 assert(s);
93 assert(f);
94
95 fprintf(f,
96 "%sSnapshot State: %s\n"
97 "%sClean Up: %s\n",
98 prefix, snapshot_state_to_string(s->state),
99 prefix, yes_no(s->cleanup));
100 }
101
102 static int snapshot_start(Unit *u) {
103 Snapshot *s = SNAPSHOT(u);
104
105 assert(s);
106 assert(s->state == SNAPSHOT_DEAD);
107
108 snapshot_set_state(s, SNAPSHOT_ACTIVE);
109
110 if (s->cleanup)
111 unit_add_to_cleanup_queue(u);
112
113 return 0;
114 }
115
116 static int snapshot_stop(Unit *u) {
117 Snapshot *s = SNAPSHOT(u);
118
119 assert(s);
120 assert(s->state == SNAPSHOT_ACTIVE);
121
122 snapshot_set_state(s, SNAPSHOT_DEAD);
123 return 0;
124 }
125
126 static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
127 Snapshot *s = SNAPSHOT(u);
128 Unit *other;
129 Iterator i;
130
131 assert(s);
132 assert(f);
133 assert(fds);
134
135 unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
136 unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
137 SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
138 unit_serialize_item(u, f, "wants", other->id);
139
140 return 0;
141 }
142
143 static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
144 Snapshot *s = SNAPSHOT(u);
145 int r;
146
147 assert(u);
148 assert(key);
149 assert(value);
150 assert(fds);
151
152 if (streq(key, "state")) {
153 SnapshotState state;
154
155 state = snapshot_state_from_string(value);
156 if (state < 0)
157 log_debug("Failed to parse state value %s", value);
158 else
159 s->deserialized_state = state;
160
161 } else if (streq(key, "cleanup")) {
162
163 r = parse_boolean(value);
164 if (r < 0)
165 log_debug("Failed to parse cleanup value %s", value);
166 else
167 s->cleanup = r;
168
169 } else if (streq(key, "wants")) {
170
171 r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true);
172 if (r < 0)
173 return r;
174 } else
175 log_debug("Unknown serialization key '%s'", key);
176
177 return 0;
178 }
179
180 _pure_ static UnitActiveState snapshot_active_state(Unit *u) {
181 assert(u);
182
183 return state_translation_table[SNAPSHOT(u)->state];
184 }
185
186 _pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
187 assert(u);
188
189 return snapshot_state_to_string(SNAPSHOT(u)->state);
190 }
191
192 int snapshot_create(Manager *m, const char *name, bool cleanup, DBusError *e, Snapshot **_s) {
193 _cleanup_free_ char *n = NULL;
194 Unit *other, *u = NULL;
195 Iterator i;
196 int r;
197 const char *k;
198
199 assert(m);
200 assert(_s);
201
202 if (name) {
203 if (!unit_name_is_valid(name, false)) {
204 dbus_set_error(e, BUS_ERROR_INVALID_NAME, "Unit name %s is not valid.", name);
205 return -EINVAL;
206 }
207
208 if (unit_name_to_type(name) != UNIT_SNAPSHOT) {
209 dbus_set_error(e, BUS_ERROR_UNIT_TYPE_MISMATCH, "Unit name %s lacks snapshot suffix.", name);
210 return -EINVAL;
211 }
212
213 if (manager_get_unit(m, name)) {
214 dbus_set_error(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
215 return -EEXIST;
216 }
217
218 } else {
219
220 for (;;) {
221 if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
222 return -ENOMEM;
223
224 if (!manager_get_unit(m, n))
225 break;
226
227 free(n);
228 n = NULL;
229 }
230 }
231
232 r = manager_load_unit_prepare(m, name, NULL, e, &u);
233 if (r < 0)
234 goto fail;
235
236 u->transient = true;
237 manager_dispatch_load_queue(m);
238 assert(u->load_state == UNIT_LOADED);
239
240 HASHMAP_FOREACH_KEY(other, k, m->units, i) {
241
242 if (other->ignore_on_snapshot ||
243 other->transient)
244 continue;
245
246 if (k != other->id)
247 continue;
248
249 if (UNIT_VTABLE(other)->check_snapshot)
250 if (!UNIT_VTABLE(other)->check_snapshot(other))
251 continue;
252
253 if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
254 continue;
255
256 r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
257 if (r < 0)
258 goto fail;
259 }
260
261 SNAPSHOT(u)->cleanup = cleanup;
262 *_s = SNAPSHOT(u);
263
264 return 0;
265
266 fail:
267 if (u)
268 unit_add_to_cleanup_queue(u);
269
270 return r;
271 }
272
273 void snapshot_remove(Snapshot *s) {
274 assert(s);
275
276 unit_add_to_cleanup_queue(UNIT(s));
277 }
278
279 static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
280 [SNAPSHOT_DEAD] = "dead",
281 [SNAPSHOT_ACTIVE] = "active"
282 };
283
284 DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
285
286 const UnitVTable snapshot_vtable = {
287 .object_size = sizeof(Snapshot),
288
289 .no_alias = true,
290 .no_instances = true,
291 .no_gc = true,
292
293 .init = snapshot_init,
294
295 .load = snapshot_load,
296 .coldplug = snapshot_coldplug,
297
298 .dump = snapshot_dump,
299
300 .start = snapshot_start,
301 .stop = snapshot_stop,
302
303 .serialize = snapshot_serialize,
304 .deserialize_item = snapshot_deserialize_item,
305
306 .active_state = snapshot_active_state,
307 .sub_state_to_string = snapshot_sub_state_to_string,
308
309 .bus_interface = "org.freedesktop.systemd1.Snapshot",
310 .bus_message_handler = bus_snapshot_message_handler
311 };