chiark / gitweb /
systemctl: add compat support for shutting down the system via upstart
[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_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 };