]>
Commit | Line | Data |
---|---|---|
9abd5e4b KZ |
1 | /* |
2 | * SPDX-License-Identifier: GPL-2.0-or-later | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * Copyright (C) 2012-2023 Karel Zak <kzak@redhat.com> | |
10 | * | |
11 | * Original implementation from Linux 0.99, without License and copyright in | |
12 | * the code. Karel Zak rewrote the code under GPL-2.0-or-later. | |
13 | */ | |
6cf8d46c | 14 | #include <stdio.h> |
6cf8d46c | 15 | #include <errno.h> |
7cf6a654 | 16 | #include <getopt.h> |
6cf8d46c KZ |
17 | |
18 | #ifdef HAVE_SYS_SWAP_H | |
19 | # include <sys/swap.h> | |
20 | #endif | |
21 | ||
22 | #include "nls.h" | |
23 | #include "c.h" | |
52f2fd9b | 24 | #include "xalloc.h" |
6cf8d46c KZ |
25 | #include "closestream.h" |
26 | ||
52f2fd9b | 27 | #include "swapprober.h" |
6cf8d46c KZ |
28 | #include "swapon-common.h" |
29 | ||
cf9b16f1 | 30 | #if !defined(HAVE_SWAPOFF) && defined(SYS_swapoff) |
6cf8d46c KZ |
31 | # include <sys/syscall.h> |
32 | # define swapoff(path) syscall(SYS_swapoff, path) | |
33 | #endif | |
34 | ||
35 | static int verbose; | |
36 | static int all; | |
37 | ||
38 | #define QUIET 1 | |
39 | #define CANONIC 1 | |
40 | ||
ecfa4dad | 41 | #define SWAPOFF_EX_OK 0 /* no errors */ |
e4253d3c KZ |
42 | #define SWAPOFF_EX_ENOMEM 2 /* swapoff(2) failed due to OOM */ |
43 | #define SWAPOFF_EX_FAILURE 4 /* swapoff(2) failed due to another reason */ | |
44 | #define SWAPOFF_EX_SYSERR 8 /* non-swaoff() errors */ | |
ecfa4dad KZ |
45 | #define SWAPOFF_EX_USAGE 16 /* usage, permissions or syntax error */ |
46 | #define SWAPOFF_EX_ALLERR 32 /* --all all failed */ | |
47 | #define SWAPOFF_EX_SOMEOK 64 /* --all some failed some OK */ | |
48 | ||
52f2fd9b | 49 | /* |
9e930041 | 50 | * This function works like mnt_resolve_tag(), but it's able to read UUID/LABEL |
52f2fd9b KZ |
51 | * from regular swap files too (according to entries in /proc/swaps). Note that |
52 | * mnt_resolve_tag() and mnt_resolve_spec() works with system visible block | |
53 | * devices only. | |
54 | */ | |
55 | static char *swapoff_resolve_tag(const char *name, const char *value, | |
56 | struct libmnt_cache *cache) | |
57 | { | |
58 | char *path; | |
59 | struct libmnt_table *tb; | |
60 | struct libmnt_iter *itr; | |
61 | struct libmnt_fs *fs; | |
62 | ||
63 | /* this is usual case for block devices (and it's really fast as it uses | |
64 | * udev /dev/disk/by-* symlinks by default */ | |
65 | path = mnt_resolve_tag(name, value, cache); | |
66 | if (path) | |
67 | return path; | |
68 | ||
69 | /* try regular files from /proc/swaps */ | |
70 | tb = get_swaps(); | |
71 | if (!tb) | |
72 | return NULL; | |
73 | ||
74 | itr = mnt_new_iter(MNT_ITER_BACKWARD); | |
75 | if (!itr) | |
ecfa4dad | 76 | err(SWAPOFF_EX_SYSERR, _("failed to initialize libmount iterator")); |
52f2fd9b KZ |
77 | |
78 | while (tb && mnt_table_next_fs(tb, itr, &fs) == 0) { | |
79 | blkid_probe pr = NULL; | |
80 | const char *src = mnt_fs_get_source(fs); | |
81 | const char *type = mnt_fs_get_swaptype(fs); | |
82 | const char *data = NULL; | |
83 | ||
84 | if (!src || !type || strcmp(type, "file") != 0) | |
85 | continue; | |
86 | pr = get_swap_prober(src); | |
87 | if (!pr) | |
88 | continue; | |
89 | blkid_probe_lookup_value(pr, name, &data, NULL); | |
90 | if (data && strcmp(data, value) == 0) | |
91 | path = xstrdup(src); | |
92 | blkid_free_probe(pr); | |
93 | if (path) | |
94 | break; | |
95 | } | |
96 | ||
97 | mnt_free_iter(itr); | |
98 | return path; | |
99 | } | |
100 | ||
6cf8d46c KZ |
101 | static int do_swapoff(const char *orig_special, int quiet, int canonic) |
102 | { | |
103 | const char *special = orig_special; | |
ecfa4dad | 104 | int rc = SWAPOFF_EX_OK; |
6cf8d46c KZ |
105 | |
106 | if (verbose) | |
e7b63bea | 107 | printf(_("swapoff %s\n"), orig_special); |
6cf8d46c KZ |
108 | |
109 | if (!canonic) { | |
52f2fd9b KZ |
110 | char *n, *v; |
111 | ||
6cf8d46c | 112 | special = mnt_resolve_spec(orig_special, mntcache); |
eeea7ef5 | 113 | if (!special && blkid_parse_tag_string(orig_special, &n, &v) == 0) { |
52f2fd9b | 114 | special = swapoff_resolve_tag(n, v, mntcache); |
eeea7ef5 KZ |
115 | free(n); |
116 | free(v); | |
117 | } | |
6cf8d46c KZ |
118 | if (!special) |
119 | return cannot_find(orig_special); | |
120 | } | |
121 | ||
122 | if (swapoff(special) == 0) | |
ecfa4dad KZ |
123 | rc = SWAPOFF_EX_OK; /* success */ |
124 | else { | |
125 | switch (errno) { | |
126 | case EPERM: | |
127 | errx(SWAPOFF_EX_USAGE, _("Not superuser.")); | |
128 | break; | |
129 | case ENOMEM: | |
130 | warn(_("%s: swapoff failed"), orig_special); | |
131 | rc = SWAPOFF_EX_ENOMEM; | |
132 | break; | |
133 | default: | |
134 | if (!quiet) | |
135 | warn(_("%s: swapoff failed"), orig_special); | |
136 | rc = SWAPOFF_EX_FAILURE; | |
137 | break; | |
138 | } | |
139 | } | |
6cf8d46c | 140 | |
ecfa4dad | 141 | return rc; |
6cf8d46c KZ |
142 | } |
143 | ||
52f2fd9b | 144 | static int swapoff_by(const char *name, const char *value, int quiet) |
6cf8d46c | 145 | { |
52f2fd9b KZ |
146 | const char *special = swapoff_resolve_tag(name, value, mntcache); |
147 | return special ? do_swapoff(special, quiet, CANONIC) : cannot_find(value); | |
6cf8d46c KZ |
148 | } |
149 | ||
6e1eda6f | 150 | static void __attribute__((__noreturn__)) usage(void) |
6cf8d46c | 151 | { |
6e1eda6f | 152 | FILE *out = stdout; |
7cf6a654 | 153 | fputs(USAGE_HEADER, out); |
6cf8d46c KZ |
154 | fprintf(out, _(" %s [options] [<spec>]\n"), program_invocation_short_name); |
155 | ||
451dbcfa BS |
156 | fputs(USAGE_SEPARATOR, out); |
157 | fputs(_("Disable devices and files for paging and swapping.\n"), out); | |
158 | ||
7cf6a654 | 159 | fputs(USAGE_OPTIONS, out); |
6cf8d46c | 160 | fputs(_(" -a, --all disable all swaps from /proc/swaps\n" |
7cf6a654 KZ |
161 | " -v, --verbose verbose mode\n"), out); |
162 | ||
163 | fputs(USAGE_SEPARATOR, out); | |
bad4c729 | 164 | fprintf(out, USAGE_HELP_OPTIONS(24)); |
6cf8d46c KZ |
165 | |
166 | fputs(_("\nThe <spec> parameter:\n" \ | |
167 | " -L <label> LABEL of device to be used\n" \ | |
168 | " -U <uuid> UUID of device to be used\n" \ | |
169 | " LABEL=<label> LABEL of device to be used\n" \ | |
170 | " UUID=<uuid> UUID of device to be used\n" \ | |
171 | " <device> name of device to be used\n" \ | |
7cf6a654 KZ |
172 | " <file> name of file to be used\n"), out); |
173 | ||
bad4c729 | 174 | fprintf(out, USAGE_MAN_TAIL("swapoff(8)")); |
ecfa4dad | 175 | exit(SWAPOFF_EX_OK); |
6cf8d46c KZ |
176 | } |
177 | ||
e7b63bea KZ |
178 | static int swapoff_all(void) |
179 | { | |
ecfa4dad | 180 | int nerrs = 0, nsucc = 0; |
e7b63bea KZ |
181 | struct libmnt_table *tb; |
182 | struct libmnt_fs *fs; | |
183 | struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD); | |
184 | ||
185 | if (!itr) | |
ecfa4dad | 186 | err(SWAPOFF_EX_SYSERR, _("failed to initialize libmount iterator")); |
e7b63bea KZ |
187 | |
188 | /* | |
189 | * In case /proc/swaps exists, unswap stuff listed there. We are quiet | |
190 | * but report errors in status. Errors might mean that /proc/swaps | |
191 | * exists as ordinary file, not in procfs. do_swapoff() exits | |
192 | * immediately on EPERM. | |
193 | */ | |
194 | tb = get_swaps(); | |
195 | ||
ecfa4dad KZ |
196 | while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) { |
197 | if (do_swapoff(mnt_fs_get_source(fs), QUIET, CANONIC) == SWAPOFF_EX_OK) | |
198 | nsucc++; | |
199 | else | |
200 | nerrs++; | |
201 | } | |
e7b63bea KZ |
202 | |
203 | /* | |
204 | * Unswap stuff mentioned in /etc/fstab. Probably it was unmounted | |
205 | * already, so errors are not bad. Doing swapoff -a twice should not | |
206 | * give error messages. | |
207 | */ | |
a5a9b544 | 208 | tb = get_fstab(NULL); |
e7b63bea KZ |
209 | mnt_reset_iter(itr, MNT_ITER_FORWARD); |
210 | ||
211 | while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) { | |
212 | if (!is_active_swap(mnt_fs_get_source(fs))) | |
213 | do_swapoff(mnt_fs_get_source(fs), QUIET, !CANONIC); | |
214 | } | |
215 | ||
216 | mnt_free_iter(itr); | |
ecfa4dad KZ |
217 | |
218 | if (nerrs == 0) | |
219 | return SWAPOFF_EX_OK; /* all success */ | |
220 | else if (nsucc == 0) | |
221 | return SWAPOFF_EX_ALLERR; /* all failed */ | |
222 | ||
223 | return SWAPOFF_EX_SOMEOK; /* some success, some failed */ | |
e7b63bea | 224 | } |
6cf8d46c KZ |
225 | |
226 | int main(int argc, char *argv[]) | |
227 | { | |
6cf8d46c KZ |
228 | int status = 0, c; |
229 | size_t i; | |
230 | ||
231 | static const struct option long_opts[] = { | |
87918040 SK |
232 | { "all", no_argument, NULL, 'a' }, |
233 | { "help", no_argument, NULL, 'h' }, | |
234 | { "verbose", no_argument, NULL, 'v' }, | |
235 | { "version", no_argument, NULL, 'V' }, | |
236 | { NULL, 0, NULL, 0 } | |
6cf8d46c KZ |
237 | }; |
238 | ||
239 | setlocale(LC_ALL, ""); | |
240 | bindtextdomain(PACKAGE, LOCALEDIR); | |
241 | textdomain(PACKAGE); | |
2c308875 | 242 | close_stdout_atexit(); |
6cf8d46c KZ |
243 | |
244 | while ((c = getopt_long(argc, argv, "ahvVL:U:", | |
245 | long_opts, NULL)) != -1) { | |
246 | switch (c) { | |
247 | case 'a': /* all */ | |
248 | ++all; | |
249 | break; | |
6cf8d46c KZ |
250 | case 'v': /* be chatty */ |
251 | ++verbose; | |
252 | break; | |
6cf8d46c KZ |
253 | case 'L': |
254 | add_label(optarg); | |
255 | break; | |
256 | case 'U': | |
257 | add_uuid(optarg); | |
258 | break; | |
2c308875 KZ |
259 | |
260 | case 'h': /* help */ | |
261 | usage(); | |
262 | case 'V': /* version */ | |
ecfa4dad | 263 | print_version(SWAPOFF_EX_OK); |
6cf8d46c | 264 | default: |
ecfa4dad | 265 | errtryhelp(SWAPOFF_EX_USAGE); |
6cf8d46c KZ |
266 | } |
267 | } | |
268 | argv += optind; | |
269 | ||
6e1eda6f RM |
270 | if (!all && !numof_labels() && !numof_uuids() && *argv == NULL) { |
271 | warnx(_("bad usage")); | |
ecfa4dad | 272 | errtryhelp(SWAPOFF_EX_USAGE); |
6e1eda6f | 273 | } |
6cf8d46c KZ |
274 | |
275 | mnt_init_debug(0); | |
276 | mntcache = mnt_new_cache(); | |
277 | ||
6cf8d46c | 278 | for (i = 0; i < numof_labels(); i++) |
52f2fd9b | 279 | status |= swapoff_by("LABEL", get_label(i), !QUIET); |
6cf8d46c KZ |
280 | |
281 | for (i = 0; i < numof_uuids(); i++) | |
52f2fd9b | 282 | status |= swapoff_by("UUID", get_uuid(i), !QUIET); |
6cf8d46c KZ |
283 | |
284 | while (*argv != NULL) | |
285 | status |= do_swapoff(*argv++, !QUIET, !CANONIC); | |
286 | ||
e7b63bea KZ |
287 | if (all) |
288 | status |= swapoff_all(); | |
6cf8d46c KZ |
289 | |
290 | free_tables(); | |
6195f9e6 | 291 | mnt_unref_cache(mntcache); |
6cf8d46c KZ |
292 | |
293 | return status; | |
294 | } |