chiark / gitweb /
journald: log provenience of signals
[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, sd_bus_error *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, TEMPLATE_INVALID))
204                         return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s is not valid.", name);
205
206                 if (unit_name_to_type(name) != UNIT_SNAPSHOT)
207                         return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Unit name %s lacks snapshot suffix.", name);
208
209                 if (manager_get_unit(m, name))
210                         sd_bus_error_setf(e, BUS_ERROR_UNIT_EXISTS, "Snapshot %s exists already.", name);
211
212         } else {
213
214                 for (;;) {
215                         if (asprintf(&n, "snapshot-%u.snapshot", ++ m->n_snapshots) < 0)
216                                 return -ENOMEM;
217
218                         if (!manager_get_unit(m, n)) {
219                                 name = n;
220                                 break;
221                         }
222
223                         free(n);
224                         n = NULL;
225                 }
226         }
227
228         r = manager_load_unit_prepare(m, name, NULL, e, &u);
229         if (r < 0)
230                 goto fail;
231
232         u->transient = true;
233         manager_dispatch_load_queue(m);
234         assert(u->load_state == UNIT_LOADED);
235
236         HASHMAP_FOREACH_KEY(other, k, m->units, i) {
237
238                 if (other->ignore_on_snapshot ||
239                     other->transient)
240                         continue;
241
242                 if (k != other->id)
243                         continue;
244
245                 if (UNIT_VTABLE(other)->check_snapshot)
246                         if (!UNIT_VTABLE(other)->check_snapshot(other))
247                             continue;
248
249                 if (!UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other)))
250                         continue;
251
252                 r = unit_add_two_dependencies(u, UNIT_AFTER, UNIT_WANTS, other, true);
253                 if (r < 0)
254                         goto fail;
255         }
256
257         SNAPSHOT(u)->cleanup = cleanup;
258         *_s = SNAPSHOT(u);
259
260         return 0;
261
262 fail:
263         if (u)
264                 unit_add_to_cleanup_queue(u);
265
266         return r;
267 }
268
269 void snapshot_remove(Snapshot *s) {
270         assert(s);
271
272         unit_add_to_cleanup_queue(UNIT(s));
273 }
274
275 static const char* const snapshot_state_table[_SNAPSHOT_STATE_MAX] = {
276         [SNAPSHOT_DEAD] = "dead",
277         [SNAPSHOT_ACTIVE] = "active"
278 };
279
280 DEFINE_STRING_TABLE_LOOKUP(snapshot_state, SnapshotState);
281
282 const UnitVTable snapshot_vtable = {
283         .object_size = sizeof(Snapshot),
284
285         .no_alias = true,
286         .no_instances = true,
287         .no_gc = true,
288
289         .init = snapshot_init,
290         .load = snapshot_load,
291
292         .coldplug = snapshot_coldplug,
293
294         .dump = snapshot_dump,
295
296         .start = snapshot_start,
297         .stop = snapshot_stop,
298
299         .serialize = snapshot_serialize,
300         .deserialize_item = snapshot_deserialize_item,
301
302         .active_state = snapshot_active_state,
303         .sub_state_to_string = snapshot_sub_state_to_string,
304
305         .bus_interface = "org.freedesktop.systemd1.Snapshot",
306         .bus_vtable = bus_snapshot_vtable
307 };