05ad79
From c43588bf03cc05b2eae724751b6652949e5c9caa Mon Sep 17 00:00:00 2001
05ad79
From: Karel Zak <kzak@redhat.com>
05ad79
Date: Tue, 21 Mar 2017 13:04:17 +0100
05ad79
Subject: [PATCH 106/116] zramctl: backport from v2.29
05ad79
05ad79
Addresses: https://bugzilla.redhat.com/show_bug.cgi?id=1358755
05ad79
Signed-off-by: Karel Zak <kzak@redhat.com>
05ad79
---
05ad79
 .gitignore              |   1 +
05ad79
 configure.ac            |   6 +
05ad79
 include/Makemodule.am   |   1 +
05ad79
 include/c.h             |   8 +
05ad79
 include/strutils.h      |   9 +
05ad79
 include/strv.h          |  55 ++++
05ad79
 include/sysfs.h         |   3 +
05ad79
 lib/Makemodule.am       |   3 +-
05ad79
 lib/strutils.c          | 129 +++++++++
05ad79
 lib/strv.c              | 403 ++++++++++++++++++++++++++
05ad79
 lib/sysfs.c             |  45 ++-
05ad79
 sys-utils/Makemodule.am |   7 +
05ad79
 sys-utils/zramctl.8     | 123 ++++++++
05ad79
 sys-utils/zramctl.c     | 736 ++++++++++++++++++++++++++++++++++++++++++++++++
05ad79
 14 files changed, 1524 insertions(+), 5 deletions(-)
05ad79
 create mode 100644 include/strv.h
05ad79
 create mode 100644 lib/strv.c
05ad79
 create mode 100644 sys-utils/zramctl.8
05ad79
 create mode 100644 sys-utils/zramctl.c
05ad79
05ad79
diff --git a/configure.ac b/configure.ac
05ad79
index f87a885..db7095a 100644
05ad79
--- a/configure.ac
05ad79
+++ b/configure.ac
05ad79
@@ -826,6 +826,12 @@ UL_REQUIRES_LINUX([losetup])
05ad79
 AM_CONDITIONAL(BUILD_LOSETUP, test "x$build_losetup" = xyes)
05ad79
 
05ad79
 
05ad79
+UL_BUILD_INIT([zramctl], [check])
05ad79
+UL_REQUIRES_LINUX([zramctl])
05ad79
+UL_REQUIRES_BUILD([zramctl], [libsmartcols])
05ad79
+AM_CONDITIONAL([BUILD_ZRAMCTL], [test "x$build_zramctl" = xyes])
05ad79
+
05ad79
+
05ad79
 AC_ARG_ENABLE([cytune],
05ad79
   AS_HELP_STRING([--disable-cytune], [do not build cytune]),
05ad79
   [], enable_cytune=check
05ad79
diff --git a/include/Makemodule.am b/include/Makemodule.am
05ad79
index 757f317..1680296 100644
05ad79
--- a/include/Makemodule.am
05ad79
+++ b/include/Makemodule.am
05ad79
@@ -39,6 +39,7 @@ dist_noinst_HEADERS += \
05ad79
 	include/readutmp.h \
05ad79
 	include/setproctitle.h \
05ad79
 	include/strutils.h \
05ad79
+	include/strv.h \
05ad79
 	include/swapheader.h \
05ad79
 	include/sysfs.h \
05ad79
 	include/timer.h \
05ad79
diff --git a/include/c.h b/include/c.h
05ad79
index a2779a5..3754e75 100644
05ad79
--- a/include/c.h
05ad79
+++ b/include/c.h
05ad79
@@ -309,6 +309,14 @@ static inline int usleep(useconds_t usec)
05ad79
 #endif
05ad79
 
05ad79
 /*
05ad79
+ * Macros to convert #define'itions to strings, for example
05ad79
+ * #define XYXXY 42
05ad79
+ * printf ("%s=%s\n", stringify(XYXXY), stringify_value(XYXXY));
05ad79
+ */
05ad79
+#define stringify_value(s) stringify(s)
05ad79
+#define stringify(s) #s
05ad79
+
05ad79
+/*
05ad79
  * Note that sysconf(_SC_GETPW_R_SIZE_MAX) returns *initial* suggested size for
05ad79
  * pwd buffer and in some cases it is not large enough. See POSIX and
05ad79
  * getpwnam_r man page for more details.
05ad79
diff --git a/include/strutils.h b/include/strutils.h
05ad79
index 709fcad..aa7b95f 100644
05ad79
--- a/include/strutils.h
05ad79
+++ b/include/strutils.h
05ad79
@@ -5,6 +5,7 @@
05ad79
 #include <inttypes.h>
05ad79
 #include <string.h>
05ad79
 #include <sys/types.h>
05ad79
+#include <stdio.h>
05ad79
 
05ad79
 /* default strtoxx_or_err() exit code */
05ad79
 #ifndef STRTOXX_EXIT_CODE
05ad79
@@ -102,4 +103,12 @@ extern int parse_range(const char *str, int *lower, int *upper, int def);
05ad79
 
05ad79
 extern int streq_except_trailing_slash(const char *s1, const char *s2);
05ad79
 
05ad79
+extern char *strnappend(const char *s, const char *suffix, size_t b);
05ad79
+extern char *strappend(const char *s, const char *suffix);
05ad79
+extern char *strfappend(const char *s, const char *format, ...)
05ad79
+		 __attribute__ ((__format__ (__printf__, 2, 0)));
05ad79
+extern const char *split(const char **state, size_t *l, const char *separator, int quoted);
05ad79
+
05ad79
+extern int skip_fline(FILE *fp);
05ad79
+
05ad79
 #endif
05ad79
diff --git a/include/strv.h b/include/strv.h
05ad79
new file mode 100644
05ad79
index 0000000..260ad12
05ad79
--- /dev/null
05ad79
+++ b/include/strv.h
05ad79
@@ -0,0 +1,55 @@
05ad79
+#ifndef UTIL_LINUX_STRV
05ad79
+#define UTIL_LINUX_STRV
05ad79
+
05ad79
+#include <stdarg.h>
05ad79
+
05ad79
+#include "c.h"
05ad79
+
05ad79
+char **strv_free(char **l);
05ad79
+void strv_clear(char **l);
05ad79
+char **strv_copy(char * const *l);
05ad79
+unsigned strv_length(char * const *l);
05ad79
+
05ad79
+int strv_extend_strv(char ***a, char **b);
05ad79
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix);
05ad79
+int strv_extend(char ***l, const char *value);
05ad79
+int strv_extendv(char ***l, const char *format, va_list ap);
05ad79
+int strv_extendf(char ***l, const char *format, ...)
05ad79
+			__attribute__ ((__format__ (__printf__, 2, 0)));
05ad79
+int strv_push(char ***l, char *value);
05ad79
+int strv_push_prepend(char ***l, char *value);
05ad79
+int strv_consume(char ***l, char *value);
05ad79
+int strv_consume_prepend(char ***l, char *value);
05ad79
+
05ad79
+char **strv_remove(char **l, const char *s);
05ad79
+
05ad79
+char **strv_new(const char *x, ...);
05ad79
+char **strv_new_ap(const char *x, va_list ap);
05ad79
+
05ad79
+static inline const char* STRV_IFNOTNULL(const char *x) {
05ad79
+        return x ? x : (const char *) -1;
05ad79
+}
05ad79
+
05ad79
+static inline int strv_isempty(char * const *l) {
05ad79
+        return !l || !*l;
05ad79
+}
05ad79
+
05ad79
+char **strv_split(const char *s, const char *separator);
05ad79
+char *strv_join(char **l, const char *separator);
05ad79
+
05ad79
+#define STRV_FOREACH(s, l)                      \
05ad79
+        for ((s) = (l); (s) && *(s); (s)++)
05ad79
+
05ad79
+#define STRV_FOREACH_BACKWARDS(s, l)            \
05ad79
+        STRV_FOREACH(s, l)                      \
05ad79
+                ;                               \
05ad79
+        for ((s)--; (l) && ((s) >= (l)); (s)--)
05ad79
+
05ad79
+
05ad79
+#define STRV_MAKE_EMPTY ((char*[1]) { NULL })
05ad79
+
05ad79
+char **strv_reverse(char **l);
05ad79
+
05ad79
+#endif /* UTIL_LINUX_STRV */
05ad79
+
05ad79
+
05ad79
diff --git a/include/sysfs.h b/include/sysfs.h
05ad79
index 0a9c218..a547005 100644
05ad79
--- a/include/sysfs.h
05ad79
+++ b/include/sysfs.h
05ad79
@@ -58,6 +58,9 @@ extern int sysfs_read_s64(struct sysfs_cxt *cxt, const char *attr, int64_t *res)
05ad79
 extern int sysfs_read_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t *res);
05ad79
 extern int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res);
05ad79
 
05ad79
+extern int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str);
05ad79
+extern int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num);
05ad79
+
05ad79
 extern char *sysfs_get_devname(struct sysfs_cxt *cxt, char *buf, size_t bufsiz);
05ad79
 
05ad79
 extern char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr);
05ad79
diff --git a/lib/Makemodule.am b/lib/Makemodule.am
05ad79
index 73280f9..faf9d74 100644
05ad79
--- a/lib/Makemodule.am
05ad79
+++ b/lib/Makemodule.am
05ad79
@@ -27,7 +27,8 @@ libcommon_la_SOURCES = \
05ad79
 	lib/ttyutils.c \
05ad79
 	lib/xgetpass.c \
05ad79
 	lib/exec_shell.c \
05ad79
-	lib/readutmp.c
05ad79
+	lib/readutmp.c \
05ad79
+	lib/strv.c
05ad79
 
05ad79
 if LINUX
05ad79
 libcommon_la_SOURCES += \
05ad79
diff --git a/lib/strutils.c b/lib/strutils.c
05ad79
index f9cdcbb..4b8a813 100644
05ad79
--- a/lib/strutils.c
05ad79
+++ b/lib/strutils.c
05ad79
@@ -10,6 +10,7 @@
05ad79
 #include <errno.h>
05ad79
 #include <sys/stat.h>
05ad79
 #include <string.h>
05ad79
+#include <assert.h>
05ad79
 
05ad79
 #include "c.h"
05ad79
 #include "nls.h"
05ad79
@@ -687,6 +688,134 @@ int streq_except_trailing_slash(const char *s1, const char *s2)
05ad79
 	return equal;
05ad79
 }
05ad79
 
05ad79
+char *strnappend(const char *s, const char *suffix, size_t b)
05ad79
+{
05ad79
+        size_t a;
05ad79
+        char *r;
05ad79
+
05ad79
+        if (!s && !suffix)
05ad79
+                return strdup("");
05ad79
+        if (!s)
05ad79
+                return strndup(suffix, b);
05ad79
+        if (!suffix)
05ad79
+                return strdup(s);
05ad79
+
05ad79
+        assert(s);
05ad79
+        assert(suffix);
05ad79
+
05ad79
+        a = strlen(s);
05ad79
+        if (b > ((size_t) -1) - a)
05ad79
+                return NULL;
05ad79
+
05ad79
+        r = malloc(a + b + 1);
05ad79
+        if (!r)
05ad79
+                return NULL;
05ad79
+
05ad79
+        memcpy(r, s, a);
05ad79
+        memcpy(r + a, suffix, b);
05ad79
+        r[a+b] = 0;
05ad79
+
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+char *strappend(const char *s, const char *suffix)
05ad79
+{
05ad79
+        return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
05ad79
+}
05ad79
+
05ad79
+char *strfappend(const char *s, const char *format, ...)
05ad79
+{
05ad79
+	va_list ap;
05ad79
+	char *val, *res;
05ad79
+	int sz;
05ad79
+
05ad79
+	va_start(ap, format);
05ad79
+	sz = vasprintf(&val, format, ap);
05ad79
+	va_end(ap);
05ad79
+
05ad79
+	if (sz < 0)
05ad79
+		return NULL;
05ad79
+
05ad79
+	res = strnappend(s, val, sz);
05ad79
+	free(val);
05ad79
+	return res;
05ad79
+}
05ad79
+
05ad79
+static size_t strcspn_escaped(const char *s, const char *reject)
05ad79
+{
05ad79
+        int escaped = 0;
05ad79
+        int n;
05ad79
+
05ad79
+        for (n=0; s[n]; n++) {
05ad79
+                if (escaped)
05ad79
+                        escaped = 0;
05ad79
+                else if (s[n] == '\\')
05ad79
+                        escaped = 1;
05ad79
+                else if (strchr(reject, s[n]))
05ad79
+                        break;
05ad79
+        }
05ad79
+
05ad79
+        /* if s ends in \, return index of previous char */
05ad79
+        return n - escaped;
05ad79
+}
05ad79
+
05ad79
+/* Split a string into words. */
05ad79
+const char *split(const char **state, size_t *l, const char *separator, int quoted)
05ad79
+{
05ad79
+        const char *current;
05ad79
+
05ad79
+        current = *state;
05ad79
+
05ad79
+        if (!*current) {
05ad79
+                assert(**state == '\0');
05ad79
+                return NULL;
05ad79
+        }
05ad79
+
05ad79
+        current += strspn(current, separator);
05ad79
+        if (!*current) {
05ad79
+                *state = current;
05ad79
+                return NULL;
05ad79
+        }
05ad79
+
05ad79
+        if (quoted && strchr("\'\"", *current)) {
05ad79
+                char quotechars[2] = {*current, '\0'};
05ad79
+
05ad79
+                *l = strcspn_escaped(current + 1, quotechars);
05ad79
+                if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
05ad79
+                    (current[*l + 2] && !strchr(separator, current[*l + 2]))) {
05ad79
+                        /* right quote missing or garbage at the end */
05ad79
+                        *state = current;
05ad79
+                        return NULL;
05ad79
+                }
05ad79
+                *state = current++ + *l + 2;
05ad79
+        } else if (quoted) {
05ad79
+                *l = strcspn_escaped(current, separator);
05ad79
+                if (current[*l] && !strchr(separator, current[*l])) {
05ad79
+                        /* unfinished escape */
05ad79
+                        *state = current;
05ad79
+                        return NULL;
05ad79
+                }
05ad79
+                *state = current + *l;
05ad79
+        } else {
05ad79
+                *l = strcspn(current, separator);
05ad79
+                *state = current + *l;
05ad79
+        }
05ad79
+
05ad79
+        return current;
05ad79
+}
05ad79
+
05ad79
+/* Rewind file pointer forward to new line.  */
05ad79
+int skip_fline(FILE *fp)
05ad79
+{
05ad79
+	int ch;
05ad79
+
05ad79
+	do {
05ad79
+		if ((ch = fgetc(fp)) == EOF)
05ad79
+			return 1;
05ad79
+		if (ch == '\n')
05ad79
+			return 0;
05ad79
+	} while (1);
05ad79
+}
05ad79
 
05ad79
 #ifdef TEST_PROGRAM
05ad79
 
05ad79
diff --git a/lib/strv.c b/lib/strv.c
05ad79
new file mode 100644
05ad79
index 0000000..ddc2a0c
05ad79
--- /dev/null
05ad79
+++ b/lib/strv.c
05ad79
@@ -0,0 +1,403 @@
05ad79
+/*
05ad79
+ *
05ad79
+ * Copyright 2010 Lennart Poettering
05ad79
+ *
05ad79
+ * This is free software; you can redistribute it and/or modify it
05ad79
+ * under the terms of the GNU Lesser General Public License as published by
05ad79
+ * the Free Software Foundation; either version 2.1 of the License, or
05ad79
+ * (at your option) any later version.
05ad79
+ *
05ad79
+ *
05ad79
+ * Copyright (C) 2015 Karel Zak <kzak@redhat.com>
05ad79
+ *    Modified the original version from systemd project for util-linux.
05ad79
+ */
05ad79
+
05ad79
+#include <stdlib.h>
05ad79
+#include <stdarg.h>
05ad79
+#include <string.h>
05ad79
+#include <errno.h>
05ad79
+#include <stdbool.h>
05ad79
+#include <assert.h>
05ad79
+
05ad79
+#include "strutils.h"
05ad79
+#include "strv.h"
05ad79
+
05ad79
+void strv_clear(char **l) {
05ad79
+        char **k;
05ad79
+
05ad79
+        if (!l)
05ad79
+                return;
05ad79
+
05ad79
+        for (k = l; *k; k++)
05ad79
+                free(*k);
05ad79
+
05ad79
+        *l = NULL;
05ad79
+}
05ad79
+
05ad79
+char **strv_free(char **l) {
05ad79
+        strv_clear(l);
05ad79
+        free(l);
05ad79
+        return NULL;
05ad79
+}
05ad79
+
05ad79
+char **strv_copy(char * const *l) {
05ad79
+        char **r, **k;
05ad79
+
05ad79
+        k = r = malloc(sizeof(char *) * (strv_length(l) + 1));
05ad79
+        if (!r)
05ad79
+                return NULL;
05ad79
+
05ad79
+        if (l)
05ad79
+                for (; *l; k++, l++) {
05ad79
+                        *k = strdup(*l);
05ad79
+                        if (!*k) {
05ad79
+                                strv_free(r);
05ad79
+                                return NULL;
05ad79
+                        }
05ad79
+                }
05ad79
+
05ad79
+        *k = NULL;
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+unsigned strv_length(char * const *l) {
05ad79
+        unsigned n = 0;
05ad79
+
05ad79
+        if (!l)
05ad79
+                return 0;
05ad79
+
05ad79
+        for (; *l; l++)
05ad79
+                n++;
05ad79
+
05ad79
+        return n;
05ad79
+}
05ad79
+
05ad79
+char **strv_new_ap(const char *x, va_list ap) {
05ad79
+        const char *s;
05ad79
+        char **a;
05ad79
+        unsigned n = 0, i = 0;
05ad79
+        va_list aq;
05ad79
+
05ad79
+        /* As a special trick we ignore all listed strings that equal
05ad79
+         * (const char*) -1. This is supposed to be used with the
05ad79
+         * STRV_IFNOTNULL() macro to include possibly NULL strings in
05ad79
+         * the string list. */
05ad79
+
05ad79
+        if (x) {
05ad79
+                n = x == (const char*) -1 ? 0 : 1;
05ad79
+
05ad79
+                va_copy(aq, ap);
05ad79
+                while ((s = va_arg(aq, const char*))) {
05ad79
+                        if (s == (const char*) -1)
05ad79
+                                continue;
05ad79
+
05ad79
+                        n++;
05ad79
+                }
05ad79
+
05ad79
+                va_end(aq);
05ad79
+        }
05ad79
+
05ad79
+        a = malloc(sizeof(char *) * (n + 1));
05ad79
+        if (!a)
05ad79
+                return NULL;
05ad79
+
05ad79
+        if (x) {
05ad79
+                if (x != (const char*) -1) {
05ad79
+                        a[i] = strdup(x);
05ad79
+                        if (!a[i])
05ad79
+                                goto fail;
05ad79
+                        i++;
05ad79
+                }
05ad79
+
05ad79
+                while ((s = va_arg(ap, const char*))) {
05ad79
+
05ad79
+                        if (s == (const char*) -1)
05ad79
+                                continue;
05ad79
+
05ad79
+                        a[i] = strdup(s);
05ad79
+                        if (!a[i])
05ad79
+                                goto fail;
05ad79
+
05ad79
+                        i++;
05ad79
+                }
05ad79
+        }
05ad79
+
05ad79
+        a[i] = NULL;
05ad79
+
05ad79
+        return a;
05ad79
+
05ad79
+fail:
05ad79
+        strv_free(a);
05ad79
+        return NULL;
05ad79
+}
05ad79
+
05ad79
+char **strv_new(const char *x, ...) {
05ad79
+        char **r;
05ad79
+        va_list ap;
05ad79
+
05ad79
+        va_start(ap, x);
05ad79
+        r = strv_new_ap(x, ap);
05ad79
+        va_end(ap);
05ad79
+
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+int strv_extend_strv(char ***a, char **b) {
05ad79
+        int r;
05ad79
+        char **s;
05ad79
+
05ad79
+        STRV_FOREACH(s, b) {
05ad79
+                r = strv_extend(a, *s);
05ad79
+                if (r < 0)
05ad79
+                        return r;
05ad79
+        }
05ad79
+
05ad79
+        return 0;
05ad79
+}
05ad79
+
05ad79
+int strv_extend_strv_concat(char ***a, char **b, const char *suffix) {
05ad79
+        int r;
05ad79
+        char **s;
05ad79
+
05ad79
+        STRV_FOREACH(s, b) {
05ad79
+                char *v;
05ad79
+
05ad79
+                v = strappend(*s, suffix);
05ad79
+                if (!v)
05ad79
+                        return -ENOMEM;
05ad79
+
05ad79
+                r = strv_push(a, v);
05ad79
+                if (r < 0) {
05ad79
+                        free(v);
05ad79
+                        return r;
05ad79
+                }
05ad79
+        }
05ad79
+
05ad79
+        return 0;
05ad79
+}
05ad79
+
05ad79
+
05ad79
+#define _FOREACH_WORD(word, length, s, separator, quoted, state)        \
05ad79
+        for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted)))
05ad79
+
05ad79
+#define FOREACH_WORD_SEPARATOR(word, length, s, separator, state)       \
05ad79
+        _FOREACH_WORD(word, length, s, separator, false, state)
05ad79
+
05ad79
+
05ad79
+char **strv_split(const char *s, const char *separator) {
05ad79
+        const char *word, *state;
05ad79
+        size_t l;
05ad79
+        unsigned n, i;
05ad79
+        char **r;
05ad79
+
05ad79
+        assert(s);
05ad79
+
05ad79
+        n = 0;
05ad79
+        FOREACH_WORD_SEPARATOR(word, l, s, separator, state)
05ad79
+                n++;
05ad79
+
05ad79
+        r = malloc(sizeof(char *) * (n + 1));
05ad79
+        if (!r)
05ad79
+                return NULL;
05ad79
+
05ad79
+        i = 0;
05ad79
+        FOREACH_WORD_SEPARATOR(word, l, s, separator, state) {
05ad79
+                r[i] = strndup(word, l);
05ad79
+                if (!r[i]) {
05ad79
+                        strv_free(r);
05ad79
+                        return NULL;
05ad79
+                }
05ad79
+
05ad79
+                i++;
05ad79
+        }
05ad79
+
05ad79
+        r[i] = NULL;
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+char *strv_join(char **l, const char *separator) {
05ad79
+        char *r, *e;
05ad79
+        char **s;
05ad79
+        size_t n, k;
05ad79
+
05ad79
+        if (!separator)
05ad79
+                separator = " ";
05ad79
+
05ad79
+        k = strlen(separator);
05ad79
+
05ad79
+        n = 0;
05ad79
+        STRV_FOREACH(s, l) {
05ad79
+                if (n != 0)
05ad79
+                        n += k;
05ad79
+                n += strlen(*s);
05ad79
+        }
05ad79
+
05ad79
+        r = malloc(n + 1);
05ad79
+        if (!r)
05ad79
+                return NULL;
05ad79
+
05ad79
+        e = r;
05ad79
+        STRV_FOREACH(s, l) {
05ad79
+                if (e != r)
05ad79
+                        e = stpcpy(e, separator);
05ad79
+
05ad79
+                e = stpcpy(e, *s);
05ad79
+        }
05ad79
+
05ad79
+        *e = 0;
05ad79
+
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+int strv_push(char ***l, char *value) {
05ad79
+        char **c;
05ad79
+        unsigned n, m;
05ad79
+
05ad79
+        if (!value)
05ad79
+                return 0;
05ad79
+
05ad79
+        n = strv_length(*l);
05ad79
+
05ad79
+        /* Increase and check for overflow */
05ad79
+        m = n + 2;
05ad79
+        if (m < n)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        c = realloc(*l, sizeof(char *) * m);
05ad79
+        if (!c)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        c[n] = value;
05ad79
+        c[n+1] = NULL;
05ad79
+
05ad79
+        *l = c;
05ad79
+        return 0;
05ad79
+}
05ad79
+
05ad79
+int strv_push_prepend(char ***l, char *value) {
05ad79
+        char **c;
05ad79
+        unsigned n, m, i;
05ad79
+
05ad79
+        if (!value)
05ad79
+                return 0;
05ad79
+
05ad79
+        n = strv_length(*l);
05ad79
+
05ad79
+        /* increase and check for overflow */
05ad79
+        m = n + 2;
05ad79
+        if (m < n)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        c = malloc(sizeof(char *) * m);
05ad79
+        if (!c)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        for (i = 0; i < n; i++)
05ad79
+                c[i+1] = (*l)[i];
05ad79
+
05ad79
+        c[0] = value;
05ad79
+        c[n+1] = NULL;
05ad79
+
05ad79
+        free(*l);
05ad79
+        *l = c;
05ad79
+
05ad79
+        return 0;
05ad79
+}
05ad79
+
05ad79
+int strv_consume(char ***l, char *value) {
05ad79
+        int r;
05ad79
+
05ad79
+        r = strv_push(l, value);
05ad79
+        if (r < 0)
05ad79
+                free(value);
05ad79
+
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+int strv_consume_prepend(char ***l, char *value) {
05ad79
+        int r;
05ad79
+
05ad79
+        r = strv_push_prepend(l, value);
05ad79
+        if (r < 0)
05ad79
+                free(value);
05ad79
+
05ad79
+        return r;
05ad79
+}
05ad79
+
05ad79
+int strv_extend(char ***l, const char *value) {
05ad79
+        char *v;
05ad79
+
05ad79
+        if (!value)
05ad79
+                return 0;
05ad79
+
05ad79
+        v = strdup(value);
05ad79
+        if (!v)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        return strv_consume(l, v);
05ad79
+}
05ad79
+
05ad79
+char **strv_remove(char **l, const char *s) {
05ad79
+        char **f, **t;
05ad79
+
05ad79
+        if (!l)
05ad79
+                return NULL;
05ad79
+
05ad79
+        assert(s);
05ad79
+
05ad79
+        /* Drops every occurrence of s in the string list, edits
05ad79
+         * in-place. */
05ad79
+
05ad79
+        for (f = t = l; *f; f++)
05ad79
+                if (strcmp(*f, s) == 0)
05ad79
+                        free(*f);
05ad79
+                else
05ad79
+                        *(t++) = *f;
05ad79
+
05ad79
+        *t = NULL;
05ad79
+        return l;
05ad79
+}
05ad79
+
05ad79
+int strv_extendf(char ***l, const char *format, ...) {
05ad79
+        va_list ap;
05ad79
+        char *x;
05ad79
+        int r;
05ad79
+
05ad79
+        va_start(ap, format);
05ad79
+        r = vasprintf(&x, format, ap);
05ad79
+        va_end(ap);
05ad79
+
05ad79
+        if (r < 0)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        return strv_consume(l, x);
05ad79
+}
05ad79
+
05ad79
+int strv_extendv(char ***l, const char *format, va_list ap) {
05ad79
+        char *x;
05ad79
+        int r;
05ad79
+
05ad79
+        r = vasprintf(&x, format, ap);
05ad79
+        if (r < 0)
05ad79
+                return -ENOMEM;
05ad79
+
05ad79
+        return strv_consume(l, x);
05ad79
+}
05ad79
+
05ad79
+char **strv_reverse(char **l) {
05ad79
+        unsigned n, i;
05ad79
+
05ad79
+        n = strv_length(l);
05ad79
+        if (n <= 1)
05ad79
+                return l;
05ad79
+
05ad79
+        for (i = 0; i < n / 2; i++) {
05ad79
+                char *t;
05ad79
+
05ad79
+                t = l[i];
05ad79
+                l[i] = l[n-1-i];
05ad79
+                l[n-1-i] = t;
05ad79
+        }
05ad79
+
05ad79
+        return l;
05ad79
+}
05ad79
diff --git a/lib/sysfs.c b/lib/sysfs.c
05ad79
index 0bfd622..65a8394 100644
05ad79
--- a/lib/sysfs.c
05ad79
+++ b/lib/sysfs.c
05ad79
@@ -10,6 +10,7 @@
05ad79
 #include "at.h"
05ad79
 #include "pathnames.h"
05ad79
 #include "sysfs.h"
05ad79
+#include "all-io.h"
05ad79
 
05ad79
 char *sysfs_devno_attribute_path(dev_t devno, char *buf,
05ad79
 				 size_t bufsiz, const char *attr)
05ad79
@@ -203,9 +204,9 @@ int sysfs_has_attribute(struct sysfs_cxt *cxt, const char *attr)
05ad79
 	return sysfs_stat(cxt, attr, &st) == 0;
05ad79
 }
05ad79
 
05ad79
-static int sysfs_open(struct sysfs_cxt *cxt, const char *attr)
05ad79
+static int sysfs_open(struct sysfs_cxt *cxt, const char *attr, int flags)
05ad79
 {
05ad79
-	int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, O_RDONLY|O_CLOEXEC);
05ad79
+	int fd = open_at(cxt->dir_fd, cxt->dir_path, attr, flags);
05ad79
 
05ad79
 	if (fd == -1 && errno == ENOENT &&
05ad79
 	    strncmp(attr, "queue/", 6) == 0 && cxt->parent) {
05ad79
@@ -238,7 +239,7 @@ DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
05ad79
 	int fd = -1;
05ad79
 
05ad79
 	if (attr)
05ad79
-		fd = sysfs_open(cxt, attr);
05ad79
+		fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
05ad79
 
05ad79
 	else if (cxt->dir_fd >= 0)
05ad79
 		/* request to open root of device in sysfs (/sys/block/<dev>)
05ad79
@@ -263,7 +264,7 @@ DIR *sysfs_opendir(struct sysfs_cxt *cxt, const char *attr)
05ad79
 
05ad79
 static FILE *sysfs_fopen(struct sysfs_cxt *cxt, const char *attr)
05ad79
 {
05ad79
-	int fd = sysfs_open(cxt, attr);
05ad79
+	int fd = sysfs_open(cxt, attr, O_RDONLY|O_CLOEXEC);
05ad79
 
05ad79
 	return fd < 0 ? NULL : fdopen(fd, "r" UL_CLOEXECSTR);
05ad79
 }
05ad79
@@ -417,6 +418,42 @@ int sysfs_read_int(struct sysfs_cxt *cxt, const char *attr, int *res)
05ad79
 	return -1;
05ad79
 }
05ad79
 
05ad79
+int sysfs_write_string(struct sysfs_cxt *cxt, const char *attr, const char *str)
05ad79
+{
05ad79
+	int fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
05ad79
+	int rc, errsv;
05ad79
+
05ad79
+	if (fd < 0)
05ad79
+		return -errno;
05ad79
+	rc = write_all(fd, str, strlen(str));
05ad79
+
05ad79
+	errsv = errno;
05ad79
+	close(fd);
05ad79
+	errno = errsv;
05ad79
+	return rc;
05ad79
+}
05ad79
+
05ad79
+int sysfs_write_u64(struct sysfs_cxt *cxt, const char *attr, uint64_t num)
05ad79
+{
05ad79
+	char buf[sizeof(stringify_value(ULLONG_MAX))];
05ad79
+	int fd, rc = 0, len, errsv;
05ad79
+
05ad79
+	fd = sysfs_open(cxt, attr, O_WRONLY|O_CLOEXEC);
05ad79
+	if (fd < 0)
05ad79
+		return -errno;
05ad79
+
05ad79
+	len = snprintf(buf, sizeof(buf), "%" PRIu64, num);
05ad79
+	if (len < 0 || (size_t) len >= sizeof(buf))
05ad79
+		rc = len < 0 ? -errno : -E2BIG;
05ad79
+	else
05ad79
+		rc = write_all(fd, buf, len);
05ad79
+
05ad79
+	errsv = errno;
05ad79
+	close(fd);
05ad79
+	errno = errsv;
05ad79
+	return rc;
05ad79
+}
05ad79
+
05ad79
 char *sysfs_strdup(struct sysfs_cxt *cxt, const char *attr)
05ad79
 {
05ad79
 	char buf[1024];
05ad79
diff --git a/sys-utils/Makemodule.am b/sys-utils/Makemodule.am
05ad79
index 6badd17..408e884 100644
05ad79
--- a/sys-utils/Makemodule.am
05ad79
+++ b/sys-utils/Makemodule.am
05ad79
@@ -184,6 +184,13 @@ losetup_static_LDADD = $(losetup_LDADD)
05ad79
 endif
05ad79
 endif # BUILD_LOSETUP
05ad79
 
05ad79
+if BUILD_ZRAMCTL
05ad79
+sbin_PROGRAMS += zramctl
05ad79
+dist_man_MANS += sys-utils/zramctl.8
05ad79
+zramctl_SOURCES = sys-utils/zramctl.c
05ad79
+zramctl_LDADD = $(LDADD) libcommon.la libsmartcols.la
05ad79
+zramctl_CFLAGS = $(AM_CFLAGS) -I$(ul_libsmartcols_incdir)
05ad79
+endif
05ad79
 
05ad79
 if BUILD_PRLIMIT
05ad79
 usrbin_exec_PROGRAMS += prlimit
05ad79
diff --git a/sys-utils/zramctl.8 b/sys-utils/zramctl.8
05ad79
new file mode 100644
05ad79
index 0000000..f6fc45c
05ad79
--- /dev/null
05ad79
+++ b/sys-utils/zramctl.8
05ad79
@@ -0,0 +1,123 @@
05ad79
+.TH ZRAMCTL 8 "July 2014" "util-linux" "System Administration"
05ad79
+.SH NAME
05ad79
+zramctl \- set up and control zram devices
05ad79
+.SH SYNOPSIS
05ad79
+.ad l
05ad79
+Get info:
05ad79
+.sp
05ad79
+.in +5
05ad79
+.BR zramctl " [options]"
05ad79
+.sp
05ad79
+.in -5
05ad79
+Reset zram:
05ad79
+.sp
05ad79
+.in +5
05ad79
+.B "zramctl \-r"
05ad79
+.IR zramdev ...
05ad79
+.sp
05ad79
+.in -5
05ad79
+Print name of first unused zram device:
05ad79
+.sp
05ad79
+.in +5
05ad79
+.B "zramctl \-f"
05ad79
+.sp
05ad79
+.in -5
05ad79
+Set up a zram device:
05ad79
+.sp
05ad79
+.in +5
05ad79
+.B zramctl
05ad79
+.RB [ \-f " | "\fIzramdev\fP ]
05ad79
+.RB [ \-s
05ad79
+.IR size ]
05ad79
+.RB [ \-t
05ad79
+.IR number ]
05ad79
+.RB [ \-a
05ad79
+.IR algorithm ]
05ad79
+.sp
05ad79
+.in -5
05ad79
+.ad b
05ad79
+.SH DESCRIPTION
05ad79
+.B zramctl
05ad79
+is used to quickly set up zram device parameters, to reset zram devices, and to
05ad79
+query the status of used zram devices.  If no option is given, all zram devices
05ad79
+are shown.
05ad79
+
05ad79
+.SH OPTIONS
05ad79
+.TP
05ad79
+.BR \-a , " \-\-algorithm lzo" | lz4
05ad79
+Set the compression algorithm to be used for compressing data in the zram device.
05ad79
+.TP
05ad79
+.BR \-f , " \-\-find"
05ad79
+Find the first unused zram device.  If a \fB--size\fR argument is present, then
05ad79
+initialize the device.
05ad79
+.TP
05ad79
+.BR \-n , " \-\-noheadings"
05ad79
+Do not print a header line in status output.
05ad79
+.TP
05ad79
+.BR \-o , " \-\-output " \fIlist
05ad79
+Define the status output columns to be used.  If no output arrangement is
05ad79
+specified, then a default set is used.
05ad79
+Use \fB\-\-help\fP to get a list of all supported columns.
05ad79
+.TP
05ad79
+.B \-\-raw
05ad79
+Use the raw format for status output.
05ad79
+.TP
05ad79
+.BR \-r , " \-\-reset"
05ad79
+Reset the options of the specified zram device(s).  Zram device settings
05ad79
+can be changed only after a reset.
05ad79
+.TP
05ad79
+.BR \-s , " \-\-size " \fIsize
05ad79
+Create a zram device of the specified \fIsize\fR.
05ad79
+Zram devices are aligned to memory pages; when the requested \fIsize\fR is
05ad79
+not a multiple of the page size, it will be rounded up to the next multiple.
05ad79
+When not otherwise specified, the unit of the \fIsize\fR parameter is bytes.
05ad79
+.IP
05ad79
+The \fIsize\fR argument may be followed by the multiplicative suffixes KiB (=1024),
05ad79
+MiB (=1024*1024), and so on for GiB, TiB, PiB, EiB, ZiB and YiB (the "iB"
05ad79
+is optional, e.g., "K" has the same meaning as "KiB") or the suffixes
05ad79
+KB (=1000), MB (=1000*1000), and so on for GB, TB, PB, EB, ZB and YB.
05ad79
+.TP
05ad79
+.BR \-t , " \-\-streams " \fInumber
05ad79
+Set the maximum number of compression streams that can be used for the device.
05ad79
+The default is one stream.
05ad79
+.TP
05ad79
+.BR \-V , " \-\-version"
05ad79
+Display version information and exit.
05ad79
+.TP
05ad79
+.BR \-h , " \-\-help"
05ad79
+Display help text and exit.
05ad79
+
05ad79
+.SH RETURN VALUE
05ad79
+.B zramctl
05ad79
+returns 0 on success, nonzero on failure.
05ad79
+
05ad79
+.SH FILES
05ad79
+.TP
05ad79
+.I /dev/zram[0..N]
05ad79
+zram block devices
05ad79
+
05ad79
+.SH EXAMPLE
05ad79
+The following commands set up a zram device with a size of one gigabyte
05ad79
+and use it as swap device.
05ad79
+.nf
05ad79
+.IP
05ad79
+# zramctl --find --size 1024M
05ad79
+/dev/zram0
05ad79
+# mkswap /dev/zram0
05ad79
+# swapon /dev/zram0
05ad79
+ ...
05ad79
+# swapoff /dev/zram0
05ad79
+# zramctl --reset /dev/zram0
05ad79
+.fi
05ad79
+.SH SEE ALSO
05ad79
+.UR http://git.\:kernel.\:org\:/cgit\:/linux\:/kernel\:/git\:/torvalds\:/linux.git\:/tree\:/Documentation\:/blockdev\:/zram.txt
05ad79
+Linux kernel documentation
05ad79
+.UE .
05ad79
+.SH AUTHORS
05ad79
+.nf
05ad79
+Timofey Titovets <nefelim4ag@gmail.com>
05ad79
+Karel Zak <kzak@redhat.com>
05ad79
+.fi
05ad79
+.SH AVAILABILITY
05ad79
+The zramctl command is part of the util-linux package and is available from
05ad79
+https://www.kernel.org/pub/linux/utils/util-linux/.
05ad79
diff --git a/sys-utils/zramctl.c b/sys-utils/zramctl.c
05ad79
new file mode 100644
05ad79
index 0000000..853401c
05ad79
--- /dev/null
05ad79
+++ b/sys-utils/zramctl.c
05ad79
@@ -0,0 +1,736 @@
05ad79
+/*
05ad79
+ * zramctl - control compressed block devices in RAM
05ad79
+ *
05ad79
+ * Copyright (c) 2014 Timofey Titovets <Nefelim4ag@gmail.com>
05ad79
+ * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
05ad79
+ *
05ad79
+ * This program is free software; you can redistribute it and/or
05ad79
+ * modify it under the terms of the GNU General Public License as
05ad79
+ * published by the Free Software Foundation.
05ad79
+ *
05ad79
+ * This program is distributed in the hope that it would be useful,
05ad79
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
05ad79
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
05ad79
+ * GNU General Public License for more details.
05ad79
+ *
05ad79
+ * You should have received a copy of the GNU General Public License along
05ad79
+ * with this program; if not, write to the Free Software Foundation, Inc.,
05ad79
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
05ad79
+ */
05ad79
+
05ad79
+#include <getopt.h>
05ad79
+#include <stdlib.h>
05ad79
+#include <string.h>
05ad79
+#include <stdarg.h>
05ad79
+#include <assert.h>
05ad79
+
05ad79
+#include <libsmartcols.h>
05ad79
+
05ad79
+#include "c.h"
05ad79
+#include "nls.h"
05ad79
+#include "closestream.h"
05ad79
+#include "strutils.h"
05ad79
+#include "xalloc.h"
05ad79
+#include "sysfs.h"
05ad79
+#include "optutils.h"
05ad79
+#include "ismounted.h"
05ad79
+#include "strv.h"
05ad79
+#include "path.h"
05ad79
+#include "pathnames.h"
05ad79
+
05ad79
+/*#define CONFIG_ZRAM_DEBUG*/
05ad79
+
05ad79
+#ifdef CONFIG_ZRAM_DEBUG
05ad79
+# define DBG(x)	 do { fputs("zram: ", stderr); x; fputc('\n', stderr); } while(0)
05ad79
+#else
05ad79
+# define DBG(x)
05ad79
+#endif
05ad79
+
05ad79
+/* status output columns */
05ad79
+struct colinfo {
05ad79
+	const char *name;
05ad79
+	double whint;
05ad79
+	int flags;
05ad79
+	const char *help;
05ad79
+};
05ad79
+
05ad79
+enum {
05ad79
+	COL_NAME = 0,
05ad79
+	COL_DISKSIZE,
05ad79
+	COL_ORIG_SIZE,
05ad79
+	COL_COMP_SIZE,
05ad79
+	COL_ALGORITHM,
05ad79
+	COL_STREAMS,
05ad79
+	COL_ZEROPAGES,
05ad79
+	COL_MEMTOTAL,
05ad79
+	COL_MEMLIMIT,
05ad79
+	COL_MEMUSED,
05ad79
+	COL_MIGRATED,
05ad79
+	COL_MOUNTPOINT
05ad79
+};
05ad79
+
05ad79
+static const struct colinfo infos[] = {
05ad79
+	[COL_NAME]      = { "NAME",      0.25, 0, N_("zram device name") },
05ad79
+	[COL_DISKSIZE]  = { "DISKSIZE",     5, SCOLS_FL_RIGHT, N_("limit on the uncompressed amount of data") },
05ad79
+	[COL_ORIG_SIZE] = { "DATA",         5, SCOLS_FL_RIGHT, N_("uncompressed size of stored data") },
05ad79
+	[COL_COMP_SIZE] = { "COMPR",        5, SCOLS_FL_RIGHT, N_("compressed size of stored data") },
05ad79
+	[COL_ALGORITHM] = { "ALGORITHM",    3, 0, N_("the selected compression algorithm") },
05ad79
+	[COL_STREAMS]   = { "STREAMS",      3, SCOLS_FL_RIGHT, N_("number of concurrent compress operations") },
05ad79
+	[COL_ZEROPAGES] = { "ZERO-PAGES",   3, SCOLS_FL_RIGHT, N_("empty pages with no allocated memory") },
05ad79
+	[COL_MEMTOTAL]  = { "TOTAL",        5, SCOLS_FL_RIGHT, N_("all memory including allocator fragmentation and metadata overhead") },
05ad79
+	[COL_MEMLIMIT]  = { "MEM-LIMIT",    5, SCOLS_FL_RIGHT, N_("memory limit used to store compressed data") },
05ad79
+	[COL_MEMUSED]   = { "MEM-USED",     5, SCOLS_FL_RIGHT, N_("memory zram have been consumed to store compressed data") },
05ad79
+	[COL_MIGRATED]  = { "MIGRATED",     5, SCOLS_FL_RIGHT, N_("number of objects migrated by compaction") },
05ad79
+	[COL_MOUNTPOINT]= { "MOUNTPOINT",0.10, SCOLS_FL_TRUNC, N_("where the device is mounted") },
05ad79
+};
05ad79
+
05ad79
+static int columns[ARRAY_SIZE(infos) * 2] = {-1};
05ad79
+static int ncolumns;
05ad79
+
05ad79
+enum {
05ad79
+	MM_ORIG_DATA_SIZE = 0,
05ad79
+	MM_COMPR_DATA_SIZE,
05ad79
+	MM_MEM_USED_TOTAL,
05ad79
+	MM_MEM_LIMIT,
05ad79
+	MM_MEM_USED_MAX,
05ad79
+	MM_ZERO_PAGES,
05ad79
+	MM_NUM_MIGRATED
05ad79
+};
05ad79
+
05ad79
+static const char *mm_stat_names[] = {
05ad79
+	[MM_ORIG_DATA_SIZE]  = "orig_data_size",
05ad79
+	[MM_COMPR_DATA_SIZE] = "compr_data_size",
05ad79
+	[MM_MEM_USED_TOTAL]  = "mem_used_total",
05ad79
+	[MM_MEM_LIMIT]       = "mem_limit",
05ad79
+	[MM_MEM_USED_MAX]    = "mem_used_max",
05ad79
+	[MM_ZERO_PAGES]      = "zero_pages",
05ad79
+	[MM_NUM_MIGRATED]    = "num_migrated"
05ad79
+};
05ad79
+
05ad79
+
05ad79
+struct zram {
05ad79
+	char	devname[32];
05ad79
+	struct sysfs_cxt sysfs;
05ad79
+	char	**mm_stat;
05ad79
+
05ad79
+	unsigned int mm_stat_probed : 1,
05ad79
+		     control_probed : 1,
05ad79
+		     has_control : 1;	/* has /sys/class/zram-control/ */
05ad79
+};
05ad79
+
05ad79
+#define ZRAM_EMPTY	{ .devname = { '\0' }, .sysfs = UL_SYSFSCXT_EMPTY }
05ad79
+
05ad79
+static unsigned int raw, no_headings, inbytes;
05ad79
+
05ad79
+
05ad79
+static int get_column_id(int num)
05ad79
+{
05ad79
+	assert(num < ncolumns);
05ad79
+	assert(columns[num] < (int) ARRAY_SIZE(infos));
05ad79
+	return columns[num];
05ad79
+}
05ad79
+
05ad79
+static const struct colinfo *get_column_info(int num)
05ad79
+{
05ad79
+	return &infos[ get_column_id(num) ];
05ad79
+}
05ad79
+
05ad79
+static int column_name_to_id(const char *name, size_t namesz)
05ad79
+{
05ad79
+	size_t i;
05ad79
+
05ad79
+	for (i = 0; i < ARRAY_SIZE(infos); i++) {
05ad79
+		const char *cn = infos[i].name;
05ad79
+
05ad79
+		if (!strncasecmp(name, cn, namesz) && !*(cn + namesz))
05ad79
+			return i;
05ad79
+	}
05ad79
+	warnx(_("unknown column: %s"), name);
05ad79
+	return -1;
05ad79
+}
05ad79
+
05ad79
+static void zram_reset_stat(struct zram *z)
05ad79
+{
05ad79
+	if (z) {
05ad79
+		strv_free(z->mm_stat);
05ad79
+		z->mm_stat = NULL;
05ad79
+		z->mm_stat_probed = 0;
05ad79
+	}
05ad79
+}
05ad79
+
05ad79
+static void zram_set_devname(struct zram *z, const char *devname, size_t n)
05ad79
+{
05ad79
+	assert(z);
05ad79
+
05ad79
+	if (!devname)
05ad79
+		snprintf(z->devname, sizeof(z->devname), "/dev/zram%zu", n);
05ad79
+	else {
05ad79
+		strncpy(z->devname, devname, sizeof(z->devname));
05ad79
+		z->devname[sizeof(z->devname) - 1] = '\0';
05ad79
+	}
05ad79
+
05ad79
+	DBG(fprintf(stderr, "set devname: %s", z->devname));
05ad79
+	sysfs_deinit(&z->sysfs);
05ad79
+	zram_reset_stat(z);
05ad79
+}
05ad79
+
05ad79
+static int zram_get_devnum(struct zram *z)
05ad79
+{
05ad79
+	int n;
05ad79
+
05ad79
+	assert(z);
05ad79
+
05ad79
+	if (sscanf(z->devname, "/dev/zram%d", &n) == 1)
05ad79
+		return n;
05ad79
+	return -EINVAL;
05ad79
+}
05ad79
+
05ad79
+static struct zram *new_zram(const char *devname)
05ad79
+{
05ad79
+	struct zram *z = xcalloc(1, sizeof(struct zram));
05ad79
+
05ad79
+	DBG(fprintf(stderr, "new: %p", z));
05ad79
+	if (devname)
05ad79
+		zram_set_devname(z, devname, 0);
05ad79
+	return z;
05ad79
+}
05ad79
+
05ad79
+static void free_zram(struct zram *z)
05ad79
+{
05ad79
+	if (!z)
05ad79
+		return;
05ad79
+	DBG(fprintf(stderr, "free: %p", z));
05ad79
+	sysfs_deinit(&z->sysfs);
05ad79
+	zram_reset_stat(z);
05ad79
+	free(z);
05ad79
+}
05ad79
+
05ad79
+static struct sysfs_cxt *zram_get_sysfs(struct zram *z)
05ad79
+{
05ad79
+	assert(z);
05ad79
+
05ad79
+	if (!z->sysfs.devno) {
05ad79
+		dev_t devno = sysfs_devname_to_devno(z->devname, NULL);
05ad79
+		if (!devno)
05ad79
+			return NULL;
05ad79
+		if (sysfs_init(&z->sysfs, devno, NULL))
05ad79
+			return NULL;
05ad79
+		if (*z->devname != '/') {
05ad79
+			/* canonicalize the device name according to /sys */
05ad79
+			char name[PATH_MAX];
05ad79
+			if (sysfs_get_devname(&z->sysfs, name, sizeof(name)))
05ad79
+				snprintf(z->devname, sizeof(z->devname), "/dev/%s", name);
05ad79
+		}
05ad79
+	}
05ad79
+
05ad79
+	return &z->sysfs;
05ad79
+}
05ad79
+
05ad79
+static inline int zram_exist(struct zram *z)
05ad79
+{
05ad79
+	assert(z);
05ad79
+
05ad79
+	errno = 0;
05ad79
+	if (zram_get_sysfs(z) == NULL) {
05ad79
+		errno = ENODEV;
05ad79
+		return 0;
05ad79
+	}
05ad79
+
05ad79
+	DBG(fprintf(stderr, "%s exists", z->devname));
05ad79
+	return 1;
05ad79
+}
05ad79
+
05ad79
+static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num)
05ad79
+{
05ad79
+	struct sysfs_cxt *sysfs = zram_get_sysfs(z);
05ad79
+	if (!sysfs)
05ad79
+		return -EINVAL;
05ad79
+	DBG(fprintf(stderr, "%s writing %ju to %s", z->devname, num, attr));
05ad79
+	return sysfs_write_u64(sysfs, attr, num);
05ad79
+}
05ad79
+
05ad79
+static int zram_set_strparm(struct zram *z, const char *attr, const char *str)
05ad79
+{
05ad79
+	struct sysfs_cxt *sysfs = zram_get_sysfs(z);
05ad79
+	if (!sysfs)
05ad79
+		return -EINVAL;
05ad79
+	DBG(fprintf(stderr, "%s writing %s to %s", z->devname, str, attr));
05ad79
+	return sysfs_write_string(sysfs, attr, str);
05ad79
+}
05ad79
+
05ad79
+
05ad79
+static int zram_used(struct zram *z)
05ad79
+{
05ad79
+	uint64_t size;
05ad79
+	struct sysfs_cxt *sysfs = zram_get_sysfs(z);
05ad79
+
05ad79
+	if (sysfs &&
05ad79
+	    sysfs_read_u64(sysfs, "disksize", &size) == 0 &&
05ad79
+	    size > 0) {
05ad79
+
05ad79
+		DBG(fprintf(stderr, "%s used", z->devname));
05ad79
+		return 1;
05ad79
+	}
05ad79
+	DBG(fprintf(stderr, "%s unused", z->devname));
05ad79
+	return 0;
05ad79
+}
05ad79
+
05ad79
+static int zram_has_control(struct zram *z)
05ad79
+{
05ad79
+	if (!z->control_probed) {
05ad79
+		z->has_control = access(_PATH_SYS_CLASS "/zram-control/", F_OK) == 0 ? 1 : 0;
05ad79
+		z->control_probed = 1;
05ad79
+		DBG(fprintf(stderr, "zram-control: %s", z->has_control ? "yes" : "no"));
05ad79
+	}
05ad79
+
05ad79
+	return z->has_control;
05ad79
+}
05ad79
+
05ad79
+static int zram_control_add(struct zram *z)
05ad79
+{
05ad79
+	int n;
05ad79
+
05ad79
+	if (!zram_has_control(z))
05ad79
+		return -ENOSYS;
05ad79
+
05ad79
+	n = path_read_s32(_PATH_SYS_CLASS "/zram-control/hot_add");
05ad79
+	if (n < 0)
05ad79
+		return n;
05ad79
+
05ad79
+	DBG(fprintf(stderr, "hot-add: %d", n));
05ad79
+	zram_set_devname(z, NULL, n);
05ad79
+	return 0;
05ad79
+}
05ad79
+
05ad79
+static int zram_control_remove(struct zram *z)
05ad79
+{
05ad79
+	char str[sizeof stringify_value(INT_MAX)];
05ad79
+	int n;
05ad79
+
05ad79
+	if (!zram_has_control(z))
05ad79
+		return -ENOSYS;
05ad79
+
05ad79
+	n = zram_get_devnum(z);
05ad79
+	if (n < 0)
05ad79
+		return n;
05ad79
+
05ad79
+	DBG(fprintf(stderr, "hot-remove: %d", n));
05ad79
+	snprintf(str, sizeof(str), "%d", n);
05ad79
+	return path_write_str(str, _PATH_SYS_CLASS "/zram-control/hot_remove");
05ad79
+}
05ad79
+
05ad79
+static struct zram *find_free_zram(void)
05ad79
+{
05ad79
+	struct zram *z = new_zram(NULL);
05ad79
+	size_t i;
05ad79
+	int isfree = 0;
05ad79
+
05ad79
+	for (i = 0; isfree == 0; i++) {
05ad79
+		DBG(fprintf(stderr, "find free: checking zram%zu", i));
05ad79
+		zram_set_devname(z, NULL, i);
05ad79
+		if (!zram_exist(z) && zram_control_add(z) != 0)
05ad79
+			break;
05ad79
+		isfree = !zram_used(z);
05ad79
+	}
05ad79
+	if (!isfree) {
05ad79
+		free_zram(z);
05ad79
+		z = NULL;
05ad79
+	}
05ad79
+	return z;
05ad79
+}
05ad79
+
05ad79
+static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
05ad79
+{
05ad79
+	struct sysfs_cxt *sysfs;
05ad79
+	const char *name;
05ad79
+	uint64_t num;
05ad79
+
05ad79
+	assert(idx < ARRAY_SIZE(mm_stat_names));
05ad79
+	assert(z);
05ad79
+
05ad79
+	sysfs = zram_get_sysfs(z);
05ad79
+	if (!sysfs)
05ad79
+		return NULL;
05ad79
+
05ad79
+	/* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */
05ad79
+	if (!z->mm_stat && !z->mm_stat_probed) {
05ad79
+		char *str;
05ad79
+
05ad79
+		str = sysfs_strdup(sysfs, "mm_stat");
05ad79
+		if (str) {
05ad79
+			z->mm_stat = strv_split(str, " ");
05ad79
+			if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names))
05ad79
+				errx(EXIT_FAILURE, _("Failed to parse mm_stat"));
05ad79
+		}
05ad79
+		z->mm_stat_probed = 1;
05ad79
+		free(str);
05ad79
+
05ad79
+	}
05ad79
+
05ad79
+	if (z->mm_stat) {
05ad79
+		if (bytes)
05ad79
+			return xstrdup(z->mm_stat[idx]);
05ad79
+
05ad79
+		num = strtou64_or_err(z->mm_stat[idx], _("Failed to parse mm_stat"));
05ad79
+		return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
05ad79
+	}
05ad79
+
05ad79
+	/* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */
05ad79
+	name = mm_stat_names[idx];
05ad79
+	if (bytes)
05ad79
+		return sysfs_strdup(sysfs, name);
05ad79
+	else if (sysfs_read_u64(sysfs, name, &num) == 0)
05ad79
+		return size_to_human_string(SIZE_SUFFIX_1LETTER, num);
05ad79
+	return NULL;
05ad79
+}
05ad79
+
05ad79
+static void fill_table_row(struct libscols_table *tb, struct zram *z)
05ad79
+{
05ad79
+	static struct libscols_line *ln;
05ad79
+	struct sysfs_cxt *sysfs;
05ad79
+	size_t i;
05ad79
+	uint64_t num;
05ad79
+
05ad79
+	assert(tb);
05ad79
+	assert(z);
05ad79
+
05ad79
+	DBG(fprintf(stderr, "%s: filling status table", z->devname));
05ad79
+
05ad79
+	sysfs = zram_get_sysfs(z);
05ad79
+	if (!sysfs)
05ad79
+		return;
05ad79
+
05ad79
+	ln = scols_table_new_line(tb, NULL);
05ad79
+	if (!ln)
05ad79
+		err(EXIT_FAILURE, _("failed to initialize output line"));
05ad79
+
05ad79
+	for (i = 0; i < (size_t) ncolumns; i++) {
05ad79
+		char *str = NULL;
05ad79
+
05ad79
+		switch (get_column_id(i)) {
05ad79
+		case COL_NAME:
05ad79
+			str = xstrdup(z->devname);
05ad79
+			break;
05ad79
+		case COL_DISKSIZE:
05ad79
+			if (inbytes)
05ad79
+				str = sysfs_strdup(sysfs, "disksize");
05ad79
+			else if (sysfs_read_u64(sysfs, "disksize", &num) == 0)
05ad79
+				str = size_to_human_string(SIZE_SUFFIX_1LETTER, num);
05ad79
+			break;
05ad79
+		case COL_ALGORITHM:
05ad79
+		{
05ad79
+			char *alg = sysfs_strdup(sysfs, "comp_algorithm");
05ad79
+			if (!alg)
05ad79
+				break;
05ad79
+			if (strstr(alg, "[lzo]") == NULL) {
05ad79
+				if (strstr(alg, "[lz4]") == NULL)
05ad79
+					;
05ad79
+				else
05ad79
+					str = xstrdup("lz4");
05ad79
+			} else
05ad79
+				str = xstrdup("lzo");
05ad79
+			free(alg);
05ad79
+			break;
05ad79
+		}
05ad79
+		case COL_MOUNTPOINT:
05ad79
+		{
05ad79
+			char path[PATH_MAX] = { '\0' };
05ad79
+			int fl;
05ad79
+
05ad79
+			check_mount_point(z->devname, &fl, path, sizeof(path));
05ad79
+			if (*path)
05ad79
+				str = xstrdup(path);
05ad79
+			break;
05ad79
+		}
05ad79
+		case COL_STREAMS:
05ad79
+			str = sysfs_strdup(sysfs, "max_comp_streams");
05ad79
+			break;
05ad79
+		case COL_ZEROPAGES:
05ad79
+			str = get_mm_stat(z, MM_ZERO_PAGES, 1);
05ad79
+			break;
05ad79
+		case COL_ORIG_SIZE:
05ad79
+			str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes);
05ad79
+			break;
05ad79
+		case COL_COMP_SIZE:
05ad79
+			str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes);
05ad79
+			break;
05ad79
+		case COL_MEMTOTAL:
05ad79
+			str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes);
05ad79
+			break;
05ad79
+		case COL_MEMLIMIT:
05ad79
+			str = get_mm_stat(z, MM_MEM_LIMIT, inbytes);
05ad79
+			break;
05ad79
+		case COL_MEMUSED:
05ad79
+			str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes);
05ad79
+			break;
05ad79
+		case COL_MIGRATED:
05ad79
+			str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes);
05ad79
+			break;
05ad79
+		}
05ad79
+		if (str)
05ad79
+			scols_line_refer_data(ln, i, str);
05ad79
+	}
05ad79
+}
05ad79
+
05ad79
+static void status(struct zram *z)
05ad79
+{
05ad79
+	struct libscols_table *tb;
05ad79
+	size_t i;
05ad79
+
05ad79
+	scols_init_debug(0);
05ad79
+
05ad79
+	tb = scols_new_table();
05ad79
+	if (!tb)
05ad79
+		err(EXIT_FAILURE, _("failed to initialize output table"));
05ad79
+
05ad79
+	scols_table_enable_raw(tb, raw);
05ad79
+	scols_table_enable_noheadings(tb, no_headings);
05ad79
+
05ad79
+	for (i = 0; i < (size_t) ncolumns; i++) {
05ad79
+		const struct colinfo *col = get_column_info(i);
05ad79
+
05ad79
+		if (!scols_table_new_column(tb, col->name, col->whint, col->flags))
05ad79
+			err(EXIT_FAILURE, _("failed to initialize output column"));
05ad79
+	}
05ad79
+
05ad79
+	if (z)
05ad79
+		fill_table_row(tb, z);		/* just one device specified */
05ad79
+	else {
05ad79
+		/* list all used devices */
05ad79
+		z = new_zram(NULL);
05ad79
+
05ad79
+		for (i = 0; ; i++) {
05ad79
+			zram_set_devname(z, NULL, i);
05ad79
+			if (!zram_exist(z))
05ad79
+				break;
05ad79
+			if (zram_used(z))
05ad79
+				fill_table_row(tb, z);
05ad79
+		}
05ad79
+		free_zram(z);
05ad79
+	}
05ad79
+
05ad79
+	scols_print_table(tb);
05ad79
+	scols_unref_table(tb);
05ad79
+}
05ad79
+
05ad79
+static void __attribute__ ((__noreturn__)) usage(FILE * out)
05ad79
+{
05ad79
+	size_t i;
05ad79
+
05ad79
+	fputs(USAGE_HEADER, out);
05ad79
+	fprintf(out, _(	" %1$s [options] <device>\n"
05ad79
+			" %1$s -r <device> [...]\n"
05ad79
+			" %1$s [options] -f | <device> -s <size>\n"),
05ad79
+			program_invocation_short_name);
05ad79
+
05ad79
+	fputs(USAGE_SEPARATOR, out);
05ad79
+	fputs(_("Set up and control zram devices.\n"), out);
05ad79
+
05ad79
+	fputs(USAGE_OPTIONS, out);
05ad79
+	fputs(_(" -a, --algorithm lzo|lz4   compression algorithm to use\n"), out);
05ad79
+	fputs(_(" -b, --bytes               print sizes in bytes rather than in human readable format\n"), out);
05ad79
+	fputs(_(" -f, --find                find a free device\n"), out);
05ad79
+	fputs(_(" -n, --noheadings          don't print headings\n"), out);
05ad79
+	fputs(_(" -o, --output <list>       columns to use for status output\n"), out);
05ad79
+	fputs(_("     --raw                 use raw status output format\n"), out);
05ad79
+	fputs(_(" -r, --reset               reset all specified devices\n"), out);
05ad79
+	fputs(_(" -s, --size <size>         device size\n"), out);
05ad79
+	fputs(_(" -t, --streams <number>    number of compression streams\n"), out);
05ad79
+
05ad79
+	fputs(USAGE_SEPARATOR, out);
05ad79
+	fputs(USAGE_HELP, out);
05ad79
+	fputs(USAGE_VERSION, out);
05ad79
+
05ad79
+	fputs(_("\nAvailable columns (for --output):\n"), out);
05ad79
+	for (i = 0; i < ARRAY_SIZE(infos); i++)
05ad79
+		fprintf(out, " %11s  %s\n", infos[i].name, _(infos[i].help));
05ad79
+
05ad79
+	fprintf(out, USAGE_MAN_TAIL("zramctl(8)"));
05ad79
+	exit(out == stderr ? 1 : EXIT_SUCCESS);
05ad79
+}
05ad79
+
05ad79
+/* actions */
05ad79
+enum {
05ad79
+	A_NONE = 0,
05ad79
+	A_STATUS,
05ad79
+	A_CREATE,
05ad79
+	A_FINDONLY,
05ad79
+	A_RESET
05ad79
+};
05ad79
+
05ad79
+int main(int argc, char **argv)
05ad79
+{
05ad79
+	uintmax_t size = 0, nstreams = 0;
05ad79
+	char *algorithm = NULL;
05ad79
+	int rc = 0, c, find = 0, act = A_NONE;
05ad79
+	struct zram *zram = NULL;
05ad79
+
05ad79
+	enum { OPT_RAW = CHAR_MAX + 1 };
05ad79
+
05ad79
+	static const struct option longopts[] = {
05ad79
+		{ "algorithm", required_argument, NULL, 'a' },
05ad79
+		{ "bytes",     no_argument, NULL, 'b' },
05ad79
+		{ "find",      no_argument, NULL, 'f' },
05ad79
+		{ "help",      no_argument, NULL, 'h' },
05ad79
+		{ "output",    required_argument, NULL, 'o' },
05ad79
+		{ "noheadings",no_argument, NULL, 'n' },
05ad79
+		{ "reset",     no_argument, NULL, 'r' },
05ad79
+		{ "raw",       no_argument, NULL, OPT_RAW },
05ad79
+		{ "size",      required_argument, NULL, 's' },
05ad79
+		{ "streams",   required_argument, NULL, 't' },
05ad79
+		{ "version",   no_argument, NULL, 'V' },
05ad79
+		{ NULL, 0, NULL, 0 }
05ad79
+	};
05ad79
+
05ad79
+	static const ul_excl_t excl[] = {
05ad79
+		{ 'f', 'o', 'r' },
05ad79
+		{ 'o', 'r', 's' },
05ad79
+		{ 0 }
05ad79
+	};
05ad79
+	int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
05ad79
+
05ad79
+	setlocale(LC_ALL, "");
05ad79
+	bindtextdomain(PACKAGE, LOCALEDIR);
05ad79
+	textdomain(PACKAGE);
05ad79
+	atexit(close_stdout);
05ad79
+
05ad79
+	while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) {
05ad79
+
05ad79
+		err_exclusive_options(c, longopts, excl, excl_st);
05ad79
+
05ad79
+		switch (c) {
05ad79
+		case 'a':
05ad79
+			if (strcmp(optarg,"lzo") && strcmp(optarg,"lz4"))
05ad79
+				errx(EXIT_FAILURE, _("unsupported algorithm: %s"),
05ad79
+					     optarg);
05ad79
+			algorithm = optarg;
05ad79
+			break;
05ad79
+		case 'b':
05ad79
+			inbytes = 1;
05ad79
+			break;
05ad79
+		case 'f':
05ad79
+			find = 1;
05ad79
+			break;
05ad79
+		case 'o':
05ad79
+			ncolumns = string_to_idarray(optarg,
05ad79
+						     columns, ARRAY_SIZE(columns),
05ad79
+						     column_name_to_id);
05ad79
+			if (ncolumns < 0)
05ad79
+				return EXIT_FAILURE;
05ad79
+			break;
05ad79
+		case 's':
05ad79
+			size = strtosize_or_err(optarg, _("failed to parse size"));
05ad79
+			act = A_CREATE;
05ad79
+			break;
05ad79
+		case 't':
05ad79
+			nstreams = strtou64_or_err(optarg, _("failed to parse streams"));
05ad79
+			break;
05ad79
+		case 'r':
05ad79
+			act = A_RESET;
05ad79
+			break;
05ad79
+		case OPT_RAW:
05ad79
+			raw = 1;
05ad79
+			break;
05ad79
+		case 'n':
05ad79
+			no_headings = 1;
05ad79
+			break;
05ad79
+		case 'V':
05ad79
+			printf(UTIL_LINUX_VERSION);
05ad79
+			return EXIT_SUCCESS;
05ad79
+		case 'h':
05ad79
+			usage(stdout);
05ad79
+		default:
05ad79
+			usage(stderr);
05ad79
+		}
05ad79
+	}
05ad79
+
05ad79
+	if (find && optind < argc)
05ad79
+		errx(EXIT_FAILURE, _("option --find is mutually exclusive "
05ad79
+				     "with <device>"));
05ad79
+	if (act == A_NONE)
05ad79
+		act = find ? A_FINDONLY : A_STATUS;
05ad79
+
05ad79
+	if (act != A_RESET && optind + 1 < argc)
05ad79
+		errx(EXIT_FAILURE, _("only one <device> at a time is allowed"));
05ad79
+
05ad79
+	if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams))
05ad79
+		errx(EXIT_FAILURE, _("options --algorithm and --streams "
05ad79
+				     "must be combined with --size"));
05ad79
+
05ad79
+	switch (act) {
05ad79
+	case A_STATUS:
05ad79
+		if (!ncolumns) {		/* default columns */
05ad79
+			columns[ncolumns++] = COL_NAME;
05ad79
+			columns[ncolumns++] = COL_ALGORITHM;
05ad79
+			columns[ncolumns++] = COL_DISKSIZE;
05ad79
+			columns[ncolumns++] = COL_ORIG_SIZE;
05ad79
+			columns[ncolumns++] = COL_COMP_SIZE;
05ad79
+			columns[ncolumns++] = COL_MEMTOTAL;
05ad79
+			columns[ncolumns++] = COL_STREAMS;
05ad79
+			columns[ncolumns++] = COL_MOUNTPOINT;
05ad79
+		}
05ad79
+		if (optind < argc) {
05ad79
+			zram = new_zram(argv[optind++]);
05ad79
+			if (!zram_exist(zram))
05ad79
+				err(EXIT_FAILURE, "%s", zram->devname);
05ad79
+		}
05ad79
+		status(zram);
05ad79
+		free_zram(zram);
05ad79
+		break;
05ad79
+	case A_RESET:
05ad79
+		if (optind == argc)
05ad79
+			errx(EXIT_FAILURE, _("no device specified"));
05ad79
+		while (optind < argc) {
05ad79
+			zram = new_zram(argv[optind]);
05ad79
+			if (!zram_exist(zram)
05ad79
+			    || zram_set_u64parm(zram, "reset", 1)) {
05ad79
+				warn(_("%s: failed to reset"), zram->devname);
05ad79
+				rc = 1;
05ad79
+			}
05ad79
+			zram_control_remove(zram);
05ad79
+			free_zram(zram);
05ad79
+			optind++;
05ad79
+		}
05ad79
+		break;
05ad79
+	case A_FINDONLY:
05ad79
+		zram = find_free_zram();
05ad79
+		if (!zram)
05ad79
+			errx(EXIT_FAILURE, _("no free zram device found"));
05ad79
+		printf("%s\n", zram->devname);
05ad79
+		free_zram(zram);
05ad79
+		break;
05ad79
+	case A_CREATE:
05ad79
+		if (find) {
05ad79
+			zram = find_free_zram();
05ad79
+			if (!zram)
05ad79
+				errx(EXIT_FAILURE, _("no free zram device found"));
05ad79
+		} else if (optind == argc)
05ad79
+			errx(EXIT_FAILURE, _("no device specified"));
05ad79
+		else {
05ad79
+			zram = new_zram(argv[optind]);
05ad79
+			if (!zram_exist(zram))
05ad79
+				err(EXIT_FAILURE, "%s", zram->devname);
05ad79
+		}
05ad79
+
05ad79
+		if (zram_set_u64parm(zram, "reset", 1))
05ad79
+			err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname);
05ad79
+
05ad79
+		if (nstreams &&
05ad79
+		    zram_set_u64parm(zram, "max_comp_streams", nstreams))
05ad79
+			err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname);
05ad79
+
05ad79
+		if (algorithm &&
05ad79
+		    zram_set_strparm(zram, "comp_algorithm", algorithm))
05ad79
+			err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname);
05ad79
+
05ad79
+		if (zram_set_u64parm(zram, "disksize", size))
05ad79
+			err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"),
05ad79
+				zram->devname, size);
05ad79
+		if (find)
05ad79
+			printf("%s\n", zram->devname);
05ad79
+		free_zram(zram);
05ad79
+		break;
05ad79
+	}
05ad79
+
05ad79
+	return rc ? EXIT_FAILURE : EXIT_SUCCESS;
05ad79
+}
05ad79
-- 
05ad79
2.9.3
05ad79