]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/swap.c
systemctl: add verbs for special units
[thirdparty/systemd.git] / src / swap.c
1 /*-*- Mode: C; c-basic-offset: 8 -*-*/
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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <limits.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/epoll.h>
27 #include <sys/stat.h>
28 #include <sys/swap.h>
29
30 #include "unit.h"
31 #include "swap.h"
32 #include "load-fragment.h"
33 #include "load-dropin.h"
34 #include "unit-name.h"
35 #include "dbus-swap.h"
36 #include "special.h"
37
38 static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = {
39 [SWAP_DEAD] = UNIT_INACTIVE,
40 [SWAP_ACTIVE] = UNIT_ACTIVE,
41 [SWAP_MAINTENANCE] = UNIT_INACTIVE
42 };
43
44 static void swap_init(Unit *u) {
45 Swap *s = SWAP(u);
46
47 assert(s);
48 assert(s->meta.load_state == UNIT_STUB);
49
50 s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1;
51 }
52
53 static void swap_done(Unit *u) {
54 Swap *s = SWAP(u);
55
56 assert(s);
57
58 free(s->what);
59 free(s->parameters_etc_fstab.what);
60 free(s->parameters_proc_swaps.what);
61 free(s->parameters_fragment.what);
62 }
63
64 int swap_add_one_mount_link(Swap *s, Mount *m) {
65 int r;
66
67 assert(s);
68 assert(m);
69
70 if (s->meta.load_state != UNIT_LOADED ||
71 m->meta.load_state != UNIT_LOADED)
72 return 0;
73
74 if (is_device_path(s->what))
75 return 0;
76
77 if (!path_startswith(s->what, m->where))
78 return 0;
79
80 if ((r = unit_add_dependency(UNIT(m), UNIT_BEFORE, UNIT(s), true)) < 0)
81 return r;
82
83 if ((r = unit_add_dependency(UNIT(s), UNIT_REQUIRES, UNIT(m), true)) < 0)
84 return r;
85
86 return 0;
87 }
88
89 static int swap_add_mount_links(Swap *s) {
90 Meta *other;
91 int r;
92
93 assert(s);
94
95 LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_MOUNT])
96 if ((r = swap_add_one_mount_link(s, (Mount*) other)) < 0)
97 return r;
98
99 return 0;
100 }
101
102 static int swap_add_target_links(Swap *s) {
103 Unit *tu;
104 SwapParameters *p;
105 int r;
106
107 assert(s);
108
109 if (s->from_fragment)
110 p = &s->parameters_fragment;
111 else if (s->from_etc_fstab)
112 p = &s->parameters_etc_fstab;
113 else
114 return 0;
115
116 if ((r = manager_load_unit(s->meta.manager, SPECIAL_SWAP_TARGET, NULL, &tu)) < 0)
117 return r;
118
119 if (!p->noauto && p->handle && s->meta.manager->running_as != MANAGER_SESSION)
120 if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0)
121 return r;
122
123 return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true);
124 }
125
126 static int swap_verify(Swap *s) {
127 bool b;
128 char *e;
129
130 if (UNIT(s)->meta.load_state != UNIT_LOADED)
131 return 0;
132
133 if (!(e = unit_name_from_path(s->what, ".swap")))
134 return -ENOMEM;
135
136 b = unit_has_name(UNIT(s), e);
137 free(e);
138
139 if (!b) {
140 log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->meta.id);
141 return -EINVAL;
142 }
143
144 return 0;
145 }
146
147 static int swap_load(Unit *u) {
148 int r;
149 Swap *s = SWAP(u);
150
151 assert(s);
152 assert(u->meta.load_state == UNIT_STUB);
153
154 /* Load a .swap file */
155 if ((r = unit_load_fragment_and_dropin_optional(u)) < 0)
156 return r;
157
158 if (u->meta.load_state == UNIT_LOADED) {
159
160 if (s->meta.fragment_path)
161 s->from_fragment = true;
162
163 if (!s->what) {
164 if (s->parameters_fragment.what)
165 s->what = strdup(s->parameters_fragment.what);
166 else if (s->parameters_etc_fstab.what)
167 s->what = strdup(s->parameters_etc_fstab.what);
168 else if (s->parameters_proc_swaps.what)
169 s->what = strdup(s->parameters_proc_swaps.what);
170 else
171 s->what = unit_name_to_path(u->meta.id);
172
173 if (!s->what)
174 return -ENOMEM;
175 }
176
177 path_kill_slashes(s->what);
178
179 if (!s->meta.description)
180 if ((r = unit_set_description(u, s->what)) < 0)
181 return r;
182
183 if ((r = unit_add_node_link(u, s->what,
184 (u->meta.manager->running_as == MANAGER_INIT ||
185 u->meta.manager->running_as == MANAGER_SYSTEM))) < 0)
186 return r;
187
188 if ((r = swap_add_mount_links(s)) < 0)
189 return r;
190
191 if ((r = swap_add_target_links(s)) < 0)
192 return r;
193 }
194
195 return swap_verify(s);
196 }
197
198 static int swap_find(Manager *m, const char *what, Unit **_u) {
199 Unit *u;
200 char *e;
201
202 assert(m);
203 assert(what);
204 assert(_u);
205
206 /* /proc/swaps and /etc/fstab might refer to this device by
207 * different names (e.g. one by uuid, the other by the kernel
208 * name), we hence need to look for all aliases we are aware
209 * of for this device */
210
211 if (!(e = unit_name_from_path(what, ".device")))
212 return -ENOMEM;
213
214 u = manager_get_unit(m, e);
215 free(e);
216
217 if (u) {
218 Iterator i;
219 const char *d;
220
221 SET_FOREACH(d, u->meta.names, i) {
222 Unit *k;
223
224 if (!(e = unit_name_change_suffix(d, ".swap")))
225 return -ENOMEM;
226
227 k = manager_get_unit(m, e);
228 free(e);
229
230 if (k) {
231 *_u = k;
232 return 0;
233 }
234 }
235 }
236
237 *_u = NULL;
238 return 0;
239 }
240
241 int swap_add_one(
242 Manager *m,
243 const char *what,
244 int priority,
245 bool noauto,
246 bool handle,
247 bool from_proc_swaps) {
248 Unit *u = NULL;
249 char *e = NULL, *w = NULL;
250 bool delete = false;
251 int r;
252 SwapParameters *p;
253
254 assert(m);
255 assert(what);
256
257 if (!(e = unit_name_from_path(what, ".swap")))
258 return -ENOMEM;
259
260 if (!(u = manager_get_unit(m, e)))
261 if ((r = swap_find(m, what, &u)) < 0)
262 goto fail;
263
264 if (!u) {
265 delete = true;
266
267 if (!(u = unit_new(m))) {
268 free(e);
269 return -ENOMEM;
270 }
271 } else
272 delete = false;
273
274 if ((r = unit_add_name(u, e)) < 0)
275 goto fail;
276
277 if (!(w = strdup(what))) {
278 r = -ENOMEM;
279 goto fail;
280 }
281
282 if (from_proc_swaps) {
283 p = &SWAP(u)->parameters_proc_swaps;
284 SWAP(u)->from_proc_swaps = true;
285 } else {
286 p = &SWAP(u)->parameters_etc_fstab;
287 SWAP(u)->from_etc_fstab = true;
288 }
289
290 free(p->what);
291 p->what = w;
292
293 p->priority = priority;
294 p->noauto = noauto;
295 p->handle = handle;
296
297 if (delete)
298 unit_add_to_load_queue(u);
299
300 unit_add_to_dbus_queue(u);
301
302 free(e);
303
304 return 0;
305
306 fail:
307 free(w);
308 free(e);
309
310 if (delete && u)
311 unit_free(u);
312
313 return r;
314 }
315
316 static void swap_set_state(Swap *s, SwapState state) {
317 SwapState old_state;
318 assert(s);
319
320 old_state = s->state;
321 s->state = state;
322
323 if (state != old_state)
324 log_debug("%s changed %s -> %s",
325 UNIT(s)->meta.id,
326 swap_state_to_string(old_state),
327 swap_state_to_string(state));
328
329 unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]);
330 }
331
332 static int swap_coldplug(Unit *u) {
333 Swap *s = SWAP(u);
334 SwapState new_state = SWAP_DEAD;
335
336 assert(s);
337 assert(s->state == SWAP_DEAD);
338
339 if (s->deserialized_state != s->state)
340 new_state = s->deserialized_state;
341 else if (s->from_proc_swaps)
342 new_state = SWAP_ACTIVE;
343
344 if (new_state != s->state)
345 swap_set_state(s, new_state);
346
347 return 0;
348 }
349
350 static void swap_dump(Unit *u, FILE *f, const char *prefix) {
351 Swap *s = SWAP(u);
352 SwapParameters *p;
353
354 assert(s);
355 assert(f);
356
357 if (s->from_proc_swaps)
358 p = &s->parameters_proc_swaps;
359 else if (s->from_fragment)
360 p = &s->parameters_fragment;
361 else
362 p = &s->parameters_etc_fstab;
363
364 fprintf(f,
365 "%sSwap State: %s\n"
366 "%sWhat: %s\n"
367 "%sPriority: %i\n"
368 "%sNoAuto: %s\n"
369 "%sHandle: %s\n"
370 "%sFrom /etc/fstab: %s\n"
371 "%sFrom /proc/swaps: %s\n"
372 "%sFrom fragment: %s\n",
373 prefix, swap_state_to_string(s->state),
374 prefix, s->what,
375 prefix, p->priority,
376 prefix, yes_no(p->noauto),
377 prefix, yes_no(p->handle),
378 prefix, yes_no(s->from_etc_fstab),
379 prefix, yes_no(s->from_proc_swaps),
380 prefix, yes_no(s->from_fragment));
381 }
382
383 static void swap_enter_dead(Swap *s, bool success) {
384 assert(s);
385
386 swap_set_state(s, success ? SWAP_MAINTENANCE : SWAP_DEAD);
387 }
388
389 static int swap_start(Unit *u) {
390 Swap *s = SWAP(u);
391 int priority = -1;
392 int r;
393
394 assert(s);
395 assert(s->state == SWAP_DEAD || s->state == SWAP_MAINTENANCE);
396
397 if (s->from_fragment)
398 priority = s->parameters_fragment.priority;
399 else if (s->from_etc_fstab)
400 priority = s->parameters_etc_fstab.priority;
401
402 r = swapon(s->what, (priority << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK);
403
404 if (r < 0 && errno != EBUSY) {
405 r = -errno;
406 swap_enter_dead(s, false);
407 return r;
408 }
409
410 swap_set_state(s, SWAP_ACTIVE);
411 return 0;
412 }
413
414 static int swap_stop(Unit *u) {
415 Swap *s = SWAP(u);
416 int r;
417
418 assert(s);
419
420 assert(s->state == SWAP_ACTIVE);
421
422 r = swapoff(s->what);
423 swap_enter_dead(s, r >= 0 || errno == EINVAL);
424
425 return 0;
426 }
427
428 static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
429 Swap *s = SWAP(u);
430
431 assert(s);
432 assert(f);
433 assert(fds);
434
435 unit_serialize_item(u, f, "state", swap_state_to_string(s->state));
436
437 return 0;
438 }
439
440 static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) {
441 Swap *s = SWAP(u);
442
443 assert(s);
444 assert(fds);
445
446 if (streq(key, "state")) {
447 SwapState state;
448
449 if ((state = swap_state_from_string(value)) < 0)
450 log_debug("Failed to parse state value %s", value);
451 else
452 s->deserialized_state = state;
453 } else
454 log_debug("Unknown serialization key '%s'", key);
455
456 return 0;
457 }
458
459 static UnitActiveState swap_active_state(Unit *u) {
460 assert(u);
461
462 return state_translation_table[SWAP(u)->state];
463 }
464
465 static const char *swap_sub_state_to_string(Unit *u) {
466 assert(u);
467
468 return swap_state_to_string(SWAP(u)->state);
469 }
470
471 static bool swap_check_gc(Unit *u) {
472 Swap *s = SWAP(u);
473
474 assert(s);
475
476 return s->from_etc_fstab || s->from_proc_swaps;
477 }
478
479 static int swap_load_proc_swaps(Manager *m) {
480 rewind(m->proc_swaps);
481
482 (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n");
483
484 for (;;) {
485 char *dev = NULL, *d;
486 int prio = 0, k;
487
488 if ((k = fscanf(m->proc_swaps,
489 "%ms " /* device/file */
490 "%*s " /* type of swap */
491 "%*s " /* swap size */
492 "%*s " /* used */
493 "%i\n", /* priority */
494 &dev, &prio)) != 2) {
495
496 if (k == EOF)
497 break;
498
499 free(dev);
500 return -EBADMSG;
501 }
502
503 d = cunescape(dev);
504 free(dev);
505
506 if (!d)
507 return -ENOMEM;
508
509 k = swap_add_one(m, d, prio, false, false, true);
510 free(d);
511
512 if (k < 0)
513 return k;
514 }
515
516 return 0;
517 }
518
519 static void swap_shutdown(Manager *m) {
520 assert(m);
521
522 if (m->proc_swaps) {
523 fclose(m->proc_swaps);
524 m->proc_swaps = NULL;
525 }
526 }
527
528 static const char* const swap_state_table[_SWAP_STATE_MAX] = {
529 [SWAP_DEAD] = "dead",
530 [SWAP_ACTIVE] = "active",
531 [SWAP_MAINTENANCE] = "maintenance"
532 };
533
534 DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState);
535
536 static int swap_enumerate(Manager *m) {
537 int r;
538 assert(m);
539
540 if (!m->proc_swaps)
541 if (!(m->proc_swaps = fopen("/proc/swaps", "re")))
542 return -errno;
543
544 if ((r = swap_load_proc_swaps(m)) < 0)
545 swap_shutdown(m);
546
547 return r;
548 }
549
550 const UnitVTable swap_vtable = {
551 .suffix = ".swap",
552
553 .no_instances = true,
554 .no_isolate = true,
555
556 .init = swap_init,
557 .load = swap_load,
558 .done = swap_done,
559
560 .coldplug = swap_coldplug,
561
562 .dump = swap_dump,
563
564 .start = swap_start,
565 .stop = swap_stop,
566
567 .serialize = swap_serialize,
568 .deserialize_item = swap_deserialize_item,
569
570 .active_state = swap_active_state,
571 .sub_state_to_string = swap_sub_state_to_string,
572
573 .check_gc = swap_check_gc,
574
575 .bus_message_handler = bus_swap_message_handler,
576
577 .enumerate = swap_enumerate,
578 .shutdown = swap_shutdown
579 };