]>
Commit | Line | Data |
---|---|---|
07b0b134 ML |
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" | |
514f4ef5 | 36 | #include "special.h" |
07b0b134 ML |
37 | |
38 | static const UnitActiveState state_translation_table[_SWAP_STATE_MAX] = { | |
39 | [SWAP_DEAD] = UNIT_INACTIVE, | |
40 | [SWAP_ACTIVE] = UNIT_ACTIVE, | |
032ff4af | 41 | [SWAP_MAINTENANCE] = UNIT_MAINTENANCE |
07b0b134 ML |
42 | }; |
43 | ||
4e85aff4 | 44 | static void swap_init(Unit *u) { |
6e620bec LP |
45 | Swap *s = SWAP(u); |
46 | ||
47 | assert(s); | |
4e85aff4 | 48 | assert(s->meta.load_state == UNIT_STUB); |
6e620bec | 49 | |
4e85aff4 | 50 | s->parameters_etc_fstab.priority = s->parameters_proc_swaps.priority = s->parameters_fragment.priority = -1; |
6e620bec LP |
51 | } |
52 | ||
4e85aff4 LP |
53 | static void swap_done(Unit *u) { |
54 | Swap *s = SWAP(u); | |
07b0b134 | 55 | |
4e85aff4 | 56 | assert(s); |
07b0b134 | 57 | |
4e85aff4 LP |
58 | free(s->what); |
59 | free(s->parameters_etc_fstab.what); | |
60 | free(s->parameters_proc_swaps.what); | |
61 | free(s->parameters_fragment.what); | |
07b0b134 ML |
62 | } |
63 | ||
6e2ef85b LP |
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 | ||
8407a5d0 LP |
74 | if (is_device_path(s->what)) |
75 | return 0; | |
76 | ||
6e2ef85b LP |
77 | if (!path_startswith(s->what, m->where)) |
78 | return 0; | |
79 | ||
2c966c03 | 80 | if ((r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, UNIT(m), true)) < 0) |
6e2ef85b LP |
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 | ||
07b0b134 | 99 | static int swap_add_target_links(Swap *s) { |
07b0b134 | 100 | Unit *tu; |
4e85aff4 | 101 | SwapParameters *p; |
07b0b134 ML |
102 | int r; |
103 | ||
4e85aff4 LP |
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 | ||
398ef8ba | 113 | if ((r = manager_load_unit(s->meta.manager, SPECIAL_SWAP_TARGET, NULL, NULL, &tu)) < 0) |
07b0b134 ML |
114 | return r; |
115 | ||
a3d4e06d | 116 | if (!p->noauto && p->handle && s->meta.manager->running_as == MANAGER_SYSTEM) |
6e2ef85b LP |
117 | if ((r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0) |
118 | return r; | |
07b0b134 ML |
119 | |
120 | return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true); | |
121 | } | |
122 | ||
4e85aff4 LP |
123 | static int swap_verify(Swap *s) { |
124 | bool b; | |
125 | char *e; | |
126 | ||
4cd1fbcc | 127 | if (s->meta.load_state != UNIT_LOADED) |
4e85aff4 LP |
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) { | |
4cd1fbcc | 137 | log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", s->meta.id); |
4e85aff4 LP |
138 | return -EINVAL; |
139 | } | |
140 | ||
141 | return 0; | |
142 | } | |
143 | ||
07b0b134 ML |
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) { | |
4e85aff4 LP |
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) | |
07b0b134 | 171 | return -ENOMEM; |
4e85aff4 | 172 | } |
07b0b134 ML |
173 | |
174 | path_kill_slashes(s->what); | |
175 | ||
4e85aff4 LP |
176 | if (!s->meta.description) |
177 | if ((r = unit_set_description(u, s->what)) < 0) | |
178 | return r; | |
179 | ||
a3d4e06d | 180 | if ((r = unit_add_node_link(u, s->what, u->meta.manager->running_as == MANAGER_SYSTEM)) < 0) |
07b0b134 ML |
181 | return r; |
182 | ||
6e2ef85b LP |
183 | if ((r = swap_add_mount_links(s)) < 0) |
184 | return r; | |
07b0b134 ML |
185 | |
186 | if ((r = swap_add_target_links(s)) < 0) | |
187 | return r; | |
188 | } | |
189 | ||
190 | return swap_verify(s); | |
191 | } | |
192 | ||
4e85aff4 | 193 | static int swap_find(Manager *m, const char *what, Unit **_u) { |
07b0b134 ML |
194 | Unit *u; |
195 | char *e; | |
4e85aff4 LP |
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; | |
2c7c6144 | 245 | bool delete = false; |
07b0b134 | 246 | int r; |
4e85aff4 LP |
247 | SwapParameters *p; |
248 | ||
249 | assert(m); | |
250 | assert(what); | |
07b0b134 ML |
251 | |
252 | if (!(e = unit_name_from_path(what, ".swap"))) | |
253 | return -ENOMEM; | |
254 | ||
4e85aff4 LP |
255 | if (!(u = manager_get_unit(m, e))) |
256 | if ((r = swap_find(m, what, &u)) < 0) | |
257 | goto fail; | |
258 | ||
259 | if (!u) { | |
07b0b134 ML |
260 | delete = true; |
261 | ||
262 | if (!(u = unit_new(m))) { | |
263 | free(e); | |
264 | return -ENOMEM; | |
265 | } | |
4e85aff4 LP |
266 | } else |
267 | delete = false; | |
07b0b134 | 268 | |
4e85aff4 LP |
269 | if ((r = unit_add_name(u, e)) < 0) |
270 | goto fail; | |
07b0b134 | 271 | |
4e85aff4 LP |
272 | if (!(w = strdup(what))) { |
273 | r = -ENOMEM; | |
274 | goto fail; | |
275 | } | |
07b0b134 | 276 | |
4e85aff4 LP |
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 | } | |
07b0b134 | 284 | |
4e85aff4 LP |
285 | free(p->what); |
286 | p->what = w; | |
07b0b134 | 287 | |
4e85aff4 LP |
288 | p->priority = priority; |
289 | p->noauto = noauto; | |
290 | p->handle = handle; | |
07b0b134 | 291 | |
4e85aff4 LP |
292 | if (delete) |
293 | unit_add_to_load_queue(u); | |
07b0b134 | 294 | |
4e85aff4 | 295 | unit_add_to_dbus_queue(u); |
07b0b134 | 296 | |
4e85aff4 | 297 | free(e); |
07b0b134 ML |
298 | |
299 | return 0; | |
300 | ||
301 | fail: | |
4e85aff4 LP |
302 | free(w); |
303 | free(e); | |
304 | ||
305 | if (delete && u) | |
07b0b134 ML |
306 | unit_free(u); |
307 | ||
4e85aff4 | 308 | return r; |
07b0b134 ML |
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", | |
4cd1fbcc | 320 | s->meta.id, |
07b0b134 ML |
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; | |
4e85aff4 | 336 | else if (s->from_proc_swaps) |
07b0b134 ML |
337 | new_state = SWAP_ACTIVE; |
338 | ||
339 | if (new_state != s->state) | |
4e85aff4 | 340 | swap_set_state(s, new_state); |
07b0b134 ML |
341 | |
342 | return 0; | |
343 | } | |
344 | ||
345 | static void swap_dump(Unit *u, FILE *f, const char *prefix) { | |
346 | Swap *s = SWAP(u); | |
4e85aff4 | 347 | SwapParameters *p; |
07b0b134 ML |
348 | |
349 | assert(s); | |
4e85aff4 LP |
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; | |
07b0b134 ML |
358 | |
359 | fprintf(f, | |
4e85aff4 | 360 | "%sSwap State: %s\n" |
07b0b134 ML |
361 | "%sWhat: %s\n" |
362 | "%sPriority: %i\n" | |
4e85aff4 LP |
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", | |
07b0b134 ML |
368 | prefix, swap_state_to_string(s->state), |
369 | prefix, s->what, | |
4e85aff4 LP |
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)); | |
07b0b134 ML |
376 | } |
377 | ||
378 | static void swap_enter_dead(Swap *s, bool success) { | |
379 | assert(s); | |
380 | ||
18c78fb1 | 381 | swap_set_state(s, success ? SWAP_MAINTENANCE : SWAP_DEAD); |
07b0b134 ML |
382 | } |
383 | ||
384 | static int swap_start(Unit *u) { | |
385 | Swap *s = SWAP(u); | |
4e85aff4 | 386 | int priority = -1; |
07b0b134 ML |
387 | int r; |
388 | ||
389 | assert(s); | |
18c78fb1 | 390 | assert(s->state == SWAP_DEAD || s->state == SWAP_MAINTENANCE); |
07b0b134 | 391 | |
4e85aff4 LP |
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); | |
07b0b134 ML |
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 | ||
4e85aff4 | 471 | return s->from_etc_fstab || s->from_proc_swaps; |
07b0b134 ML |
472 | } |
473 | ||
474 | static int swap_load_proc_swaps(Manager *m) { | |
07b0b134 | 475 | rewind(m->proc_swaps); |
bab45044 | 476 | |
4e85aff4 | 477 | (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n"); |
07b0b134 ML |
478 | |
479 | for (;;) { | |
480 | char *dev = NULL, *d; | |
481 | int prio = 0, k; | |
482 | ||
4e85aff4 LP |
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) { | |
07b0b134 | 490 | |
07b0b134 | 491 | if (k == EOF) |
4e85aff4 | 492 | break; |
07b0b134 ML |
493 | |
494 | free(dev); | |
495 | return -EBADMSG; | |
496 | } | |
07b0b134 | 497 | |
4e85aff4 | 498 | d = cunescape(dev); |
07b0b134 | 499 | free(dev); |
4e85aff4 LP |
500 | |
501 | if (!d) | |
502 | return -ENOMEM; | |
503 | ||
504 | k = swap_add_one(m, d, prio, false, false, true); | |
07b0b134 ML |
505 | free(d); |
506 | ||
507 | if (k < 0) | |
508 | return k; | |
509 | } | |
510 | ||
4e85aff4 | 511 | return 0; |
07b0b134 ML |
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", | |
18c78fb1 | 526 | [SWAP_MAINTENANCE] = "maintenance" |
07b0b134 ML |
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 | ||
4e85aff4 LP |
535 | if (!m->proc_swaps) |
536 | if (!(m->proc_swaps = fopen("/proc/swaps", "re"))) | |
537 | return -errno; | |
07b0b134 ML |
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 | ||
07b0b134 | 548 | .no_instances = true, |
6e620bec | 549 | .no_isolate = true, |
9e58ff9c | 550 | .show_status = true, |
07b0b134 | 551 | |
4e85aff4 | 552 | .init = swap_init, |
07b0b134 | 553 | .load = swap_load, |
6e620bec | 554 | .done = swap_done, |
07b0b134 ML |
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 | ||
6e620bec LP |
573 | .enumerate = swap_enumerate, |
574 | .shutdown = swap_shutdown | |
07b0b134 | 575 | }; |