chiark / gitweb /
udev: declare some symbols static
[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         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                                 name = n;
226                                 break;
227                         }
228
229                         free(n);
230                         n = NULL;
231                 }
232         }
233
234         r = manager_load_unit_prepare(m, name, NULL, e, &u);
235         if (r < 0)
236                 goto fail;
237
238         u->transient = true;
239         manager_dispatch_load_queue(m);
240         assert(u->load_state == UNIT_LOADED);
241
242         HASHMAP_FOREACH_KEY(other, k, m->units, i) {
243
244                 if (other->ignore_on_snapshot ||
245                     other->transient)
246                         continue;
247
248                 if (k != other->id)
249                         continue;
250
251                 if (UNIT_VTABLE(other)->check_snapshot)
252                         if (!UNIT_VTABLE(other)->check_snapshot(other))
253                             continue;
254
255                 if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
256                         continue;
257
258                 r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
259                 if (r < 0)
260                         goto fail;
261         }
262
263         SNAPSHOT(u)->cleanup = cleanup;
264         *_s = SNAPSHOT(u);
265
266         return 0;
267
268 fail:
269         if (u)
270                 unit_add_to_cleanup_queue(u);
271
272         return r;
273 }
274
275 void snapshot_remove(Snapshot *s) {
276         assert(s);
277
278         unit_add_to_cleanup_queue(UNIT(s));
279 }
280
281 static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
282         [SNAPSHOT_DEAD] = "dead",
283         [SNAPSHOT_ACTIVE] = "active"
284 };
285
286 DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
287
288 const UnitVTable snapshot_vtable = {
289         .object_size = sizeof(Snapshot),
290
291         .no_alias = true,
292         .no_instances = true,
293         .no_gc = true,
294
295         .init = snapshot_init,
296
297         .load = snapshot_load,
298         .coldplug = snapshot_coldplug,
299
300         .dump = snapshot_dump,
301
302         .start = snapshot_start,
303         .stop = snapshot_stop,
304
305         .serialize = snapshot_serialize,
306         .deserialize_item = snapshot_deserialize_item,
307
308         .active_state = snapshot_active_state,
309         .sub_state_to_string = snapshot_sub_state_to_string,
310
311         .bus_interface = "org.freedesktop.systemd1.Snapshot",
312         .bus_message_handler = bus_snapshot_message_handler
313 };