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