]>
Commit | Line | Data |
---|---|---|
d6c9574f | 1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
07b0b134 ML |
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 | ||
2edd4434 LP |
123 | static int swap_add_default_dependencies(Swap *s) { |
124 | int r; | |
125 | ||
126 | assert(s); | |
127 | ||
128 | if (s->meta.manager->running_as == MANAGER_SYSTEM) { | |
129 | ||
130 | if ((r = unit_add_dependency_by_name(UNIT(s), UNIT_AFTER, SPECIAL_SYSINIT_TARGET, NULL, true)) < 0) | |
131 | return r; | |
132 | ||
133 | /* Note that by default we don't disable swap devices | |
134 | * on shutdown. i.e. there is no umount.target | |
135 | * conflicts here. */ | |
136 | } | |
137 | ||
138 | return 0; | |
139 | } | |
140 | ||
4e85aff4 LP |
141 | static int swap_verify(Swap *s) { |
142 | bool b; | |
143 | char *e; | |
144 | ||
4cd1fbcc | 145 | if (s->meta.load_state != UNIT_LOADED) |
4e85aff4 LP |
146 | return 0; |
147 | ||
148 | if (!(e = unit_name_from_path(s->what, ".swap"))) | |
149 | return -ENOMEM; | |
150 | ||
151 | b = unit_has_name(UNIT(s), e); | |
152 | free(e); | |
153 | ||
154 | if (!b) { | |
4cd1fbcc | 155 | log_error("%s: Value of \"What\" and unit name do not match, not loading.\n", s->meta.id); |
4e85aff4 LP |
156 | return -EINVAL; |
157 | } | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
07b0b134 ML |
162 | static int swap_load(Unit *u) { |
163 | int r; | |
164 | Swap *s = SWAP(u); | |
165 | ||
166 | assert(s); | |
167 | assert(u->meta.load_state == UNIT_STUB); | |
168 | ||
169 | /* Load a .swap file */ | |
170 | if ((r = unit_load_fragment_and_dropin_optional(u)) < 0) | |
171 | return r; | |
172 | ||
173 | if (u->meta.load_state == UNIT_LOADED) { | |
4e85aff4 LP |
174 | |
175 | if (s->meta.fragment_path) | |
176 | s->from_fragment = true; | |
177 | ||
178 | if (!s->what) { | |
179 | if (s->parameters_fragment.what) | |
180 | s->what = strdup(s->parameters_fragment.what); | |
181 | else if (s->parameters_etc_fstab.what) | |
182 | s->what = strdup(s->parameters_etc_fstab.what); | |
183 | else if (s->parameters_proc_swaps.what) | |
184 | s->what = strdup(s->parameters_proc_swaps.what); | |
185 | else | |
186 | s->what = unit_name_to_path(u->meta.id); | |
187 | ||
188 | if (!s->what) | |
07b0b134 | 189 | return -ENOMEM; |
4e85aff4 | 190 | } |
07b0b134 ML |
191 | |
192 | path_kill_slashes(s->what); | |
193 | ||
4e85aff4 LP |
194 | if (!s->meta.description) |
195 | if ((r = unit_set_description(u, s->what)) < 0) | |
196 | return r; | |
197 | ||
a3d4e06d | 198 | if ((r = unit_add_node_link(u, s->what, u->meta.manager->running_as == MANAGER_SYSTEM)) < 0) |
07b0b134 ML |
199 | return r; |
200 | ||
6e2ef85b LP |
201 | if ((r = swap_add_mount_links(s)) < 0) |
202 | return r; | |
07b0b134 ML |
203 | |
204 | if ((r = swap_add_target_links(s)) < 0) | |
205 | return r; | |
2edd4434 LP |
206 | |
207 | if (s->meta.default_dependencies) | |
208 | if ((r = swap_add_default_dependencies(s)) < 0) | |
209 | return r; | |
07b0b134 ML |
210 | } |
211 | ||
212 | return swap_verify(s); | |
213 | } | |
214 | ||
4e85aff4 | 215 | static int swap_find(Manager *m, const char *what, Unit **_u) { |
07b0b134 ML |
216 | Unit *u; |
217 | char *e; | |
4e85aff4 LP |
218 | |
219 | assert(m); | |
220 | assert(what); | |
221 | assert(_u); | |
222 | ||
223 | /* /proc/swaps and /etc/fstab might refer to this device by | |
224 | * different names (e.g. one by uuid, the other by the kernel | |
225 | * name), we hence need to look for all aliases we are aware | |
226 | * of for this device */ | |
227 | ||
228 | if (!(e = unit_name_from_path(what, ".device"))) | |
229 | return -ENOMEM; | |
230 | ||
231 | u = manager_get_unit(m, e); | |
232 | free(e); | |
233 | ||
234 | if (u) { | |
235 | Iterator i; | |
236 | const char *d; | |
237 | ||
238 | SET_FOREACH(d, u->meta.names, i) { | |
239 | Unit *k; | |
240 | ||
241 | if (!(e = unit_name_change_suffix(d, ".swap"))) | |
242 | return -ENOMEM; | |
243 | ||
244 | k = manager_get_unit(m, e); | |
245 | free(e); | |
246 | ||
247 | if (k) { | |
248 | *_u = k; | |
249 | return 0; | |
250 | } | |
251 | } | |
252 | } | |
253 | ||
254 | *_u = NULL; | |
255 | return 0; | |
256 | } | |
257 | ||
258 | int swap_add_one( | |
259 | Manager *m, | |
260 | const char *what, | |
261 | int priority, | |
262 | bool noauto, | |
263 | bool handle, | |
264 | bool from_proc_swaps) { | |
265 | Unit *u = NULL; | |
266 | char *e = NULL, *w = NULL; | |
2c7c6144 | 267 | bool delete = false; |
07b0b134 | 268 | int r; |
4e85aff4 LP |
269 | SwapParameters *p; |
270 | ||
271 | assert(m); | |
272 | assert(what); | |
07b0b134 ML |
273 | |
274 | if (!(e = unit_name_from_path(what, ".swap"))) | |
275 | return -ENOMEM; | |
276 | ||
4e85aff4 LP |
277 | if (!(u = manager_get_unit(m, e))) |
278 | if ((r = swap_find(m, what, &u)) < 0) | |
279 | goto fail; | |
280 | ||
281 | if (!u) { | |
07b0b134 ML |
282 | delete = true; |
283 | ||
284 | if (!(u = unit_new(m))) { | |
285 | free(e); | |
286 | return -ENOMEM; | |
287 | } | |
4e85aff4 LP |
288 | } else |
289 | delete = false; | |
07b0b134 | 290 | |
4e85aff4 LP |
291 | if ((r = unit_add_name(u, e)) < 0) |
292 | goto fail; | |
07b0b134 | 293 | |
4e85aff4 LP |
294 | if (!(w = strdup(what))) { |
295 | r = -ENOMEM; | |
296 | goto fail; | |
297 | } | |
07b0b134 | 298 | |
4e85aff4 LP |
299 | if (from_proc_swaps) { |
300 | p = &SWAP(u)->parameters_proc_swaps; | |
301 | SWAP(u)->from_proc_swaps = true; | |
302 | } else { | |
303 | p = &SWAP(u)->parameters_etc_fstab; | |
304 | SWAP(u)->from_etc_fstab = true; | |
305 | } | |
07b0b134 | 306 | |
4e85aff4 LP |
307 | free(p->what); |
308 | p->what = w; | |
07b0b134 | 309 | |
4e85aff4 LP |
310 | p->priority = priority; |
311 | p->noauto = noauto; | |
312 | p->handle = handle; | |
07b0b134 | 313 | |
4e85aff4 LP |
314 | if (delete) |
315 | unit_add_to_load_queue(u); | |
07b0b134 | 316 | |
4e85aff4 | 317 | unit_add_to_dbus_queue(u); |
07b0b134 | 318 | |
4e85aff4 | 319 | free(e); |
07b0b134 ML |
320 | |
321 | return 0; | |
322 | ||
323 | fail: | |
4e85aff4 LP |
324 | free(w); |
325 | free(e); | |
326 | ||
327 | if (delete && u) | |
07b0b134 ML |
328 | unit_free(u); |
329 | ||
4e85aff4 | 330 | return r; |
07b0b134 ML |
331 | } |
332 | ||
333 | static void swap_set_state(Swap *s, SwapState state) { | |
334 | SwapState old_state; | |
335 | assert(s); | |
336 | ||
337 | old_state = s->state; | |
338 | s->state = state; | |
339 | ||
340 | if (state != old_state) | |
341 | log_debug("%s changed %s -> %s", | |
4cd1fbcc | 342 | s->meta.id, |
07b0b134 ML |
343 | swap_state_to_string(old_state), |
344 | swap_state_to_string(state)); | |
345 | ||
346 | unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state]); | |
347 | } | |
348 | ||
349 | static int swap_coldplug(Unit *u) { | |
350 | Swap *s = SWAP(u); | |
351 | SwapState new_state = SWAP_DEAD; | |
352 | ||
353 | assert(s); | |
354 | assert(s->state == SWAP_DEAD); | |
355 | ||
356 | if (s->deserialized_state != s->state) | |
357 | new_state = s->deserialized_state; | |
4e85aff4 | 358 | else if (s->from_proc_swaps) |
07b0b134 ML |
359 | new_state = SWAP_ACTIVE; |
360 | ||
361 | if (new_state != s->state) | |
4e85aff4 | 362 | swap_set_state(s, new_state); |
07b0b134 ML |
363 | |
364 | return 0; | |
365 | } | |
366 | ||
367 | static void swap_dump(Unit *u, FILE *f, const char *prefix) { | |
368 | Swap *s = SWAP(u); | |
4e85aff4 | 369 | SwapParameters *p; |
07b0b134 ML |
370 | |
371 | assert(s); | |
4e85aff4 LP |
372 | assert(f); |
373 | ||
374 | if (s->from_proc_swaps) | |
375 | p = &s->parameters_proc_swaps; | |
376 | else if (s->from_fragment) | |
377 | p = &s->parameters_fragment; | |
378 | else | |
379 | p = &s->parameters_etc_fstab; | |
07b0b134 ML |
380 | |
381 | fprintf(f, | |
4e85aff4 | 382 | "%sSwap State: %s\n" |
07b0b134 ML |
383 | "%sWhat: %s\n" |
384 | "%sPriority: %i\n" | |
4e85aff4 LP |
385 | "%sNoAuto: %s\n" |
386 | "%sHandle: %s\n" | |
387 | "%sFrom /etc/fstab: %s\n" | |
388 | "%sFrom /proc/swaps: %s\n" | |
389 | "%sFrom fragment: %s\n", | |
07b0b134 ML |
390 | prefix, swap_state_to_string(s->state), |
391 | prefix, s->what, | |
4e85aff4 LP |
392 | prefix, p->priority, |
393 | prefix, yes_no(p->noauto), | |
394 | prefix, yes_no(p->handle), | |
395 | prefix, yes_no(s->from_etc_fstab), | |
396 | prefix, yes_no(s->from_proc_swaps), | |
397 | prefix, yes_no(s->from_fragment)); | |
07b0b134 ML |
398 | } |
399 | ||
400 | static void swap_enter_dead(Swap *s, bool success) { | |
401 | assert(s); | |
402 | ||
76143638 | 403 | swap_set_state(s, success ? SWAP_DEAD : SWAP_MAINTENANCE); |
07b0b134 ML |
404 | } |
405 | ||
406 | static int swap_start(Unit *u) { | |
407 | Swap *s = SWAP(u); | |
4e85aff4 | 408 | int priority = -1; |
07b0b134 ML |
409 | int r; |
410 | ||
411 | assert(s); | |
18c78fb1 | 412 | assert(s->state == SWAP_DEAD || s->state == SWAP_MAINTENANCE); |
07b0b134 | 413 | |
4e85aff4 LP |
414 | if (s->from_fragment) |
415 | priority = s->parameters_fragment.priority; | |
416 | else if (s->from_etc_fstab) | |
417 | priority = s->parameters_etc_fstab.priority; | |
418 | ||
419 | r = swapon(s->what, (priority << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK); | |
07b0b134 ML |
420 | |
421 | if (r < 0 && errno != EBUSY) { | |
422 | r = -errno; | |
423 | swap_enter_dead(s, false); | |
424 | return r; | |
425 | } | |
426 | ||
427 | swap_set_state(s, SWAP_ACTIVE); | |
428 | return 0; | |
429 | } | |
430 | ||
431 | static int swap_stop(Unit *u) { | |
432 | Swap *s = SWAP(u); | |
433 | int r; | |
434 | ||
435 | assert(s); | |
436 | ||
437 | assert(s->state == SWAP_ACTIVE); | |
438 | ||
439 | r = swapoff(s->what); | |
440 | swap_enter_dead(s, r >= 0 || errno == EINVAL); | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | static int swap_serialize(Unit *u, FILE *f, FDSet *fds) { | |
446 | Swap *s = SWAP(u); | |
447 | ||
448 | assert(s); | |
449 | assert(f); | |
450 | assert(fds); | |
451 | ||
452 | unit_serialize_item(u, f, "state", swap_state_to_string(s->state)); | |
453 | ||
454 | return 0; | |
455 | } | |
456 | ||
457 | static int swap_deserialize_item(Unit *u, const char *key, const char *value, FDSet *fds) { | |
458 | Swap *s = SWAP(u); | |
459 | ||
460 | assert(s); | |
461 | assert(fds); | |
462 | ||
463 | if (streq(key, "state")) { | |
464 | SwapState state; | |
465 | ||
466 | if ((state = swap_state_from_string(value)) < 0) | |
467 | log_debug("Failed to parse state value %s", value); | |
468 | else | |
469 | s->deserialized_state = state; | |
470 | } else | |
471 | log_debug("Unknown serialization key '%s'", key); | |
472 | ||
473 | return 0; | |
474 | } | |
475 | ||
476 | static UnitActiveState swap_active_state(Unit *u) { | |
477 | assert(u); | |
478 | ||
479 | return state_translation_table[SWAP(u)->state]; | |
480 | } | |
481 | ||
482 | static const char *swap_sub_state_to_string(Unit *u) { | |
483 | assert(u); | |
484 | ||
485 | return swap_state_to_string(SWAP(u)->state); | |
486 | } | |
487 | ||
488 | static bool swap_check_gc(Unit *u) { | |
489 | Swap *s = SWAP(u); | |
490 | ||
491 | assert(s); | |
492 | ||
4e85aff4 | 493 | return s->from_etc_fstab || s->from_proc_swaps; |
07b0b134 ML |
494 | } |
495 | ||
496 | static int swap_load_proc_swaps(Manager *m) { | |
07b0b134 | 497 | rewind(m->proc_swaps); |
bab45044 | 498 | |
4e85aff4 | 499 | (void) fscanf(m->proc_swaps, "%*s %*s %*s %*s %*s\n"); |
07b0b134 ML |
500 | |
501 | for (;;) { | |
502 | char *dev = NULL, *d; | |
503 | int prio = 0, k; | |
504 | ||
4e85aff4 LP |
505 | if ((k = fscanf(m->proc_swaps, |
506 | "%ms " /* device/file */ | |
507 | "%*s " /* type of swap */ | |
508 | "%*s " /* swap size */ | |
509 | "%*s " /* used */ | |
510 | "%i\n", /* priority */ | |
511 | &dev, &prio)) != 2) { | |
07b0b134 | 512 | |
07b0b134 | 513 | if (k == EOF) |
4e85aff4 | 514 | break; |
07b0b134 ML |
515 | |
516 | free(dev); | |
517 | return -EBADMSG; | |
518 | } | |
07b0b134 | 519 | |
4e85aff4 | 520 | d = cunescape(dev); |
07b0b134 | 521 | free(dev); |
4e85aff4 LP |
522 | |
523 | if (!d) | |
524 | return -ENOMEM; | |
525 | ||
526 | k = swap_add_one(m, d, prio, false, false, true); | |
07b0b134 ML |
527 | free(d); |
528 | ||
529 | if (k < 0) | |
530 | return k; | |
531 | } | |
532 | ||
4e85aff4 | 533 | return 0; |
07b0b134 ML |
534 | } |
535 | ||
536 | static void swap_shutdown(Manager *m) { | |
537 | assert(m); | |
538 | ||
539 | if (m->proc_swaps) { | |
540 | fclose(m->proc_swaps); | |
541 | m->proc_swaps = NULL; | |
542 | } | |
543 | } | |
544 | ||
07b0b134 ML |
545 | static int swap_enumerate(Manager *m) { |
546 | int r; | |
547 | assert(m); | |
548 | ||
4e85aff4 LP |
549 | if (!m->proc_swaps) |
550 | if (!(m->proc_swaps = fopen("/proc/swaps", "re"))) | |
551 | return -errno; | |
07b0b134 ML |
552 | |
553 | if ((r = swap_load_proc_swaps(m)) < 0) | |
554 | swap_shutdown(m); | |
555 | ||
556 | return r; | |
557 | } | |
558 | ||
5632e374 LP |
559 | static void swap_reset_maintenance(Unit *u) { |
560 | Swap *s = SWAP(u); | |
561 | ||
562 | assert(s); | |
563 | ||
564 | if (s->state == SWAP_MAINTENANCE) | |
565 | swap_set_state(s, SWAP_DEAD); | |
566 | } | |
567 | ||
568 | static const char* const swap_state_table[_SWAP_STATE_MAX] = { | |
569 | [SWAP_DEAD] = "dead", | |
570 | [SWAP_ACTIVE] = "active", | |
571 | [SWAP_MAINTENANCE] = "maintenance" | |
572 | }; | |
573 | ||
574 | DEFINE_STRING_TABLE_LOOKUP(swap_state, SwapState); | |
575 | ||
07b0b134 ML |
576 | const UnitVTable swap_vtable = { |
577 | .suffix = ".swap", | |
578 | ||
07b0b134 | 579 | .no_instances = true, |
6e620bec | 580 | .no_isolate = true, |
9e58ff9c | 581 | .show_status = true, |
07b0b134 | 582 | |
4e85aff4 | 583 | .init = swap_init, |
07b0b134 | 584 | .load = swap_load, |
6e620bec | 585 | .done = swap_done, |
07b0b134 ML |
586 | |
587 | .coldplug = swap_coldplug, | |
588 | ||
589 | .dump = swap_dump, | |
590 | ||
591 | .start = swap_start, | |
592 | .stop = swap_stop, | |
593 | ||
594 | .serialize = swap_serialize, | |
595 | .deserialize_item = swap_deserialize_item, | |
596 | ||
597 | .active_state = swap_active_state, | |
598 | .sub_state_to_string = swap_sub_state_to_string, | |
599 | ||
600 | .check_gc = swap_check_gc, | |
601 | ||
c4e2ceae | 602 | .bus_interface = "org.freedesktop.systemd1.Swap", |
07b0b134 | 603 | .bus_message_handler = bus_swap_message_handler, |
c4e2ceae | 604 | .bus_invalidating_properties = bus_swap_invalidating_properties, |
07b0b134 | 605 | |
5632e374 LP |
606 | .reset_maintenance = swap_reset_maintenance, |
607 | ||
6e620bec LP |
608 | .enumerate = swap_enumerate, |
609 | .shutdown = swap_shutdown | |
07b0b134 | 610 | }; |