(root)/
util-linux-2.39/
sys-utils/
swapon.c
       1  #include <assert.h>
       2  #include <stdlib.h>
       3  #include <stdio.h>
       4  #include <getopt.h>
       5  #include <string.h>
       6  #include <errno.h>
       7  #include <sys/stat.h>
       8  #include <unistd.h>
       9  #include <sys/types.h>
      10  #include <sys/wait.h>
      11  #include <fcntl.h>
      12  #include <stdint.h>
      13  #include <ctype.h>
      14  
      15  #include <libsmartcols.h>
      16  
      17  #include "c.h"
      18  #include "nls.h"
      19  #include "bitops.h"
      20  #include "blkdev.h"
      21  #include "pathnames.h"
      22  #include "xalloc.h"
      23  #include "strutils.h"
      24  #include "optutils.h"
      25  #include "closestream.h"
      26  
      27  #include "swapheader.h"
      28  #include "swapprober.h"
      29  #include "swapon-common.h"
      30  
      31  #ifdef HAVE_SYS_SWAP_H
      32  # include <sys/swap.h>
      33  #endif
      34  
      35  #ifndef SWAP_FLAG_DISCARD
      36  # define SWAP_FLAG_DISCARD	0x10000 /* enable discard for swap */
      37  #endif
      38  
      39  #ifndef SWAP_FLAG_DISCARD_ONCE
      40  # define SWAP_FLAG_DISCARD_ONCE 0x20000 /* discard swap area at swapon-time */
      41  #endif
      42  
      43  #ifndef SWAP_FLAG_DISCARD_PAGES
      44  # define SWAP_FLAG_DISCARD_PAGES 0x40000 /* discard page-clusters after use */
      45  #endif
      46  
      47  #define SWAP_FLAGS_DISCARD_VALID (SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | \
      48  				  SWAP_FLAG_DISCARD_PAGES)
      49  
      50  #ifndef SWAP_FLAG_PREFER
      51  # define SWAP_FLAG_PREFER	0x8000	/* set if swap priority specified */
      52  #endif
      53  
      54  #ifndef SWAP_FLAG_PRIO_MASK
      55  # define SWAP_FLAG_PRIO_MASK	0x7fff
      56  #endif
      57  
      58  #ifndef SWAP_FLAG_PRIO_SHIFT
      59  # define SWAP_FLAG_PRIO_SHIFT	0
      60  #endif
      61  
      62  #if !defined(HAVE_SWAPON) && defined(SYS_swapon)
      63  # include <sys/syscall.h>
      64  # define swapon(path, flags) syscall(SYS_swapon, path, flags)
      65  #endif
      66  
      67  #define MAX_PAGESIZE	(64 * 1024)
      68  
      69  #ifndef UUID_STR_LEN
      70  # define UUID_STR_LEN	37
      71  #endif
      72  
      73  enum {
      74  	SIG_SWAPSPACE = 1,
      75  	SIG_SWSUSPEND
      76  };
      77  
      78  /* column names */
      79  struct colinfo {
      80          const char *name; /* header */
      81          double     whint; /* width hint (N < 1 is in percent of termwidth) */
      82  	int        flags; /* SCOLS_FL_* */
      83          const char *help;
      84  };
      85  
      86  enum {
      87  	COL_PATH,
      88  	COL_TYPE,
      89  	COL_SIZE,
      90  	COL_USED,
      91  	COL_PRIO,
      92  	COL_UUID,
      93  	COL_LABEL
      94  };
      95  static struct colinfo infos[] = {
      96  	[COL_PATH]     = { "NAME",	0.20, 0, N_("device file or partition path") },
      97  	[COL_TYPE]     = { "TYPE",	0.20, SCOLS_FL_TRUNC, N_("type of the device")},
      98  	[COL_SIZE]     = { "SIZE",	0.20, SCOLS_FL_RIGHT, N_("size of the swap area")},
      99  	[COL_USED]     = { "USED",	0.20, SCOLS_FL_RIGHT, N_("bytes in use")},
     100  	[COL_PRIO]     = { "PRIO",	0.20, SCOLS_FL_RIGHT, N_("swap priority")},
     101  	[COL_UUID]     = { "UUID",	0.20, 0, N_("swap uuid")},
     102  	[COL_LABEL]    = { "LABEL",	0.20, 0, N_("swap label")},
     103  };
     104  
     105  
     106  /* swap area properties */
     107  struct swap_prop {
     108  	int discard;			/* discard policy */
     109  	int priority;			/* non-prioritized swap by default */
     110  	int no_fail;			/* skip device if not exist */
     111  };
     112  
     113  /* device description */
     114  struct swap_device {
     115  	const char *path;		/* device or file to be turned on */
     116  	const char *label;		/* swap label */
     117  	const char *uuid;		/* unique identifier */
     118  	unsigned int pagesize;
     119  };
     120  
     121  /* control struct */
     122  struct swapon_ctl {
     123  	int columns[ARRAY_SIZE(infos) * 2];	/* --show columns */
     124  	int ncolumns;				/* number of columns */
     125  
     126  	struct swap_prop props;		/* global settings for all devices */
     127  
     128  	unsigned int
     129  		all:1,			/* turn on all swap devices */
     130  		bytes:1,		/* display --show in bytes */
     131  		fix_page_size:1,	/* reinitialize page size */
     132  		no_heading:1,		/* toggle --show headers */
     133  		raw:1,			/* toggle --show alignment */
     134  		show:1,			/* display --show information */
     135  		verbose:1;		/* be chatty */
     136  };
     137  
     138  static int column_name_to_id(const char *name, size_t namesz)
     139  {
     140  	size_t i;
     141  
     142  	assert(name);
     143  
     144  	for (i = 0; i < ARRAY_SIZE(infos); i++) {
     145  		const char *cn = infos[i].name;
     146  
     147  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     148  			return i;
     149  	}
     150  	warnx(_("unknown column: %s"), name);
     151  	return -1;
     152  }
     153  
     154  static inline int get_column_id(const struct swapon_ctl *ctl, int num)
     155  {
     156  	assert(num < ctl->ncolumns);
     157  	assert(ctl->columns[num] < (int) ARRAY_SIZE(infos));
     158  
     159  	return ctl->columns[num];
     160  }
     161  
     162  static inline struct colinfo *get_column_info(const struct swapon_ctl *ctl, unsigned num)
     163  {
     164  	return &infos[get_column_id(ctl, num)];
     165  }
     166  
     167  static void add_scols_line(const struct swapon_ctl *ctl, struct libscols_table *table, struct libmnt_fs *fs)
     168  {
     169  	int i;
     170  	struct libscols_line *line;
     171  	blkid_probe pr = NULL;
     172  	const char *data;
     173  
     174  	assert(table);
     175  	assert(fs);
     176  
     177  	line = scols_table_new_line(table, NULL);
     178  	if (!line)
     179  		err(EXIT_FAILURE, _("failed to allocate output line"));
     180  
     181  	data = mnt_fs_get_source(fs);
     182  	if (access(data, R_OK) == 0)
     183  		pr = get_swap_prober(data);
     184  	for (i = 0; i < ctl->ncolumns; i++) {
     185  		char *str = NULL;
     186  		off_t size;
     187  
     188  		switch (get_column_id(ctl, i)) {
     189  		case COL_PATH:
     190  			xasprintf(&str, "%s", mnt_fs_get_source(fs));
     191  			break;
     192  		case COL_TYPE:
     193  			xasprintf(&str, "%s", mnt_fs_get_swaptype(fs));
     194  			break;
     195  		case COL_SIZE:
     196  			size = mnt_fs_get_size(fs);
     197  			size *= 1024;	/* convert to bytes */
     198  			if (ctl->bytes)
     199  				xasprintf(&str, "%jd", size);
     200  			else
     201  				str = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
     202  			break;
     203  		case COL_USED:
     204  			size = mnt_fs_get_usedsize(fs);
     205  			size *= 1024;	/* convert to bytes */
     206  			if (ctl->bytes)
     207  				xasprintf(&str, "%jd", size);
     208  			else
     209  				str = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
     210  			break;
     211  		case COL_PRIO:
     212  			xasprintf(&str, "%d", mnt_fs_get_priority(fs));
     213  			break;
     214  		case COL_UUID:
     215  			if (pr && !blkid_probe_lookup_value(pr, "UUID", &data, NULL))
     216  				xasprintf(&str, "%s", data);
     217  			break;
     218  		case COL_LABEL:
     219  			if (pr && !blkid_probe_lookup_value(pr, "LABEL", &data, NULL))
     220  				xasprintf(&str, "%s", data);
     221  			break;
     222  		default:
     223  			break;
     224  		}
     225  
     226  		if (str && scols_line_refer_data(line, i, str))
     227  			err(EXIT_FAILURE, _("failed to add output data"));
     228  	}
     229  	if (pr)
     230  		blkid_free_probe(pr);
     231  }
     232  
     233  static int display_summary(void)
     234  {
     235  	struct libmnt_table *st = get_swaps();
     236  	struct libmnt_iter *itr;
     237  	struct libmnt_fs *fs;
     238  
     239  	if (!st)
     240  		return -1;
     241  
     242  	if (mnt_table_is_empty(st))
     243  		return 0;
     244  
     245  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     246  	if (!itr)
     247  		err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
     248  
     249  	/* TRANSLATORS: The tabs make each field a multiple of 8 characters. Keep aligned with each entry below. */
     250  	printf(_("Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority\n"));
     251  
     252  	while (mnt_table_next_fs(st, itr, &fs) == 0) {
     253  		const char *src = mnt_fs_get_source(fs);
     254  		const char *type = mnt_fs_get_swaptype(fs);
     255  		int srclen = strlen(src);
     256  		int typelen = strlen(type);
     257  		off_t size = mnt_fs_get_size(fs);
     258  		off_t used = mnt_fs_get_usedsize(fs);
     259  
     260  		/* TRANSLATORS: Keep each field a multiple of 8 characters and aligned with the header above. */
     261  		printf("%s%*s%s%s\t%jd%s\t%jd%s\t%d\n",
     262  			src,
     263  			srclen < 40 ? 40 - srclen : 1, " ",
     264  			type,
     265  			typelen < 8 ? "\t" : "",
     266  			size,
     267  			size < 10000000 ? "\t" : "",
     268  			used,
     269  			used < 10000000 ? "\t" : "",
     270  			mnt_fs_get_priority(fs));
     271  	}
     272  
     273  	mnt_free_iter(itr);
     274  	return 0;
     275  }
     276  
     277  static int show_table(struct swapon_ctl *ctl)
     278  {
     279  	struct libmnt_table *st = get_swaps();
     280  	struct libmnt_iter *itr = NULL;
     281  	struct libmnt_fs *fs;
     282  	int i;
     283  	struct libscols_table *table = NULL;
     284  
     285  	if (!st)
     286  		return -1;
     287  
     288  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     289  	if (!itr)
     290  		err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
     291  
     292  	scols_init_debug(0);
     293  
     294  	table = scols_new_table();
     295  	if (!table)
     296  		err(EXIT_FAILURE, _("failed to allocate output table"));
     297  
     298  	scols_table_enable_raw(table, ctl->raw);
     299  	scols_table_enable_noheadings(table, ctl->no_heading);
     300  
     301  	for (i = 0; i < ctl->ncolumns; i++) {
     302  		struct colinfo *col = get_column_info(ctl, i);
     303  
     304  		if (!scols_table_new_column(table, col->name, col->whint, col->flags))
     305  			err(EXIT_FAILURE, _("failed to allocate output column"));
     306  	}
     307  
     308  	while (mnt_table_next_fs(st, itr, &fs) == 0)
     309  		add_scols_line(ctl, table, fs);
     310  
     311  	scols_print_table(table);
     312  	scols_unref_table(table);
     313  	mnt_free_iter(itr);
     314  	return 0;
     315  }
     316  
     317  /* calls mkswap */
     318  static int swap_reinitialize(struct swap_device *dev)
     319  {
     320  	pid_t pid;
     321  	int status, ret;
     322  	char const *cmd[7];
     323  	int idx=0;
     324  
     325  	assert(dev);
     326  	assert(dev->path);
     327  
     328  	warnx(_("%s: reinitializing the swap."), dev->path);
     329  
     330  	switch ((pid=fork())) {
     331  	case -1: /* fork error */
     332  		warn(_("fork failed"));
     333  		return -1;
     334  
     335  	case 0:	/* child */
     336  		if (geteuid() != getuid() && drop_permissions() != 0)
     337  			exit(EXIT_FAILURE);
     338  
     339  		cmd[idx++] = "mkswap";
     340  		if (dev->label) {
     341  			cmd[idx++] = "-L";
     342  			cmd[idx++] = dev->label;
     343  		}
     344  		if (dev->uuid) {
     345  			cmd[idx++] = "-U";
     346  			cmd[idx++] = dev->uuid;
     347  		}
     348  		cmd[idx++] = dev->path;
     349  		cmd[idx++] = NULL;
     350  		execvp(cmd[0], (char * const *) cmd);
     351  		errexec(cmd[0]);
     352  
     353  	default: /* parent */
     354  		do {
     355  			ret = waitpid(pid, &status, 0);
     356  		} while (ret == -1 && errno == EINTR);
     357  
     358  		if (ret < 0) {
     359  			warn(_("waitpid failed"));
     360  			return -1;
     361  		}
     362  
     363  		/* mkswap returns: 0=suss, >0 error */
     364  		if (WIFEXITED(status) && WEXITSTATUS(status)==0)
     365  			return 0; /* ok */
     366  		break;
     367  	}
     368  	return -1; /* error */
     369  }
     370  
     371  /* Replaces unwanted SWSUSPEND signature with swap signature */
     372  static int swap_rewrite_signature(const struct swap_device *dev)
     373  {
     374  	int fd, rc = -1;
     375  
     376  	assert(dev);
     377  	assert(dev->path);
     378  	assert(dev->pagesize);
     379  
     380  	fd = open(dev->path, O_WRONLY);
     381  	if (fd == -1) {
     382  		warn(_("cannot open %s"), dev->path);
     383  		return -1;
     384  	}
     385  
     386  	if (lseek(fd, dev->pagesize - SWAP_SIGNATURE_SZ, SEEK_SET) < 0) {
     387  		warn(_("%s: lseek failed"), dev->path);
     388  		goto err;
     389  	}
     390  
     391  	if (write(fd, (void *) SWAP_SIGNATURE,
     392  			SWAP_SIGNATURE_SZ) != SWAP_SIGNATURE_SZ) {
     393  		warn(_("%s: write signature failed"), dev->path);
     394  		goto err;
     395  	}
     396  
     397  	rc  = 0;
     398  err:
     399  	if (close_fd(fd) != 0) {
     400  		warn(_("write failed: %s"), dev->path);
     401  		rc = -1;
     402  	}
     403  	return rc;
     404  }
     405  
     406  static int swap_detect_signature(const char *buf, int *sig)
     407  {
     408  	assert(buf);
     409  	assert(sig);
     410  
     411  	if (memcmp(buf, SWAP_SIGNATURE, SWAP_SIGNATURE_SZ) == 0)
     412  		*sig = SIG_SWAPSPACE;
     413  
     414  	else if (memcmp(buf, "S1SUSPEND", 9) == 0 ||
     415  		 memcmp(buf, "S2SUSPEND", 9) == 0 ||
     416  		 memcmp(buf, "ULSUSPEND", 9) == 0 ||
     417  		 memcmp(buf, "\xed\xc3\x02\xe9\x98\x56\xe5\x0c", 8) == 0 ||
     418  		 memcmp(buf, "LINHIB0001", 10) == 0)
     419  		*sig = SIG_SWSUSPEND;
     420  	else
     421  		return 0;
     422  
     423  	return 1;
     424  }
     425  
     426  static char *swap_get_header(int fd, int *sig, unsigned int *pagesize)
     427  {
     428  	char *buf;
     429  	ssize_t datasz;
     430  	unsigned int page;
     431  
     432  	assert(sig);
     433  	assert(pagesize);
     434  
     435  	*pagesize = 0;
     436  	*sig = 0;
     437  
     438  	buf = xmalloc(MAX_PAGESIZE);
     439  
     440  	datasz = read(fd, buf, MAX_PAGESIZE);
     441  	if (datasz == (ssize_t) -1)
     442  		goto err;
     443  
     444  	for (page = 0x1000; page <= MAX_PAGESIZE; page <<= 1) {
     445  		/* skip 32k pagesize since this does not seem to
     446  		 * be supported */
     447  		if (page == 0x8000)
     448  			continue;
     449  		/* the smallest swap area is PAGE_SIZE*10, it means
     450  		 * 40k, that's less than MAX_PAGESIZE */
     451  		if (datasz < 0 || (size_t) datasz < (page - SWAP_SIGNATURE_SZ))
     452  			break;
     453  		if (swap_detect_signature(buf + page - SWAP_SIGNATURE_SZ, sig)) {
     454  			*pagesize = page;
     455  			break;
     456  		}
     457  	}
     458  
     459  	if (*pagesize)
     460  		return buf;
     461  err:
     462  	free(buf);
     463  	return NULL;
     464  }
     465  
     466  /* returns real size of swap space */
     467  static unsigned long long swap_get_size(const struct swap_device *dev,
     468  					const char *hdr)
     469  {
     470  	unsigned int last_page = 0;
     471  	const unsigned int swap_version = SWAP_VERSION;
     472  	const struct swap_header_v1_2 *s;
     473  
     474  	assert(dev);
     475  	assert(dev->pagesize > 0);
     476  
     477  	s = (const struct swap_header_v1_2 *) hdr;
     478  
     479  	if (s->version == swap_version)
     480  		last_page = s->last_page;
     481  	else if (swab32(s->version) == swap_version)
     482  		last_page = swab32(s->last_page);
     483  
     484  	return ((unsigned long long) last_page + 1) * dev->pagesize;
     485  }
     486  
     487  static void swap_get_info(struct swap_device *dev, const char *hdr)
     488  {
     489  	const struct swap_header_v1_2 *s = (const struct swap_header_v1_2 *) hdr;
     490  
     491  	assert(dev);
     492  
     493  	if (s && *s->volume_name)
     494  		dev->label = xstrdup(s->volume_name);
     495  
     496  	if (s && *s->uuid) {
     497  		const unsigned char *u = s->uuid;
     498  		char str[UUID_STR_LEN];
     499  
     500  		snprintf(str, sizeof(str),
     501  			"%02x%02x%02x%02x-"
     502  			"%02x%02x-%02x%02x-"
     503  			"%02x%02x-%02x%02x%02x%02x%02x%02x",
     504  			u[0], u[1], u[2], u[3],
     505  			u[4], u[5], u[6], u[7],
     506  			u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15]);
     507  		dev->uuid = xstrdup(str);
     508  	}
     509  }
     510  
     511  static int swapon_checks(const struct swapon_ctl *ctl, struct swap_device *dev)
     512  {
     513  	struct stat st;
     514  	int fd, sig;
     515  	char *hdr = NULL;
     516  	unsigned long long devsize = 0;
     517  	int permMask;
     518  
     519  	assert(ctl);
     520  	assert(dev);
     521  	assert(dev->path);
     522  
     523  	fd = open(dev->path, O_RDONLY);
     524  	if (fd == -1) {
     525  		warn(_("cannot open %s"), dev->path);
     526  		goto err;
     527  	}
     528  
     529  	if (fstat(fd, &st) < 0) {
     530  		warn(_("stat of %s failed"), dev->path);
     531  		goto err;
     532  	}
     533  
     534  	permMask = S_ISBLK(st.st_mode) ? 07007 : 07077;
     535  	if ((st.st_mode & permMask) != 0)
     536  		warnx(_("%s: insecure permissions %04o, %04o suggested."),
     537  				dev->path, st.st_mode & 07777,
     538  				~permMask & 0666);
     539  
     540  	if (S_ISREG(st.st_mode) && st.st_uid != 0)
     541  		warnx(_("%s: insecure file owner %d, 0 (root) suggested."),
     542  				dev->path, st.st_uid);
     543  
     544  	/* test for holes by LBT */
     545  	if (S_ISREG(st.st_mode)) {
     546  		if (st.st_blocks * 512L < st.st_size) {
     547  			warnx(_("%s: skipping - it appears to have holes."),
     548  				dev->path);
     549  			goto err;
     550  		}
     551  		devsize = st.st_size;
     552  	}
     553  
     554  	if (S_ISBLK(st.st_mode) && blkdev_get_size(fd, &devsize)) {
     555  		warnx(_("%s: get size failed"), dev->path);
     556  		goto err;
     557  	}
     558  
     559  	hdr = swap_get_header(fd, &sig, &dev->pagesize);
     560  	if (!hdr) {
     561  		warnx(_("%s: read swap header failed"), dev->path);
     562  		goto err;
     563  	}
     564  
     565  	if (ctl->verbose)
     566  		warnx(_("%s: found signature [pagesize=%d, signature=%s]"),
     567  			dev->path,
     568  			dev->pagesize,
     569  			sig == SIG_SWAPSPACE ? "swap" :
     570  			sig == SIG_SWSUSPEND ? "suspend" : "unknown");
     571  
     572  	if (sig == SIG_SWAPSPACE && dev->pagesize) {
     573  		unsigned long long swapsize = swap_get_size(dev, hdr);
     574  		int syspg = getpagesize();
     575  
     576  		if (ctl->verbose)
     577  			warnx(_("%s: pagesize=%d, swapsize=%llu, devsize=%llu"),
     578  				dev->path, dev->pagesize, swapsize, devsize);
     579  
     580  		if (swapsize > devsize) {
     581  			if (ctl->verbose)
     582  				warnx(_("%s: last_page 0x%08llx is larger"
     583  					" than actual size of swapspace"),
     584  					dev->path, swapsize);
     585  
     586  		} else if (syspg < 0 || (unsigned int) syspg != dev->pagesize) {
     587  			if (ctl->fix_page_size) {
     588  				int rc;
     589  
     590  				swap_get_info(dev, hdr);
     591  
     592  				warnx(_("%s: swap format pagesize does not match."),
     593  					dev->path);
     594  				rc = swap_reinitialize(dev);
     595  				if (rc < 0)
     596  					goto err;
     597  			} else
     598  				warnx(_("%s: swap format pagesize does not match. "
     599  					"(Use --fixpgsz to reinitialize it.)"),
     600  					dev->path);
     601  		}
     602  	} else if (sig == SIG_SWSUSPEND) {
     603  		/* We have to reinitialize swap with old (=useless) software suspend
     604  		 * data. The problem is that if we don't do it, then we get data
     605  		 * corruption the next time an attempt at unsuspending is made.
     606  		 */
     607  		warnx(_("%s: software suspend data detected. "
     608  				"Rewriting the swap signature."),
     609  			dev->path);
     610  		if (swap_rewrite_signature(dev) < 0)
     611  			goto err;
     612  	}
     613  
     614  	free(hdr);
     615  	close(fd);
     616  	return 0;
     617  err:
     618  	if (fd != -1)
     619  		close(fd);
     620  	free(hdr);
     621  	return -1;
     622  }
     623  
     624  static int do_swapon(const struct swapon_ctl *ctl,
     625  		     const struct swap_prop *prop,
     626  		     const char *spec,
     627  		     int canonic)
     628  {
     629  	struct swap_device dev = { .path = NULL };
     630  	int status;
     631  	int flags = 0;
     632  	int priority;
     633  
     634  	assert(ctl);
     635  	assert(prop);
     636  
     637  	if (!canonic) {
     638  		dev.path = mnt_resolve_spec(spec, mntcache);
     639  		if (!dev.path)
     640  			return cannot_find(spec);
     641  	} else
     642  		dev.path = spec;
     643  
     644  	priority = prop->priority;
     645  
     646  	if (swapon_checks(ctl, &dev))
     647  		return -1;
     648  
     649  #ifdef SWAP_FLAG_PREFER
     650  	if (priority >= 0) {
     651  		if (priority > SWAP_FLAG_PRIO_MASK)
     652  			priority = SWAP_FLAG_PRIO_MASK;
     653  
     654  		flags = SWAP_FLAG_PREFER
     655  			| ((priority & SWAP_FLAG_PRIO_MASK)
     656  			   << SWAP_FLAG_PRIO_SHIFT);
     657  	}
     658  #endif
     659  	/*
     660  	 * Validate the discard flags passed and set them
     661  	 * accordingly before calling sys_swapon.
     662  	 */
     663  	if (prop->discard && !(prop->discard & ~SWAP_FLAGS_DISCARD_VALID)) {
     664  		/*
     665  		 * If we get here with both discard policy flags set,
     666  		 * we just need to tell the kernel to enable discards
     667  		 * and it will do correctly, just as we expect.
     668  		 */
     669  		if ((prop->discard & SWAP_FLAG_DISCARD_ONCE) &&
     670  		    (prop->discard & SWAP_FLAG_DISCARD_PAGES))
     671  			flags |= SWAP_FLAG_DISCARD;
     672  		else
     673  			flags |= prop->discard;
     674  	}
     675  
     676  	if (ctl->verbose)
     677  		printf(_("swapon %s\n"), dev.path);
     678  
     679  	status = swapon(dev.path, flags);
     680  	if (status < 0)
     681  		warn(_("%s: swapon failed"), dev.path);
     682  
     683  	return status;
     684  }
     685  
     686  static int swapon_by_label(struct swapon_ctl *ctl, const char *label)
     687  {
     688  	char *device = mnt_resolve_tag("LABEL", label, mntcache);
     689  	return device ? do_swapon(ctl, &ctl->props, device, TRUE) :  cannot_find(label);
     690  }
     691  
     692  static int swapon_by_uuid(struct swapon_ctl *ctl, const char *uuid)
     693  {
     694  	char *device = mnt_resolve_tag("UUID", uuid, mntcache);
     695  	return device ? do_swapon(ctl, &ctl->props, device, TRUE) : cannot_find(uuid);
     696  }
     697  
     698  /* -o <options> or fstab */
     699  static int parse_options(struct swap_prop *props, const char *options)
     700  {
     701  	char *arg = NULL;
     702  	size_t argsz = 0;
     703  
     704  	assert(props);
     705  	assert(options);
     706  
     707  	if (mnt_optstr_get_option(options, "nofail", NULL, NULL) == 0)
     708  		props->no_fail = 1;
     709  
     710  	if (mnt_optstr_get_option(options, "discard", &arg, &argsz) == 0) {
     711  		props->discard |= SWAP_FLAG_DISCARD;
     712  
     713  		if (arg) {
     714  			/* only single-time discards are wanted */
     715  			if (strncmp(arg, "once", argsz) == 0)
     716  				props->discard |= SWAP_FLAG_DISCARD_ONCE;
     717  
     718  			/* do discard for every released swap page */
     719  			if (strncmp(arg, "pages", argsz) == 0)
     720  				props->discard |= SWAP_FLAG_DISCARD_PAGES;
     721  		}
     722  	}
     723  
     724  	arg = NULL;
     725  	if (mnt_optstr_get_option(options, "pri", &arg, &argsz) == 0 && arg) {
     726  		char *end = NULL;
     727  		int n;
     728  
     729  		errno = 0;
     730  		n = (int) strtol(arg, &end, 10);
     731  		if (errno == 0 && end && end > arg)
     732  			props->priority = n;
     733  	}
     734  	return 0;
     735  }
     736  
     737  
     738  static int swapon_all(struct swapon_ctl *ctl, const char *filename)
     739  {
     740  	struct libmnt_table *tb = get_fstab(filename);
     741  	struct libmnt_iter *itr;
     742  	struct libmnt_fs *fs;
     743  	int status = 0;
     744  
     745  	if (!tb)
     746  		err(EXIT_FAILURE, _("failed to parse %s"), mnt_get_fstab_path());
     747  
     748  	itr = mnt_new_iter(MNT_ITER_FORWARD);
     749  	if (!itr)
     750  		err(EXIT_FAILURE, _("failed to initialize libmount iterator"));
     751  
     752  	while (mnt_table_find_next_fs(tb, itr, match_swap, NULL, &fs) == 0) {
     753  		/* defaults */
     754  		const char *opts;
     755  		const char *device;
     756  		struct swap_prop prop;		/* per device setting */
     757  
     758  		if (mnt_fs_get_option(fs, "noauto", NULL, NULL) == 0) {
     759  			if (ctl->verbose)
     760  				warnx(_("%s: noauto option -- ignored"), mnt_fs_get_source(fs));
     761  			continue;
     762  		}
     763  
     764  		/* default setting */
     765  		prop = ctl->props;
     766  
     767  		/* overwrite default by setting from fstab */
     768  		opts = mnt_fs_get_options(fs);
     769  		if (opts)
     770  			parse_options(&prop, opts);
     771  
     772  		/* convert LABEL=, UUID= etc. from fstab to device name */
     773  		device = mnt_resolve_spec(mnt_fs_get_source(fs), mntcache);
     774  		if (!device) {
     775  			if (!prop.no_fail)
     776  				status |= cannot_find(mnt_fs_get_source(fs));
     777  			continue;
     778  		}
     779  
     780  		if (is_active_swap(device)) {
     781  			if (ctl->verbose)
     782  				warnx(_("%s: already active -- ignored"), device);
     783  			continue;
     784  		}
     785  
     786  		if (prop.no_fail && access(device, R_OK) != 0) {
     787  			if (ctl->verbose)
     788  				warnx(_("%s: inaccessible -- ignored"), device);
     789  			continue;
     790  		}
     791  
     792  		/* swapon */
     793  		status |= do_swapon(ctl, &prop, device, TRUE);
     794  	}
     795  
     796  	mnt_free_iter(itr);
     797  	return status;
     798  }
     799  
     800  
     801  static void __attribute__((__noreturn__)) usage(void)
     802  {
     803  	FILE *out = stdout;
     804  	size_t i;
     805  
     806  	fputs(USAGE_HEADER, out);
     807  	fprintf(out, _(" %s [options] [<spec>]\n"), program_invocation_short_name);
     808  
     809  	fputs(USAGE_SEPARATOR, out);
     810  	fputs(_("Enable devices and files for paging and swapping.\n"), out);
     811  
     812  	fputs(USAGE_OPTIONS, out);
     813  	fputs(_(" -a, --all                enable all swaps from /etc/fstab\n"), out);
     814  	fputs(_(" -d, --discard[=<policy>] enable swap discards, if supported by device\n"), out);
     815  	fputs(_(" -e, --ifexists           silently skip devices that do not exist\n"), out);
     816  	fputs(_(" -f, --fixpgsz            reinitialize the swap space if necessary\n"), out);
     817  	fputs(_(" -o, --options <list>     comma-separated list of swap options\n"), out);
     818  	fputs(_(" -p, --priority <prio>    specify the priority of the swap device\n"), out);
     819  	fputs(_(" -s, --summary            display summary about used swap devices (DEPRECATED)\n"), out);
     820  	fputs(_(" -T, --fstab <path>       alternative file to /etc/fstab\n"), out);
     821  	fputs(_("     --show[=<columns>]   display summary in definable table\n"), out);
     822  	fputs(_("     --noheadings         don't print table heading (with --show)\n"), out);
     823  	fputs(_("     --raw                use the raw output format (with --show)\n"), out);
     824  	fputs(_("     --bytes              display swap size in bytes in --show output\n"), out);
     825  	fputs(_(" -v, --verbose            verbose mode\n"), out);
     826  
     827  	fputs(USAGE_SEPARATOR, out);
     828  	printf(USAGE_HELP_OPTIONS(26));
     829  
     830  	fputs(_("\nThe <spec> parameter:\n" \
     831  		" -L <label>             synonym for LABEL=<label>\n"
     832  		" -U <uuid>              synonym for UUID=<uuid>\n"
     833  		" LABEL=<label>          specifies device by swap area label\n"
     834  		" UUID=<uuid>            specifies device by swap area UUID\n"
     835  		" PARTLABEL=<label>      specifies device by partition label\n"
     836  		" PARTUUID=<uuid>        specifies device by partition UUID\n"
     837  		" <device>               name of device to be used\n"
     838  		" <file>                 name of file to be used\n"), out);
     839  
     840  	fputs(_("\nAvailable discard policy types (for --discard):\n"
     841  		" once    : only single-time area discards are issued\n"
     842  		" pages   : freed pages are discarded before they are reused\n"
     843  		"If no policy is selected, both discard types are enabled (default).\n"), out);
     844  
     845  	fputs(USAGE_COLUMNS, out);
     846  	for (i = 0; i < ARRAY_SIZE(infos); i++)
     847  		fprintf(out, " %-5s  %s\n", infos[i].name, _(infos[i].help));
     848  
     849  	printf(USAGE_MAN_TAIL("swapon(8)"));
     850  	exit(EXIT_SUCCESS);
     851  }
     852  
     853  int main(int argc, char *argv[])
     854  {
     855  	int status = 0, c;
     856  	size_t i;
     857  	char *options = NULL, *fstab_filename = NULL;
     858  
     859  	enum {
     860  		BYTES_OPTION = CHAR_MAX + 1,
     861  		NOHEADINGS_OPTION,
     862  		RAW_OPTION,
     863  		SHOW_OPTION,
     864  		OPT_LIST_TYPES
     865  	};
     866  
     867  	static const struct option long_opts[] = {
     868  		{ "priority",   required_argument, NULL, 'p'               },
     869  		{ "discard",    optional_argument, NULL, 'd'               },
     870  		{ "ifexists",   no_argument,       NULL, 'e'               },
     871  		{ "options",    optional_argument, NULL, 'o'               },
     872  		{ "summary",    no_argument,       NULL, 's'               },
     873  		{ "fixpgsz",    no_argument,       NULL, 'f'               },
     874  		{ "all",        no_argument,       NULL, 'a'               },
     875  		{ "help",       no_argument,       NULL, 'h'               },
     876  		{ "verbose",    no_argument,       NULL, 'v'               },
     877  		{ "version",    no_argument,       NULL, 'V'               },
     878  		{ "show",       optional_argument, NULL, SHOW_OPTION       },
     879  		{ "output-all", no_argument,       NULL, OPT_LIST_TYPES    },
     880  		{ "noheadings", no_argument,       NULL, NOHEADINGS_OPTION },
     881  		{ "raw",        no_argument,       NULL, RAW_OPTION        },
     882  		{ "bytes",      no_argument,       NULL, BYTES_OPTION      },
     883  		{ "fstab",      required_argument, NULL, 'T'               },
     884  		{ NULL, 0, NULL, 0 }
     885  	};
     886  
     887  	static const ul_excl_t excl[] = {       /* rows and cols in ASCII order */
     888  		{ 'a','o','s', SHOW_OPTION },
     889  		{ 'a','o', BYTES_OPTION },
     890  		{ 'a','o', NOHEADINGS_OPTION },
     891  		{ 'a','o', RAW_OPTION },
     892  		{ 0 }
     893  	};
     894  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     895  
     896  	struct swapon_ctl ctl;
     897  
     898  	setlocale(LC_ALL, "");
     899  	bindtextdomain(PACKAGE, LOCALEDIR);
     900  	textdomain(PACKAGE);
     901  	close_stdout_atexit();
     902  
     903  	memset(&ctl, 0, sizeof(struct swapon_ctl));
     904  	ctl.props.priority = -1;
     905  
     906  	mnt_init_debug(0);
     907  	mntcache = mnt_new_cache();
     908  
     909  	while ((c = getopt_long(argc, argv, "ahd::efo:p:svVL:U:T:",
     910  				long_opts, NULL)) != -1) {
     911  
     912  		err_exclusive_options(c, long_opts, excl, excl_st);
     913  
     914  		switch (c) {
     915  		case 'a':		/* all */
     916  			ctl.all = 1;
     917  			break;
     918  		case 'o':
     919  			options = optarg;
     920  			break;
     921  		case 'p':		/* priority */
     922  			ctl.props.priority = strtos16_or_err(optarg,
     923  					   _("failed to parse priority"));
     924  			break;
     925  		case 'L':
     926  			add_label(optarg);
     927  			break;
     928  		case 'U':
     929  			add_uuid(optarg);
     930  			break;
     931  		case 'T':
     932  			fstab_filename = optarg;
     933  			break;
     934  		case 'd':
     935  			ctl.props.discard |= SWAP_FLAG_DISCARD;
     936  			if (optarg) {
     937  				if (*optarg == '=')
     938  					optarg++;
     939  
     940  				if (strcmp(optarg, "once") == 0)
     941  					ctl.props.discard |= SWAP_FLAG_DISCARD_ONCE;
     942  				else if (strcmp(optarg, "pages") == 0)
     943  					ctl.props.discard |= SWAP_FLAG_DISCARD_PAGES;
     944  				else
     945  					errx(EXIT_FAILURE, _("unsupported discard policy: %s"), optarg);
     946  			}
     947  			break;
     948  		case 'e':               /* ifexists */
     949  			ctl.props.no_fail = 1;
     950  			break;
     951  		case 'f':
     952  			ctl.fix_page_size = 1;
     953  			break;
     954  		case 's':		/* status report */
     955  			status = display_summary();
     956  			return status;
     957  		case 'v':		/* be chatty */
     958  			ctl.verbose = 1;
     959  			break;
     960  		case SHOW_OPTION:
     961  			if (optarg) {
     962  				ctl.ncolumns = string_to_idarray(optarg,
     963  							     ctl.columns,
     964  							     ARRAY_SIZE(ctl.columns),
     965  							     column_name_to_id);
     966  				if (ctl.ncolumns < 0)
     967  					return EXIT_FAILURE;
     968  			}
     969  			ctl.show = 1;
     970  			break;
     971  		case OPT_LIST_TYPES:
     972  			for (ctl.ncolumns = 0; (size_t)ctl.ncolumns < ARRAY_SIZE(infos); ctl.ncolumns++)
     973  				ctl.columns[ctl.ncolumns] = ctl.ncolumns;
     974  			break;
     975  		case NOHEADINGS_OPTION:
     976  			ctl.no_heading = 1;
     977  			break;
     978  		case RAW_OPTION:
     979  			ctl.raw = 1;
     980  			break;
     981  		case BYTES_OPTION:
     982  			ctl.bytes = 1;
     983  			break;
     984  		case 0:
     985  			break;
     986  
     987  		case 'h':		/* help */
     988  			usage();
     989  		case 'V':		/* version */
     990  			print_version(EXIT_SUCCESS);
     991  		default:
     992  			errtryhelp(EXIT_FAILURE);
     993  		}
     994  	}
     995  	argv += optind;
     996  
     997  	if (ctl.show || (!ctl.all && !numof_labels() && !numof_uuids() && *argv == NULL)) {
     998  		if (!ctl.ncolumns) {
     999  			/* default columns */
    1000  			ctl.columns[ctl.ncolumns++] = COL_PATH;
    1001  			ctl.columns[ctl.ncolumns++] = COL_TYPE;
    1002  			ctl.columns[ctl.ncolumns++] = COL_SIZE;
    1003  			ctl.columns[ctl.ncolumns++] = COL_USED;
    1004  			ctl.columns[ctl.ncolumns++] = COL_PRIO;
    1005  		}
    1006  		status = show_table(&ctl);
    1007  		return status;
    1008  	}
    1009  
    1010  	if (ctl.props.no_fail && !ctl.all) {
    1011  		warnx(_("bad usage"));
    1012  		errtryhelp(EXIT_FAILURE);
    1013  	}
    1014  
    1015  	if (ctl.all)
    1016  		status |= swapon_all(&ctl, fstab_filename);
    1017  
    1018  	if (options)
    1019  		parse_options(&ctl.props, options);
    1020  
    1021  	for (i = 0; i < numof_labels(); i++)
    1022  		status |= swapon_by_label(&ctl, get_label(i));
    1023  
    1024  	for (i = 0; i < numof_uuids(); i++)
    1025  		status |= swapon_by_uuid(&ctl, get_uuid(i));
    1026  
    1027  	while (*argv != NULL)
    1028  		status |= do_swapon(&ctl, &ctl.props, *argv++, FALSE);
    1029  
    1030  	free_tables();
    1031  	mnt_unref_cache(mntcache);
    1032  
    1033  	return status;
    1034  }