(root)/
util-linux-2.39/
sys-utils/
zramctl.c
       1  /*
       2   * zramctl - control compressed block devices in RAM
       3   *
       4   * Copyright (c) 2014 Timofey Titovets <Nefelim4ag@gmail.com>
       5   * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
       6   *
       7   * This program is free software; you can redistribute it and/or
       8   * modify it under the terms of the GNU General Public License as
       9   * published by the Free Software Foundation.
      10   *
      11   * This program is distributed in the hope that it would be useful,
      12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14   * GNU General Public License for more details.
      15   *
      16   * You should have received a copy of the GNU General Public License along
      17   * with this program; if not, write to the Free Software Foundation, Inc.,
      18   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19   */
      20  
      21  #include <getopt.h>
      22  #include <stdlib.h>
      23  #include <string.h>
      24  #include <stdarg.h>
      25  #include <assert.h>
      26  #include <sys/types.h>
      27  #include <dirent.h>
      28  
      29  #include <libsmartcols.h>
      30  
      31  #include "c.h"
      32  #include "nls.h"
      33  #include "closestream.h"
      34  #include "strutils.h"
      35  #include "xalloc.h"
      36  #include "sysfs.h"
      37  #include "optutils.h"
      38  #include "ismounted.h"
      39  #include "strv.h"
      40  #include "path.h"
      41  #include "pathnames.h"
      42  
      43  /*#define CONFIG_ZRAM_DEBUG*/
      44  
      45  #ifdef CONFIG_ZRAM_DEBUG
      46  # define DBG(x)	 do { fputs("zram: ", stderr); x; fputc('\n', stderr); } while(0)
      47  #else
      48  # define DBG(x)
      49  #endif
      50  
      51  /* status output columns */
      52  struct colinfo {
      53  	const char *name;
      54  	double whint;
      55  	int flags;
      56  	const char *help;
      57  };
      58  
      59  enum {
      60  	COL_NAME = 0,
      61  	COL_DISKSIZE,
      62  	COL_ORIG_SIZE,
      63  	COL_COMP_SIZE,
      64  	COL_ALGORITHM,
      65  	COL_STREAMS,
      66  	COL_ZEROPAGES,
      67  	COL_MEMTOTAL,
      68  	COL_MEMLIMIT,
      69  	COL_MEMUSED,
      70  	COL_MIGRATED,
      71  	COL_MOUNTPOINT
      72  };
      73  
      74  static const struct colinfo infos[] = {
      75  	[COL_NAME]      = { "NAME",      0.25, 0, N_("zram device name") },
      76  	[COL_DISKSIZE]  = { "DISKSIZE",     5, SCOLS_FL_RIGHT, N_("limit on the uncompressed amount of data") },
      77  	[COL_ORIG_SIZE] = { "DATA",         5, SCOLS_FL_RIGHT, N_("uncompressed size of stored data") },
      78  	[COL_COMP_SIZE] = { "COMPR",        5, SCOLS_FL_RIGHT, N_("compressed size of stored data") },
      79  	[COL_ALGORITHM] = { "ALGORITHM",    3, 0, N_("the selected compression algorithm") },
      80  	[COL_STREAMS]   = { "STREAMS",      3, SCOLS_FL_RIGHT, N_("number of concurrent compress operations") },
      81  	[COL_ZEROPAGES] = { "ZERO-PAGES",   3, SCOLS_FL_RIGHT, N_("empty pages with no allocated memory") },
      82  	[COL_MEMTOTAL]  = { "TOTAL",        5, SCOLS_FL_RIGHT, N_("all memory including allocator fragmentation and metadata overhead") },
      83  	[COL_MEMLIMIT]  = { "MEM-LIMIT",    5, SCOLS_FL_RIGHT, N_("memory limit used to store compressed data") },
      84  	[COL_MEMUSED]   = { "MEM-USED",     5, SCOLS_FL_RIGHT, N_("memory zram have been consumed to store compressed data") },
      85  	[COL_MIGRATED]  = { "MIGRATED",     5, SCOLS_FL_RIGHT, N_("number of objects migrated by compaction") },
      86  	[COL_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
      87  };
      88  
      89  static int columns[ARRAY_SIZE(infos) * 2] = {-1};
      90  static int ncolumns;
      91  
      92  enum {
      93  	MM_ORIG_DATA_SIZE = 0,
      94  	MM_COMPR_DATA_SIZE,
      95  	MM_MEM_USED_TOTAL,
      96  	MM_MEM_LIMIT,
      97  	MM_MEM_USED_MAX,
      98  	MM_ZERO_PAGES,
      99  	MM_NUM_MIGRATED
     100  };
     101  
     102  static const char *mm_stat_names[] = {
     103  	[MM_ORIG_DATA_SIZE]  = "orig_data_size",
     104  	[MM_COMPR_DATA_SIZE] = "compr_data_size",
     105  	[MM_MEM_USED_TOTAL]  = "mem_used_total",
     106  	[MM_MEM_LIMIT]       = "mem_limit",
     107  	[MM_MEM_USED_MAX]    = "mem_used_max",
     108  	[MM_ZERO_PAGES]      = "zero_pages",
     109  	[MM_NUM_MIGRATED]    = "num_migrated"
     110  };
     111  
     112  struct zram {
     113  	char	devname[32];
     114  	struct	path_cxt *sysfs;	/* device specific sysfs directory */
     115  	char	**mm_stat;
     116  
     117  	unsigned int mm_stat_probed : 1,
     118  		     control_probed : 1,
     119  		     has_control : 1;	/* has /sys/class/zram-control/ */
     120  };
     121  
     122  static unsigned int raw, no_headings, inbytes;
     123  static struct path_cxt *__control;
     124  
     125  static int get_column_id(int num)
     126  {
     127  	assert(num < ncolumns);
     128  	assert(columns[num] < (int) ARRAY_SIZE(infos));
     129  	return columns[num];
     130  }
     131  
     132  static const struct colinfo *get_column_info(int num)
     133  {
     134  	return &infos[ get_column_id(num) ];
     135  }
     136  
     137  static int column_name_to_id(const char *name, size_t namesz)
     138  {
     139  	size_t i;
     140  
     141  	for (i = 0; i < ARRAY_SIZE(infos); i++) {
     142  		const char *cn = infos[i].name;
     143  
     144  		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
     145  			return i;
     146  	}
     147  	warnx(_("unknown column: %s"), name);
     148  	return -1;
     149  }
     150  
     151  static void zram_reset_stat(struct zram *z)
     152  {
     153  	if (z) {
     154  		strv_free(z->mm_stat);
     155  		z->mm_stat = NULL;
     156  		z->mm_stat_probed = 0;
     157  	}
     158  }
     159  
     160  static void zram_set_devname(struct zram *z, const char *devname, size_t n)
     161  {
     162  	assert(z);
     163  
     164  	if (!devname)
     165  		snprintf(z->devname, sizeof(z->devname), "/dev/zram%zu", n);
     166  	else
     167  		xstrncpy(z->devname, devname, sizeof(z->devname));
     168  
     169  	DBG(fprintf(stderr, "set devname: %s", z->devname));
     170  	ul_unref_path(z->sysfs);
     171  	z->sysfs = NULL;
     172  	zram_reset_stat(z);
     173  }
     174  
     175  static int zram_get_devnum(struct zram *z)
     176  {
     177  	int n;
     178  
     179  	assert(z);
     180  
     181  	if (sscanf(z->devname, "/dev/zram%d", &n) == 1)
     182  		return n;
     183  	return -EINVAL;
     184  }
     185  
     186  static struct zram *new_zram(const char *devname)
     187  {
     188  	struct zram *z = xcalloc(1, sizeof(struct zram));
     189  
     190  	DBG(fprintf(stderr, "new: %p", z));
     191  	if (devname)
     192  		zram_set_devname(z, devname, 0);
     193  	return z;
     194  }
     195  
     196  static void free_zram(struct zram *z)
     197  {
     198  	if (!z)
     199  		return;
     200  	DBG(fprintf(stderr, "free: %p", z));
     201  	ul_unref_path(z->sysfs);
     202  	zram_reset_stat(z);
     203  	free(z);
     204  }
     205  
     206  static struct path_cxt *zram_get_sysfs(struct zram *z)
     207  {
     208  	assert(z);
     209  
     210  	if (!z->sysfs) {
     211  		dev_t devno = sysfs_devname_to_devno(z->devname);
     212  		if (!devno)
     213  			return NULL;
     214  		z->sysfs = ul_new_sysfs_path(devno, NULL, NULL);
     215  		if (!z->sysfs)
     216  			return NULL;
     217  		if (*z->devname != '/')
     218  			/* canonicalize the device name according to /sys */
     219  			sysfs_blkdev_get_path(z->sysfs, z->devname, sizeof(z->devname));
     220  	}
     221  
     222  	return z->sysfs;
     223  }
     224  
     225  static inline int zram_exist(struct zram *z)
     226  {
     227  	assert(z);
     228  
     229  	errno = 0;
     230  	if (zram_get_sysfs(z) == NULL) {
     231  		errno = ENODEV;
     232  		return 0;
     233  	}
     234  
     235  	DBG(fprintf(stderr, "%s exists", z->devname));
     236  	return 1;
     237  }
     238  
     239  static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num)
     240  {
     241  	struct path_cxt *sysfs = zram_get_sysfs(z);
     242  	if (!sysfs)
     243  		return -EINVAL;
     244  	DBG(fprintf(stderr, "%s writing %ju to %s", z->devname, num, attr));
     245  	return ul_path_write_u64(sysfs, num, attr);
     246  }
     247  
     248  static int zram_set_strparm(struct zram *z, const char *attr, const char *str)
     249  {
     250  	struct path_cxt *sysfs = zram_get_sysfs(z);
     251  	if (!sysfs)
     252  		return -EINVAL;
     253  	DBG(fprintf(stderr, "%s writing %s to %s", z->devname, str, attr));
     254  	return ul_path_write_string(sysfs, str, attr);
     255  }
     256  
     257  
     258  static int zram_used(struct zram *z)
     259  {
     260  	uint64_t size;
     261  	struct path_cxt *sysfs = zram_get_sysfs(z);
     262  
     263  	if (sysfs &&
     264  	    ul_path_read_u64(sysfs, &size, "disksize") == 0 &&
     265  	    size > 0) {
     266  
     267  		DBG(fprintf(stderr, "%s used", z->devname));
     268  		return 1;
     269  	}
     270  	DBG(fprintf(stderr, "%s unused", z->devname));
     271  	return 0;
     272  }
     273  
     274  static int zram_has_control(struct zram *z)
     275  {
     276  	if (!z->control_probed) {
     277  		z->has_control = access(_PATH_SYS_CLASS "/zram-control/", F_OK) == 0 ? 1 : 0;
     278  		z->control_probed = 1;
     279  		DBG(fprintf(stderr, "zram-control: %s", z->has_control ? "yes" : "no"));
     280  	}
     281  
     282  	return z->has_control;
     283  }
     284  
     285  static struct path_cxt *zram_get_control(void)
     286  {
     287  	if (!__control)
     288  		__control = ul_new_path(_PATH_SYS_CLASS "/zram-control");
     289  	return __control;
     290  }
     291  
     292  static int zram_control_add(struct zram *z)
     293  {
     294  	int n = 0;
     295  	struct path_cxt *ctl;
     296  
     297  	if (!zram_has_control(z) || !(ctl = zram_get_control()))
     298  		return -ENOSYS;
     299  
     300  	if (ul_path_read_s32(ctl, &n, "hot_add") != 0 || n < 0)
     301  		return n;
     302  
     303  	DBG(fprintf(stderr, "hot-add: %d", n));
     304  	zram_set_devname(z, NULL, n);
     305  	return 0;
     306  }
     307  
     308  static int zram_control_remove(struct zram *z)
     309  {
     310  	struct path_cxt *ctl;
     311  	int n;
     312  
     313  	if (!zram_has_control(z) || !(ctl = zram_get_control()))
     314  		return -ENOSYS;
     315  
     316  	n = zram_get_devnum(z);
     317  	if (n < 0)
     318  		return n;
     319  
     320  	DBG(fprintf(stderr, "hot-remove: %d", n));
     321  	return ul_path_write_u64(ctl, n, "hot_remove");
     322  }
     323  
     324  static struct zram *find_free_zram(void)
     325  {
     326  	struct zram *z = new_zram(NULL);
     327  	size_t i;
     328  	int isfree = 0;
     329  
     330  	for (i = 0; isfree == 0; i++) {
     331  		DBG(fprintf(stderr, "find free: checking zram%zu", i));
     332  		zram_set_devname(z, NULL, i);
     333  		if (!zram_exist(z) && zram_control_add(z) != 0)
     334  			break;
     335  		isfree = !zram_used(z);
     336  	}
     337  	if (!isfree) {
     338  		free_zram(z);
     339  		z = NULL;
     340  	}
     341  	return z;
     342  }
     343  
     344  static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
     345  {
     346  	struct path_cxt *sysfs;
     347  	const char *name;
     348  	char *str = NULL;
     349  	uint64_t num;
     350  
     351  	assert(idx < ARRAY_SIZE(mm_stat_names));
     352  	assert(z);
     353  
     354  	sysfs = zram_get_sysfs(z);
     355  	if (!sysfs)
     356  		return NULL;
     357  
     358  	/* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */
     359  	if (!z->mm_stat && !z->mm_stat_probed) {
     360  		if (ul_path_read_string(sysfs, &str, "mm_stat") > 0 && str) {
     361  			z->mm_stat = strv_split(str, " ");
     362  
     363  			/* make sure kernel provides mm_stat as expected */
     364  			if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) {
     365  				strv_free(z->mm_stat);
     366  				z->mm_stat = NULL;
     367  			}
     368  		}
     369  		z->mm_stat_probed = 1;
     370  		free(str);
     371  		str = NULL;
     372  	}
     373  
     374  	if (z->mm_stat) {
     375  		if (bytes)
     376  			return xstrdup(z->mm_stat[idx]);
     377  
     378  		num = strtou64_or_err(z->mm_stat[idx], _("Failed to parse mm_stat"));
     379  		return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
     380  	}
     381  
     382  	/* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */
     383  	name = mm_stat_names[idx];
     384  	if (bytes) {
     385  		ul_path_read_string(sysfs, &str, name);
     386  		return str;
     387  
     388  	}
     389  
     390  	if (ul_path_read_u64(sysfs, &num, name) == 0)
     391  		return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
     392  
     393  	return NULL;
     394  }
     395  
     396  static void fill_table_row(struct libscols_table *tb, struct zram *z)
     397  {
     398  	static struct libscols_line *ln;
     399  	struct path_cxt *sysfs;
     400  	size_t i;
     401  	uint64_t num;
     402  
     403  	assert(tb);
     404  	assert(z);
     405  
     406  	DBG(fprintf(stderr, "%s: filling status table", z->devname));
     407  
     408  	sysfs = zram_get_sysfs(z);
     409  	if (!sysfs)
     410  		return;
     411  
     412  	ln = scols_table_new_line(tb, NULL);
     413  	if (!ln)
     414  		err(EXIT_FAILURE, _("failed to allocate output line"));
     415  
     416  	for (i = 0; i < (size_t) ncolumns; i++) {
     417  		char *str = NULL;
     418  
     419  		switch (get_column_id(i)) {
     420  		case COL_NAME:
     421  			str = xstrdup(z->devname);
     422  			break;
     423  		case COL_DISKSIZE:
     424  			if (inbytes)
     425  				ul_path_read_string(sysfs, &str, "disksize");
     426  
     427  			else if (ul_path_read_u64(sysfs, &num, "disksize") == 0)
     428  				str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
     429  			break;
     430  		case COL_ALGORITHM:
     431  		{
     432  			char *alg = NULL;
     433  
     434  			ul_path_read_string(sysfs, &alg, "comp_algorithm");
     435  			if (alg) {
     436  				char* lbr = strrchr(alg, '[');
     437  				char* rbr = strrchr(alg, ']');
     438  
     439  				if (lbr != NULL && rbr != NULL && rbr - lbr > 1)
     440  					str = xstrndup(lbr + 1, rbr - lbr - 1);
     441  				free(alg);
     442  			}
     443  			break;
     444  		}
     445  		case COL_MOUNTPOINT:
     446  		{
     447  			char path[PATH_MAX] = { '\0' };
     448  			int fl;
     449  
     450  			check_mount_point(z->devname, &fl, path, sizeof(path));
     451  			if (*path)
     452  				str = xstrdup(path);
     453  			break;
     454  		}
     455  		case COL_STREAMS:
     456  			ul_path_read_string(sysfs, &str, "max_comp_streams");
     457  			break;
     458  		case COL_ZEROPAGES:
     459  			str = get_mm_stat(z, MM_ZERO_PAGES, 1);
     460  			break;
     461  		case COL_ORIG_SIZE:
     462  			str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes);
     463  			break;
     464  		case COL_COMP_SIZE:
     465  			str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes);
     466  			break;
     467  		case COL_MEMTOTAL:
     468  			str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes);
     469  			break;
     470  		case COL_MEMLIMIT:
     471  			str = get_mm_stat(z, MM_MEM_LIMIT, inbytes);
     472  			break;
     473  		case COL_MEMUSED:
     474  			str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes);
     475  			break;
     476  		case COL_MIGRATED:
     477  			str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes);
     478  			break;
     479  		}
     480  		if (str && scols_line_refer_data(ln, i, str))
     481  			err(EXIT_FAILURE, _("failed to add output data"));
     482  	}
     483  }
     484  
     485  static void status(struct zram *z)
     486  {
     487  	struct libscols_table *tb;
     488  	size_t i;
     489  	DIR *dir;
     490  	struct dirent *d;
     491  
     492  	scols_init_debug(0);
     493  
     494  	tb = scols_new_table();
     495  	if (!tb)
     496  		err(EXIT_FAILURE, _("failed to allocate output table"));
     497  
     498  	scols_table_enable_raw(tb, raw);
     499  	scols_table_enable_noheadings(tb, no_headings);
     500  
     501  	for (i = 0; i < (size_t) ncolumns; i++) {
     502  		const struct colinfo *col = get_column_info(i);
     503  
     504  		if (!scols_table_new_column(tb, col->name, col->whint, col->flags))
     505  			err(EXIT_FAILURE, _("failed to initialize output column"));
     506  	}
     507  
     508  	if (z) {
     509  		/* just one device specified */
     510  		fill_table_row(tb, z);
     511  		goto print_table;
     512  	}
     513  
     514  	/* list all used devices */
     515  	z = new_zram(NULL);
     516  	if (!(dir = opendir(_PATH_DEV)))
     517  		err(EXIT_FAILURE, _("cannot open %s"), _PATH_DEV);
     518  
     519  	while ((d = readdir(dir))) {
     520  		int n;
     521  		if (sscanf(d->d_name, "zram%d", &n) != 1)
     522  			continue;
     523  		zram_set_devname(z, NULL, n);
     524  		if (zram_exist(z) && zram_used(z))
     525  			fill_table_row(tb, z);
     526  	}
     527  	closedir(dir);
     528  	free_zram(z);
     529  
     530  print_table:
     531  	scols_print_table(tb);
     532  	scols_unref_table(tb);
     533  }
     534  
     535  static void __attribute__((__noreturn__)) usage(void)
     536  {
     537  	FILE *out = stdout;
     538  	size_t i;
     539  
     540  	fputs(USAGE_HEADER, out);
     541  	fprintf(out, _(	" %1$s [options] <device>\n"
     542  			" %1$s -r <device> [...]\n"
     543  			" %1$s [options] -f | <device> -s <size>\n"),
     544  			program_invocation_short_name);
     545  
     546  	fputs(USAGE_SEPARATOR, out);
     547  	fputs(_("Set up and control zram devices.\n"), out);
     548  
     549  	fputs(USAGE_OPTIONS, out);
     550  	fputs(_(" -a, --algorithm <alg>     compression algorithm to use\n"), out);
     551  	fputs(_(" -b, --bytes               print sizes in bytes rather than in human readable format\n"), out);
     552  	fputs(_(" -f, --find                find a free device\n"), out);
     553  	fputs(_(" -n, --noheadings          don't print headings\n"), out);
     554  	fputs(_(" -o, --output <list>       columns to use for status output\n"), out);
     555  	fputs(_("     --output-all          output all columns\n"), out);
     556  	fputs(_("     --raw                 use raw status output format\n"), out);
     557  	fputs(_(" -r, --reset               reset all specified devices\n"), out);
     558  	fputs(_(" -s, --size <size>         device size\n"), out);
     559  	fputs(_(" -t, --streams <number>    number of compression streams\n"), out);
     560  
     561  	fputs(USAGE_SEPARATOR, out);
     562  	printf(USAGE_HELP_OPTIONS(27));
     563  
     564  	fputs(USAGE_ARGUMENTS, out);
     565  	printf(USAGE_ARG_SIZE(_("<size>")));
     566  
     567  	fputs(_(" <alg> specify algorithm, supported are:\n"), out);
     568  	fputs(_("   lzo, lz4, lz4hc, deflate, 842 and zstd\n"), out);
     569  
     570  	fputs(USAGE_COLUMNS, out);
     571  	for (i = 0; i < ARRAY_SIZE(infos); i++)
     572  		fprintf(out, " %11s  %s\n", infos[i].name, _(infos[i].help));
     573  
     574  	printf(USAGE_MAN_TAIL("zramctl(8)"));
     575  	exit(EXIT_SUCCESS);
     576  }
     577  
     578  /* actions */
     579  enum {
     580  	A_NONE = 0,
     581  	A_STATUS,
     582  	A_CREATE,
     583  	A_FINDONLY,
     584  	A_RESET
     585  };
     586  
     587  int main(int argc, char **argv)
     588  {
     589  	uintmax_t size = 0, nstreams = 0;
     590  	char *algorithm = NULL;
     591  	int rc = 0, c, find = 0, act = A_NONE;
     592  	struct zram *zram = NULL;
     593  
     594  	enum {
     595  		OPT_RAW = CHAR_MAX + 1,
     596  		OPT_LIST_TYPES
     597  	};
     598  
     599  	static const struct option longopts[] = {
     600  		{ "algorithm", required_argument, NULL, 'a' },
     601  		{ "bytes",     no_argument, NULL, 'b' },
     602  		{ "find",      no_argument, NULL, 'f' },
     603  		{ "help",      no_argument, NULL, 'h' },
     604  		{ "output",    required_argument, NULL, 'o' },
     605  		{ "output-all",no_argument, NULL, OPT_LIST_TYPES },
     606  		{ "noheadings",no_argument, NULL, 'n' },
     607  		{ "reset",     no_argument, NULL, 'r' },
     608  		{ "raw",       no_argument, NULL, OPT_RAW },
     609  		{ "size",      required_argument, NULL, 's' },
     610  		{ "streams",   required_argument, NULL, 't' },
     611  		{ "version",   no_argument, NULL, 'V' },
     612  		{ NULL, 0, NULL, 0 }
     613  	};
     614  
     615  	static const ul_excl_t excl[] = {
     616  		{ 'f', 'o', 'r' },
     617  		{ 'o', 'r', 's' },
     618  		{ 0 }
     619  	};
     620  	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
     621  
     622  	setlocale(LC_ALL, "");
     623  	bindtextdomain(PACKAGE, LOCALEDIR);
     624  	textdomain(PACKAGE);
     625  	close_stdout_atexit();
     626  
     627  	while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) {
     628  
     629  		err_exclusive_options(c, longopts, excl, excl_st);
     630  
     631  		switch (c) {
     632  		case 'a':
     633  			algorithm = optarg;
     634  			break;
     635  		case 'b':
     636  			inbytes = 1;
     637  			break;
     638  		case 'f':
     639  			find = 1;
     640  			break;
     641  		case 'o':
     642  			ncolumns = string_to_idarray(optarg,
     643  						     columns, ARRAY_SIZE(columns),
     644  						     column_name_to_id);
     645  			if (ncolumns < 0)
     646  				return EXIT_FAILURE;
     647  			break;
     648  		case OPT_LIST_TYPES:
     649  			for (ncolumns = 0; (size_t)ncolumns < ARRAY_SIZE(infos); ncolumns++)
     650  				columns[ncolumns] = ncolumns;
     651  			break;
     652  		case 's':
     653  			size = strtosize_or_err(optarg, _("failed to parse size"));
     654  			act = A_CREATE;
     655  			break;
     656  		case 't':
     657  			nstreams = strtou64_or_err(optarg, _("failed to parse streams"));
     658  			break;
     659  		case 'r':
     660  			act = A_RESET;
     661  			break;
     662  		case OPT_RAW:
     663  			raw = 1;
     664  			break;
     665  		case 'n':
     666  			no_headings = 1;
     667  			break;
     668  
     669  		case 'V':
     670  			print_version(EXIT_SUCCESS);
     671  		case 'h':
     672  			usage();
     673  		default:
     674  			errtryhelp(EXIT_FAILURE);
     675  		}
     676  	}
     677  
     678  	if (find && optind < argc)
     679  		errx(EXIT_FAILURE, _("option --find is mutually exclusive "
     680  				     "with <device>"));
     681  	if (act == A_NONE)
     682  		act = find ? A_FINDONLY : A_STATUS;
     683  
     684  	if (act != A_RESET && optind + 1 < argc)
     685  		errx(EXIT_FAILURE, _("only one <device> at a time is allowed"));
     686  
     687  	if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams))
     688  		errx(EXIT_FAILURE, _("options --algorithm and --streams "
     689  				     "must be combined with --size"));
     690  
     691  	ul_path_init_debug();
     692  	ul_sysfs_init_debug();
     693  
     694  	switch (act) {
     695  	case A_STATUS:
     696  		if (!ncolumns) {		/* default columns */
     697  			columns[ncolumns++] = COL_NAME;
     698  			columns[ncolumns++] = COL_ALGORITHM;
     699  			columns[ncolumns++] = COL_DISKSIZE;
     700  			columns[ncolumns++] = COL_ORIG_SIZE;
     701  			columns[ncolumns++] = COL_COMP_SIZE;
     702  			columns[ncolumns++] = COL_MEMTOTAL;
     703  			columns[ncolumns++] = COL_STREAMS;
     704  			columns[ncolumns++] = COL_MOUNTPOINT;
     705  		}
     706  		if (optind < argc) {
     707  			zram = new_zram(argv[optind++]);
     708  			if (!zram_exist(zram))
     709  				err(EXIT_FAILURE, "%s", zram->devname);
     710  		}
     711  		status(zram);
     712  		free_zram(zram);
     713  		break;
     714  	case A_RESET:
     715  		if (optind == argc)
     716  			errx(EXIT_FAILURE, _("no device specified"));
     717  		while (optind < argc) {
     718  			zram = new_zram(argv[optind]);
     719  			if (!zram_exist(zram)
     720  			    || zram_set_u64parm(zram, "reset", 1)) {
     721  				warn(_("%s: failed to reset"), zram->devname);
     722  				rc = 1;
     723  			}
     724  			zram_control_remove(zram);
     725  			free_zram(zram);
     726  			optind++;
     727  		}
     728  		break;
     729  	case A_FINDONLY:
     730  		zram = find_free_zram();
     731  		if (!zram)
     732  			errx(EXIT_FAILURE, _("no free zram device found"));
     733  		printf("%s\n", zram->devname);
     734  		free_zram(zram);
     735  		break;
     736  	case A_CREATE:
     737  		if (find) {
     738  			zram = find_free_zram();
     739  			if (!zram)
     740  				errx(EXIT_FAILURE, _("no free zram device found"));
     741  		} else if (optind == argc)
     742  			errx(EXIT_FAILURE, _("no device specified"));
     743  		else {
     744  			zram = new_zram(argv[optind]);
     745  			if (!zram_exist(zram))
     746  				err(EXIT_FAILURE, "%s", zram->devname);
     747  		}
     748  
     749  		if (zram_set_u64parm(zram, "reset", 1))
     750  			err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname);
     751  
     752  		if (nstreams &&
     753  		    zram_set_u64parm(zram, "max_comp_streams", nstreams))
     754  			err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname);
     755  
     756  		if (algorithm &&
     757  		    zram_set_strparm(zram, "comp_algorithm", algorithm))
     758  			err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname);
     759  
     760  		if (zram_set_u64parm(zram, "disksize", size))
     761  			err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"),
     762  				zram->devname, size);
     763  		if (find)
     764  			printf("%s\n", zram->devname);
     765  		free_zram(zram);
     766  		break;
     767  	}
     768  
     769  	ul_unref_path(__control);
     770  	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
     771  }