]> git.ipfire.org Git - thirdparty/util-linux.git/blob - sys-utils/swapoff.c
sys-utils: cleanup license lines, add SPDX
[thirdparty/util-linux.git] / sys-utils / swapoff.c
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 */
14 #include <stdio.h>
15 #include <errno.h>
16 #include <getopt.h>
17
18 #ifdef HAVE_SYS_SWAP_H
19 # include <sys/swap.h>
20 #endif
21
22 #include "nls.h"
23 #include "c.h"
24 #include "xalloc.h"
25 #include "closestream.h"
26
27 #include "swapprober.h"
28 #include "swapon-common.h"
29
30 #if !defined(HAVE_SWAPOFF) && defined(SYS_swapoff)
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
41 #define SWAPOFF_EX_OK 0 /* no errors */
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 */
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
49 /*
50 * This function works like mnt_resolve_tag(), but it's able to read UUID/LABEL
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)
76 err(SWAPOFF_EX_SYSERR, _("failed to initialize libmount iterator"));
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
101 static int do_swapoff(const char *orig_special, int quiet, int canonic)
102 {
103 const char *special = orig_special;
104 int rc = SWAPOFF_EX_OK;
105
106 if (verbose)
107 printf(_("swapoff %s\n"), orig_special);
108
109 if (!canonic) {
110 char *n, *v;
111
112 special = mnt_resolve_spec(orig_special, mntcache);
113 if (!special && blkid_parse_tag_string(orig_special, &n, &v) == 0) {
114 special = swapoff_resolve_tag(n, v, mntcache);
115 free(n);
116 free(v);
117 }
118 if (!special)
119 return cannot_find(orig_special);
120 }
121
122 if (swapoff(special) == 0)
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 }
140
141 return rc;
142 }
143
144 static int swapoff_by(const char *name, const char *value, int quiet)
145 {
146 const char *special = swapoff_resolve_tag(name, value, mntcache);
147 return special ? do_swapoff(special, quiet, CANONIC) : cannot_find(value);
148 }
149
150 static void __attribute__((__noreturn__)) usage(void)
151 {
152 FILE *out = stdout;
153 fputs(USAGE_HEADER, out);
154 fprintf(out, _(" %s [options] [<spec>]\n"), program_invocation_short_name);
155
156 fputs(USAGE_SEPARATOR, out);
157 fputs(_("Disable devices and files for paging and swapping.\n"), out);
158
159 fputs(USAGE_OPTIONS, out);
160 fputs(_(" -a, --all disable all swaps from /proc/swaps\n"
161 " -v, --verbose verbose mode\n"), out);
162
163 fputs(USAGE_SEPARATOR, out);
164 fprintf(out, USAGE_HELP_OPTIONS(24));
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" \
172 " <file> name of file to be used\n"), out);
173
174 fprintf(out, USAGE_MAN_TAIL("swapoff(8)"));
175 exit(SWAPOFF_EX_OK);
176 }
177
178 static int swapoff_all(void)
179 {
180 int nerrs = 0, nsucc = 0;
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)
186 err(SWAPOFF_EX_SYSERR, _("failed to initialize libmount iterator"));
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
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 }
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 */
208 tb = get_fstab(NULL);
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);
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 */
224 }
225
226 int main(int argc, char *argv[])
227 {
228 int status = 0, c;
229 size_t i;
230
231 static const struct option long_opts[] = {
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 }
237 };
238
239 setlocale(LC_ALL, "");
240 bindtextdomain(PACKAGE, LOCALEDIR);
241 textdomain(PACKAGE);
242 close_stdout_atexit();
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;
250 case 'v': /* be chatty */
251 ++verbose;
252 break;
253 case 'L':
254 add_label(optarg);
255 break;
256 case 'U':
257 add_uuid(optarg);
258 break;
259
260 case 'h': /* help */
261 usage();
262 case 'V': /* version */
263 print_version(SWAPOFF_EX_OK);
264 default:
265 errtryhelp(SWAPOFF_EX_USAGE);
266 }
267 }
268 argv += optind;
269
270 if (!all && !numof_labels() && !numof_uuids() && *argv == NULL) {
271 warnx(_("bad usage"));
272 errtryhelp(SWAPOFF_EX_USAGE);
273 }
274
275 mnt_init_debug(0);
276 mntcache = mnt_new_cache();
277
278 for (i = 0; i < numof_labels(); i++)
279 status |= swapoff_by("LABEL", get_label(i), !QUIET);
280
281 for (i = 0; i < numof_uuids(); i++)
282 status |= swapoff_by("UUID", get_uuid(i), !QUIET);
283
284 while (*argv != NULL)
285 status |= do_swapoff(*argv++, !QUIET, !CANONIC);
286
287 if (all)
288 status |= swapoff_all();
289
290 free_tables();
291 mnt_unref_cache(mntcache);
292
293 return status;
294 }