]>
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" | |
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 | ||
6e620bec LP |
43 | static void swap_done(Unit *u) { |
44 | Swap *s = SWAP(u); | |
45 | ||
46 | assert(s); | |
47 | ||
48 | free(s->what); | |
49 | } | |
50 | ||
07b0b134 ML |
51 | static int swap_verify(Swap *s) { |
52 | bool b; | |
53 | char *e; | |
54 | ||
55 | if (UNIT(s)->meta.load_state != UNIT_LOADED) | |
56 | return 0; | |
57 | ||
58 | if (!(e = unit_name_from_path(s->what, ".swap"))) | |
59 | return -ENOMEM; | |
60 | ||
61 | b = unit_has_name(UNIT(s), e); | |
62 | free(e); | |
63 | ||
64 | if (!b) { | |
65 | log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", UNIT(s)->meta.id); | |
66 | return -EINVAL; | |
67 | } | |
68 | return 0; | |
69 | } | |
70 | ||
71 | static int swap_add_target_links(Swap *s) { | |
72 | Manager *m = s->meta.manager; | |
73 | Unit *tu; | |
74 | int r; | |
75 | ||
76 | r = manager_load_unit(m, SPECIAL_SWAP_TARGET, NULL, &tu); | |
77 | if (r < 0) | |
78 | return r; | |
79 | ||
80 | if (!s->no_auto && (r = unit_add_dependency(tu, UNIT_WANTS, UNIT(s), true)) < 0) | |
81 | return r; | |
82 | ||
83 | return unit_add_dependency(UNIT(s), UNIT_BEFORE, tu, true); | |
84 | } | |
85 | ||
86 | static int swap_load(Unit *u) { | |
87 | int r; | |
88 | Swap *s = SWAP(u); | |
89 | ||
90 | assert(s); | |
91 | assert(u->meta.load_state == UNIT_STUB); | |
92 | ||
93 | /* Load a .swap file */ | |
94 | if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) | |
95 | return r; | |
96 | ||
97 | if (u->meta.load_state == UNIT_LOADED) { | |
98 | if (!s->what) | |
99 | if (!(s->what = unit_name_to_path(u->meta.id))) | |
100 | return -ENOMEM; | |
101 | ||
102 | path_kill_slashes(s->what); | |
103 | ||
104 | if ((r = mount_add_node_links(u, s->what)) < 0) | |
105 | return r; | |
106 | ||
107 | if (!path_startswith(s->what, "/dev/")) | |
108 | if ((r = mount_add_path_links(u, s->what, true)) < 0) | |
109 | return r; | |
110 | ||
111 | if ((r = swap_add_target_links(s)) < 0) | |
112 | return r; | |
113 | } | |
114 | ||
115 | return swap_verify(s); | |
116 | } | |
117 | ||
118 | int swap_add_one(Manager *m, const char *what, bool no_auto, int prio, bool from_proc_swaps) { | |
119 | Unit *u; | |
120 | char *e; | |
121 | bool delete; | |
122 | int r; | |
123 | ||
124 | if (!(e = unit_name_from_path(what, ".swap"))) | |
125 | return -ENOMEM; | |
126 | ||
127 | if (!(u = manager_get_unit(m, e))) { | |
128 | delete = true; | |
129 | ||
130 | if (!(u = unit_new(m))) { | |
131 | free(e); | |
132 | return -ENOMEM; | |
133 | } | |
134 | ||
135 | r = unit_add_name(u, e); | |
136 | free(e); | |
137 | ||
138 | if (r < 0) | |
139 | goto fail; | |
140 | ||
141 | if (!(SWAP(u)->what = strdup(what))) { | |
142 | r = -ENOMEM; | |
143 | goto fail; | |
144 | } | |
145 | ||
146 | if ((r = unit_set_description(u, what)) < 0) | |
147 | goto fail; | |
148 | ||
149 | unit_add_to_load_queue(u); | |
150 | ||
151 | SWAP(u)->from_proc_swaps_only = from_proc_swaps; | |
152 | } else { | |
153 | if (SWAP(u)->from_proc_swaps_only && !from_proc_swaps) | |
154 | SWAP(u)->from_proc_swaps_only = false; | |
155 | ||
156 | delete = false; | |
157 | free(e); | |
158 | } | |
159 | ||
160 | if (!from_proc_swaps) | |
161 | SWAP(u)->no_auto = no_auto; | |
162 | else | |
163 | SWAP(u)->found_in_proc_swaps = true; | |
164 | ||
165 | SWAP(u)->priority = prio; | |
166 | ||
167 | return 0; | |
168 | ||
169 | fail: | |
170 | if (delete) | |
171 | unit_free(u); | |
172 | ||
173 | return 0; | |
174 | } | |
175 | ||
176 | static void swap_set_state(Swap *s, SwapState state) { | |
177 | SwapState old_state; | |
178 | assert(s); | |
179 | ||
180 | old_state = s->state; | |
181 | s->state = state; | |
182 | ||
183 | if (state != old_state) | |
184 | log_debug("%s changed %s -> %s", | |
185 | UNIT(s)->meta.id, | |
186 | swap_state_to_string(old_state), | |
187 | swap_state_to_string(state)); | |
188 | ||
189 | unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); | |
190 | } | |
191 | ||
192 | static int swap_coldplug(Unit *u) { | |
193 | Swap *s = SWAP(u); | |
194 | SwapState new_state = SWAP_DEAD; | |
195 | ||
196 | assert(s); | |
197 | assert(s->state == SWAP_DEAD); | |
198 | ||
199 | if (s->deserialized_state != s->state) | |
200 | new_state = s->deserialized_state; | |
201 | else if (s->found_in_proc_swaps) | |
202 | new_state = SWAP_ACTIVE; | |
203 | ||
204 | if (new_state != s->state) | |
205 | swap_set_state(s, s->deserialized_state); | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static void swap_dump(Unit *u, FILE *f, const char *prefix) { | |
211 | Swap *s = SWAP(u); | |
212 | ||
213 | assert(s); | |
214 | ||
215 | fprintf(f, | |
216 | "%sAutomount State: %s\n" | |
217 | "%sWhat: %s\n" | |
218 | "%sPriority: %i\n" | |
219 | "%sNoAuto: %s\n", | |
220 | prefix, swap_state_to_string(s->state), | |
221 | prefix, s->what, | |
222 | prefix, s->priority, | |
223 | prefix, yes_no(s->no_auto)); | |
224 | } | |
225 | ||
226 | static void swap_enter_dead(Swap *s, bool success) { | |
227 | assert(s); | |
228 | ||
229 | swap_set_state(s, success ? SWAP_MAINTAINANCE : SWAP_DEAD); | |
230 | } | |
231 | ||
232 | static int swap_start(Unit *u) { | |
233 | Swap *s = SWAP(u); | |
234 | int r; | |
235 | ||
236 | assert(s); | |
237 | ||
238 | assert(s->state == SWAP_DEAD || s->state == SWAP_MAINTAINANCE); | |
239 | ||
240 | r = swapon(s->what, (s->priority << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK); | |
241 | ||
242 | if (r < 0 && errno != EBUSY) { | |
243 | r = -errno; | |
244 | swap_enter_dead(s, false); | |
245 | return r; | |
246 | } | |
247 | ||
248 | swap_set_state(s, SWAP_ACTIVE); | |
249 | return 0; | |
250 | } | |
251 | ||
252 | static int swap_stop(Unit *u) { | |
253 | Swap *s = SWAP(u); | |
254 | int r; | |
255 | ||
256 | assert(s); | |
257 | ||
258 | assert(s->state == SWAP_ACTIVE); | |
259 | ||
260 | r = swapoff(s->what); | |
261 | swap_enter_dead(s, r >= 0 || errno == EINVAL); | |
262 | ||
263 | return 0; | |
264 | } | |
265 | ||
266 | static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { | |
267 | Swap *s = SWAP(u); | |
268 | ||
269 | assert(s); | |
270 | assert(f); | |
271 | assert(fds); | |
272 | ||
273 | unit_serialize_item(u, f, "state", swap_state_to_string(s->state)); | |
274 | ||
275 | return 0; | |
276 | } | |
277 | ||
278 | static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { | |
279 | Swap *s = SWAP(u); | |
280 | ||
281 | assert(s); | |
282 | assert(fds); | |
283 | ||
284 | if (streq(key, "state")) { | |
285 | SwapState state; | |
286 | ||
287 | if ((state = swap_state_from_string(value)) < 0) | |
288 | log_debug("Failed to parse state value %s", value); | |
289 | else | |
290 | s->deserialized_state = state; | |
291 | } else | |
292 | log_debug("Unknown serialization key '%s'", key); | |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | static UnitActiveState swap_active_state(Unit *u) { | |
298 | assert(u); | |
299 | ||
300 | return state_translation_table[SWAP(u)->state]; | |
301 | } | |
302 | ||
303 | static const char *swap_sub_state_to_string(Unit *u) { | |
304 | assert(u); | |
305 | ||
306 | return swap_state_to_string(SWAP(u)->state); | |
307 | } | |
308 | ||
309 | static bool swap_check_gc(Unit *u) { | |
310 | Swap *s = SWAP(u); | |
311 | ||
312 | assert(s); | |
313 | ||
314 | return !s->from_proc_swaps_only || s->found_in_proc_swaps; | |
315 | } | |
316 | ||
317 | static int swap_load_proc_swaps(Manager *m) { | |
318 | Meta *meta; | |
319 | ||
320 | rewind(m->proc_swaps); | |
bab45044 LP |
321 | |
322 | (void) fscanf(m->proc_self_mountinfo, "%*s %*s %*s %*s %*s\n"); | |
07b0b134 ML |
323 | |
324 | for (;;) { | |
325 | char *dev = NULL, *d; | |
326 | int prio = 0, k; | |
327 | ||
328 | k = fscanf(m->proc_self_mountinfo, | |
329 | "%ms " /* device/file */ | |
330 | "%*s " /* type of swap */ | |
331 | "%*s " /* swap size */ | |
332 | "%*s " /* used */ | |
333 | "%d\n", /* priority */ | |
334 | &dev, &prio); | |
335 | ||
336 | if (k != 2) { | |
337 | if (k == EOF) | |
338 | k = 0; | |
339 | ||
340 | free(dev); | |
341 | return -EBADMSG; | |
342 | } | |
343 | if (!(d = cunescape(dev))) { | |
344 | free(dev); | |
345 | k = -ENOMEM; | |
346 | return k; | |
347 | } | |
348 | ||
349 | k = swap_add_one(m, d, false, prio, true); | |
350 | free(dev); | |
351 | free(d); | |
352 | ||
353 | if (k < 0) | |
354 | return k; | |
355 | } | |
356 | ||
357 | LIST_FOREACH(units_per_type, meta, m->units_per_type[UNIT_SWAP]) { | |
358 | Swap *s = (Swap*) meta; | |
359 | ||
360 | if (s->state != SWAP_DEAD && s->state != SWAP_ACTIVE) | |
361 | continue; | |
362 | ||
363 | if ((s->state == SWAP_DEAD && !s->found_in_proc_swaps) || | |
364 | (s->state == SWAP_ACTIVE && s->found_in_proc_swaps)) | |
365 | continue; | |
366 | ||
367 | swap_set_state(s, s->found_in_proc_swaps ? SWAP_ACTIVE : SWAP_DEAD); | |
368 | ||
369 | /* Reset the flags for later calls */ | |
370 | s->found_in_proc_swaps = false; | |
371 | } | |
372 | } | |
373 | ||
374 | static void swap_shutdown(Manager *m) { | |
375 | assert(m); | |
376 | ||
377 | if (m->proc_swaps) { | |
378 | fclose(m->proc_swaps); | |
379 | m->proc_swaps = NULL; | |
380 | } | |
381 | } | |
382 | ||
383 | static const char* const swap_state_table[_SWAP_STATE_MAX] = { | |
384 | [SWAP_DEAD] = "dead", | |
385 | [SWAP_ACTIVE] = "active", | |
386 | [SWAP_MAINTAINANCE] = "maintainance" | |
387 | }; | |
388 | ||
389 | DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState); | |
390 | ||
391 | static int swap_enumerate(Manager *m) { | |
392 | int r; | |
393 | assert(m); | |
394 | ||
395 | if (!m->proc_swaps && | |
396 | !(m->proc_swaps = fopen("/proc/swaps", "er"))) | |
397 | return -errno; | |
398 | ||
399 | if ((r = swap_load_proc_swaps(m)) < 0) | |
400 | swap_shutdown(m); | |
401 | ||
402 | return r; | |
403 | } | |
404 | ||
405 | const UnitVTable swap_vtable = { | |
406 | .suffix = ".swap", | |
407 | ||
408 | .no_alias = true, | |
409 | .no_instances = true, | |
6e620bec | 410 | .no_isolate = true, |
07b0b134 ML |
411 | |
412 | .load = swap_load, | |
6e620bec | 413 | .done = swap_done, |
07b0b134 ML |
414 | |
415 | .coldplug = swap_coldplug, | |
416 | ||
417 | .dump = swap_dump, | |
418 | ||
419 | .start = swap_start, | |
420 | .stop = swap_stop, | |
421 | ||
422 | .serialize = swap_serialize, | |
423 | .deserialize_item = swap_deserialize_item, | |
424 | ||
425 | .active_state = swap_active_state, | |
426 | .sub_state_to_string = swap_sub_state_to_string, | |
427 | ||
428 | .check_gc = swap_check_gc, | |
429 | ||
430 | .bus_message_handler = bus_swap_message_handler, | |
431 | ||
6e620bec LP |
432 | .enumerate = swap_enumerate, |
433 | .shutdown = swap_shutdown | |
07b0b134 | 434 | }; |