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