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