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