]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/snapshot.c
util-lib: split our string related calls from util.[ch] into its own file string...
[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 "bus-common-errors.h"
25 #include "dbus-snapshot.h"
26 #include "string-util.h"
27 #include "unit-name.h"
28 #include "unit.h"
29 #include "snapshot.h"
30
31 static const UnitActiveState state_translation_table[_SNAPSHOT_STATE_MAX] = {
32 [SNAPSHOT_DEAD] = UNIT_INACTIVE,
33 [SNAPSHOT_ACTIVE] = UNIT_ACTIVE
34 };
35
36 static void snapshot_init(Unit *u) {
37 Snapshot *s = SNAPSHOT(u);
38
39 assert(s);
40 assert(UNIT(s)->load_state == UNIT_STUB);
41
42 UNIT(s)->ignore_on_isolate = true;
43 UNIT(s)->ignore_on_snapshot = true;
44 UNIT(s)->allow_isolate = true;
45 }
46
47 static void snapshot_set_state(Snapshot *s, SnapshotState state) {
48 SnapshotState old_state;
49 assert(s);
50
51 old_state = s->state;
52 s->state = state;
53
54 if (state != old_state)
55 log_unit_debug(UNIT(s), "Changed %s -> %s", snapshot_state_to_string(old_state), snapshot_state_to_string(state));
56
57 unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], true);
58 }
59
60 static int snapshot_load(Unit *u) {
61 Snapshot *s = SNAPSHOT(u);
62
63 assert(u);
64 assert(u->load_state == UNIT_STUB);
65
66 /* Make sure that only snapshots created via snapshot_create()
67 * can be loaded */
68 if (!u->transient && UNIT(s)->manager->n_reloading <= 0)
69 return -ENOENT;
70
71 u->load_state = UNIT_LOADED;
72 return 0;
73 }
74
75 static int snapshot_coldplug(Unit *u) {
76 Snapshot *s = SNAPSHOT(u);
77
78 assert(s);
79 assert(s->state == SNAPSHOT_DEAD);
80
81 if (s->deserialized_state != s->state)
82 snapshot_set_state(s, s->deserialized_state);
83
84 return 0;
85 }
86
87 static void snapshot_dump(Unit *u, FILE *f, const char *prefix) {
88 Snapshot *s = SNAPSHOT(u);
89
90 assert(s);
91 assert(f);
92
93 fprintf(f,
94 "%sSnapshot State: %s\n"
95 "%sClean Up: %s\n",
96 prefix, snapshot_state_to_string(s->state),
97 prefix, yes_no(s->cleanup));
98 }
99
100 static int snapshot_start(Unit *u) {
101 Snapshot *s = SNAPSHOT(u);
102
103 assert(s);
104 assert(s->state == SNAPSHOT_DEAD);
105
106 snapshot_set_state(s, SNAPSHOT_ACTIVE);
107
108 if (s->cleanup)
109 unit_add_to_cleanup_queue(u);
110
111 return 1;
112 }
113
114 static int snapshot_stop(Unit *u) {
115 Snapshot *s = SNAPSHOT(u);
116
117 assert(s);
118 assert(s->state == SNAPSHOT_ACTIVE);
119
120 snapshot_set_state(s, SNAPSHOT_DEAD);
121 return 1;
122 }
123
124 static int snapshot_serialize(Unit *u, FILE *f, FDSet *fds) {
125 Snapshot *s = SNAPSHOT(u);
126 Unit *other;
127 Iterator i;
128
129 assert(s);
130 assert(f);
131 assert(fds);
132
133 unit_serialize_item(u, f, "state", snapshot_state_to_string(s->state));
134 unit_serialize_item(u, f, "cleanup", yes_no(s->cleanup));
135 SET_FOREACH(other, u->dependencies[UNIT_WANTS], i)
136 unit_serialize_item(u, f, "wants", other->id);
137
138 return 0;
139 }
140
141 static int snapshot_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
142 Snapshot *s = SNAPSHOT(u);
143 int r;
144
145 assert(u);
146 assert(key);
147 assert(value);
148 assert(fds);
149
150 if (streq(key, "state")) {
151 SnapshotState state;
152
153 state = snapshot_state_from_string(value);
154 if (state < 0)
155 log_unit_debug(u, "Failed to parse state value: %s", value);
156 else
157 s->deserialized_state = state;
158
159 } else if (streq(key, "cleanup")) {
160
161 r = parse_boolean(value);
162 if (r < 0)
163 log_unit_debug(u, "Failed to parse cleanup value: %s", value);
164 else
165 s->cleanup = r;
166
167 } else if (streq(key, "wants")) {
168
169 r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_WANTS, value, NULL, true);
170 if (r < 0)
171 return r;
172 } else
173 log_unit_debug(u, "Unknown serialization key: %s", key);
174
175 return 0;
176 }
177
178 _pure_ static UnitActiveState snapshot_active_state(Unit *u) {
179 assert(u);
180
181 return state_translation_table[SNAPSHOT(u)->state];
182 }
183
184 _pure_ static const char *snapshot_sub_state_to_string(Unit *u) {
185 assert(u);
186
187 return snapshot_state_to_string(SNAPSHOT(u)->state);
188 }
189
190 int snapshot_create(Manager *m, const char *name, bool cleanup, sd_bus_error *e, Snapshot **_s) {
191 _cleanup_free_ char *n = NULL;
192 Unit *other, *u = NULL;
193 Iterator i;
194 int r;
195 const char *k;
196
197 assert(m);
198 assert(_s);
199
200 if (name) {
201 if (!unit_name_is_valid(name, UNIT_NAME_PLAIN))
202 return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
203
204 if (!endswith(name, ".snapshot"))
205 return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name);
206
207 if (manager_get_unit(m, name))
208 return sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
209
210 } else {
211
212 for (;;) {
213 if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
214 return -ENOMEM;
215
216 if (!manager_get_unit(m, n)) {
217 name = n;
218 break;
219 }
220
221 n = mfree(n);
222 }
223 }
224
225 r = manager_load_unit_prepare(m, name, NULL, e, &u);
226 if (r < 0)
227 goto fail;
228
229 u->transient = true;
230 manager_dispatch_load_queue(m);
231 assert(u->load_state == UNIT_LOADED);
232
233 HASHMAP_FOREACH_KEY(other, k, m->units, i) {
234
235 if (other->ignore_on_snapshot ||
236 other->transient)
237 continue;
238
239 if (k != other->id)
240 continue;
241
242 if (UNIT_VTABLE(other)->check_snapshot)
243 if (!UNIT_VTABLE(other)->check_snapshot(other))
244 continue;
245
246 if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
247 continue;
248
249 r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
250 if (r < 0)
251 goto fail;
252 }
253
254 SNAPSHOT(u)->cleanup = cleanup;
255 *_s = SNAPSHOT(u);
256
257 log_unit_info(u, "Created snapshot.");
258
259 return 0;
260
261 fail:
262 if (u)
263 unit_add_to_cleanup_queue(u);
264
265 return r;
266 }
267
268 void snapshot_remove(Snapshot *s) {
269 assert(s);
270
271 log_unit_info(UNIT(s), "Removing snapshot.");
272
273 unit_add_to_cleanup_queue(UNIT(s));
274 }
275
276 const UnitVTable snapshot_vtable = {
277 .object_size = sizeof(Snapshot),
278
279 .no_alias = true,
280 .no_instances = true,
281 .no_gc = true,
282
283 .init = snapshot_init,
284 .load = snapshot_load,
285
286 .coldplug = snapshot_coldplug,
287
288 .dump = snapshot_dump,
289
290 .start = snapshot_start,
291 .stop = snapshot_stop,
292
293 .serialize = snapshot_serialize,
294 .deserialize_item = snapshot_deserialize_item,
295
296 .active_state = snapshot_active_state,
297 .sub_state_to_string = snapshot_sub_state_to_string,
298
299 .bus_vtable = bus_snapshot_vtable
300 };