chiark / gitweb /
core: do not spawn jobs or touch other units during coldplugging
[elogind.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, Hashmap *deferred_work) {
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 };