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