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