summaryrefslogtreecommitdiffstats
path: root/sys-utils/swapoff.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys-utils/swapoff.c')
-rw-r--r--sys-utils/swapoff.c281
1 files changed, 281 insertions, 0 deletions
diff --git a/sys-utils/swapoff.c b/sys-utils/swapoff.c
new file mode 100644
index 0000000..1ddfcda
--- /dev/null
+++ b/sys-utils/swapoff.c
@@ -0,0 +1,281 @@
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+
+#ifdef HAVE_SYS_SWAP_H
+# include <sys/swap.h>
+#endif
+
+#include "nls.h"
+#include "c.h"
+#include "xalloc.h"
+#include "closestream.h"
+
+#include "swapprober.h"
+#include "swapon-common.h"
+
+#if !defined(HAVE_SWAPOFF) && defined(SYS_swapoff)
+# include <sys/syscall.h>
+# define swapoff(path) syscall(SYS_swapoff, path)
+#endif
+
+static int verbose;
+static int all;
+
+#define QUIET 1
+#define CANONIC 1
+
+#define SWAPOFF_EX_OK 0 /* no errors */
+#define SWAPOFF_EX_ENOMEM 2 /* swapoff(2) failed due to OOM */
+#define SWAPOFF_EX_FAILURE 4 /* swapoff(2) failed due to another reason */
+#define SWAPOFF_EX_SYSERR 8 /* non-swaoff() errors */
+#define SWAPOFF_EX_USAGE 16 /* usage, permissions or syntax error */
+#define SWAPOFF_EX_ALLERR 32 /* --all all failed */
+#define SWAPOFF_EX_SOMEOK 64 /* --all some failed some OK */
+
+/*
+ * This function works like mnt_resolve_tag(), but it's able to read UUID/LABEL
+ * from regular swap files too (according to entries in /proc/swaps). Note that
+ * mnt_resolve_tag() and mnt_resolve_spec() works with system visible block
+ * devices only.
+ */
+static char *swapoff_resolve_tag(const char *name, const char *value,
+ struct libmnt_cache *cache)
+{
+ char *path;
+ struct libmnt_table *tb;
+ struct libmnt_iter *itr;
+ struct libmnt_fs *fs;
+
+ /* this is usual case for block devices (and it's really fast as it uses
+ * udev /dev/disk/by-* symlinks by default */
+ path = mnt_resolve_tag(name, value, cache);
+ if (path)
+ return path;
+
+ /* try regular files from /proc/swaps */
+ tb = get_swaps();
+ if (!tb)
+ return NULL;
+
+ itr = mnt_new_iter(MNT_ITER_BACKWARD);
+ if (!itr)
+ err(SWAPOFF_EX_SYSERR, _("failed to initialize libmount iterator"));
+
+ while (tb && mnt_table_next_fs(tb, itr, &fs) == 0) {
+ blkid_probe pr = NULL;
+ const char *src = mnt_fs_get_source(fs);
+ const char *type = mnt_fs_get_swaptype(fs);
+ const char *data = NULL;
+
+ if (!src || !type || strcmp(type, "file") != 0)
+ continue;
+ pr = get_swap_prober(src);
+ if (!pr)
+ continue;
+ blkid_probe_lookup_value(pr, name, &data, NULL);
+ if (data && strcmp(data, value) == 0)
+ path = xstrdup(src);
+ blkid_free_probe(pr);
+ if (path)
+ break;
+ }
+
+ mnt_free_iter(itr);
+ return path;
+}
+
+static int do_swapoff(const char *orig_special, int quiet, int canonic)
+{
+ const char *special = orig_special;
+ int rc = SWAPOFF_EX_OK;
+
+ if (verbose)
+ printf(_("swapoff %s\n"), orig_special);
+
+ if (!canonic) {
+ char *n, *v;
+
+ special = mnt_resolve_spec(orig_special, mntcache);
+ if (!special && blkid_parse_tag_string(orig_special, &n, &v) == 0) {
+ special = swapoff_resolve_tag(n, v, mntcache);
+ free(n);
+ free(v);
+ }
+ if (!special)
+ return cannot_find(orig_special);
+ }
+
+ if (swapoff(special) == 0)
+ rc = SWAPOFF_EX_OK; /* success */
+ else {
+ switch (errno) {
+ case EPERM:
+ errx(SWAPOFF_EX_USAGE, _("Not superuser."));
+ break;
+ case ENOMEM:
+ warn(_("%s: swapoff failed"), orig_special);
+ rc = SWAPOFF_EX_ENOMEM;
+ break;
+ default:
+ if (!quiet)
+ warn(_("%s: swapoff failed"), orig_special);
+ rc = SWAPOFF_EX_FAILURE;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+static int swapoff_by(const char *name, const char *value, int quiet)
+{
+ const char *special = swapoff_resolve_tag(name, value, mntcache);
+ return special ? do_swapoff(special, quiet, CANONIC) : cannot_find(value);
+}
+
+static void __attribute__((__noreturn__)) usage(void)
+{
+ FILE *out = stdout;
+ fputs(USAGE_HEADER, out);
+ fprintf(out, _(" %s [options] [<spec>]\n"), program_invocation_short_name);
+
+ fputs(USAGE_SEPARATOR, out);
+ fputs(_("Disable devices and files for paging and swapping.\n"), out);
+
+ fputs(USAGE_OPTIONS, out);
+ fputs(_(" -a, --all disable all swaps from /proc/swaps\n"
+ " -v, --verbose verbose mode\n"), out);
+
+ fputs(USAGE_SEPARATOR, out);
+ printf(USAGE_HELP_OPTIONS(24));
+
+ fputs(_("\nThe <spec> parameter:\n" \
+ " -L <label> LABEL of device to be used\n" \
+ " -U <uuid> UUID of device to be used\n" \
+ " LABEL=<label> LABEL of device to be used\n" \
+ " UUID=<uuid> UUID of device to be used\n" \
+ " <device> name of device to be used\n" \
+ " <file> name of file to be used\n"), out);
+
+ printf(USAGE_MAN_TAIL("swapoff(8)"));
+ exit(SWAPOFF_EX_OK);
+}
+
+static int swapoff_all(void)
+{
+ int nerrs = 0, nsucc = 0;
+ struct libmnt_table *tb;
+ struct libmnt_fs *fs;
+ struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
+
+ if (!itr)
+ err(SWAPOFF_EX_SYSERR, _("failed to initialize libmount iterator"));
+
+ /*
+ * In case /proc/swaps exists, unswap stuff listed there. We are quiet
+ * but report errors in status. Errors might mean that /proc/swaps
+ * exists as ordinary file, not in procfs. do_swapoff() exits
+ * immediately on EPERM.
+ */
+ tb = get_swaps();
+
+ while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) {
+ if (do_swapoff(mnt_fs_get_source(fs), QUIET, CANONIC) == SWAPOFF_EX_OK)
+ nsucc++;
+ else
+ nerrs++;
+ }
+
+ /*
+ * Unswap stuff mentioned in /etc/fstab. Probably it was unmounted
+ * already, so errors are not bad. Doing swapoff -a twice should not
+ * give error messages.
+ */
+ tb = get_fstab(NULL);
+ mnt_reset_iter(itr, MNT_ITER_FORWARD);
+
+ while (tb && mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) {
+ if (!is_active_swap(mnt_fs_get_source(fs)))
+ do_swapoff(mnt_fs_get_source(fs), QUIET, !CANONIC);
+ }
+
+ mnt_free_iter(itr);
+
+ if (nerrs == 0)
+ return SWAPOFF_EX_OK; /* all success */
+ else if (nsucc == 0)
+ return SWAPOFF_EX_ALLERR; /* all failed */
+
+ return SWAPOFF_EX_SOMEOK; /* some success, some failed */
+}
+
+int main(int argc, char *argv[])
+{
+ int status = 0, c;
+ size_t i;
+
+ static const struct option long_opts[] = {
+ { "all", no_argument, NULL, 'a' },
+ { "help", no_argument, NULL, 'h' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ close_stdout_atexit();
+
+ while ((c = getopt_long(argc, argv, "ahvVL:U:",
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'a': /* all */
+ ++all;
+ break;
+ case 'v': /* be chatty */
+ ++verbose;
+ break;
+ case 'L':
+ add_label(optarg);
+ break;
+ case 'U':
+ add_uuid(optarg);
+ break;
+
+ case 'h': /* help */
+ usage();
+ case 'V': /* version */
+ print_version(SWAPOFF_EX_OK);
+ default:
+ errtryhelp(SWAPOFF_EX_USAGE);
+ }
+ }
+ argv += optind;
+
+ if (!all && !numof_labels() && !numof_uuids() && *argv == NULL) {
+ warnx(_("bad usage"));
+ errtryhelp(SWAPOFF_EX_USAGE);
+ }
+
+ mnt_init_debug(0);
+ mntcache = mnt_new_cache();
+
+ for (i = 0; i < numof_labels(); i++)
+ status |= swapoff_by("LABEL", get_label(i), !QUIET);
+
+ for (i = 0; i < numof_uuids(); i++)
+ status |= swapoff_by("UUID", get_uuid(i), !QUIET);
+
+ while (*argv != NULL)
+ status |= do_swapoff(*argv++, !QUIET, !CANONIC);
+
+ if (all)
+ status |= swapoff_all();
+
+ free_tables();
+ mnt_unref_cache(mntcache);
+
+ return status;
+}