]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - sys-utils/zramctl.c
Merge branch 'patch-23' of https://github.com/mariobl/util-linux
[thirdparty/util-linux.git] / sys-utils / zramctl.c
index 30134338fcfd2a5d7c596b9371e2ede5fa2a725a..cc32ab4f69c8b9d3dce16125dee9acf55b13e7c3 100644 (file)
@@ -23,6 +23,8 @@
 #include <string.h>
 #include <stdarg.h>
 #include <assert.h>
+#include <sys/types.h>
+#include <dirent.h>
 
 #include <libsmartcols.h>
 
@@ -35,6 +37,8 @@
 #include "optutils.h"
 #include "ismounted.h"
 #include "strv.h"
+#include "path.h"
+#include "pathnames.h"
 
 /*#define CONFIG_ZRAM_DEBUG*/
 
@@ -77,7 +81,7 @@ static const struct colinfo infos[] = {
        [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 consumed 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") },
 };
@@ -105,19 +109,18 @@ static const char *mm_stat_names[] = {
        [MM_NUM_MIGRATED]    = "num_migrated"
 };
 
-
 struct zram {
        char    devname[32];
-       struct sysfs_cxt sysfs;
+       struct  path_cxt *sysfs;        /* device specific sysfs directory */
        char    **mm_stat;
 
-       unsigned int mm_stat_probed : 1;
+       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 struct path_cxt *__control;
 
 static int get_column_id(int num)
 {
@@ -145,19 +148,39 @@ static int column_name_to_id(const char *name, size_t namesz)
        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';
-       }
+       else
+               xstrncpy(z->devname, devname, sizeof(z->devname));
 
        DBG(fprintf(stderr, "set devname: %s", z->devname));
-       sysfs_deinit(&z->sysfs);
+       ul_unref_path(z->sysfs);
+       z->sysfs = NULL;
+       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)
@@ -175,31 +198,28 @@ static void free_zram(struct zram *z)
        if (!z)
                return;
        DBG(fprintf(stderr, "free: %p", z));
-       sysfs_deinit(&z->sysfs);
-
-       strv_free(z->mm_stat);
+       ul_unref_path(z->sysfs);
+       zram_reset_stat(z);
        free(z);
 }
 
-static struct sysfs_cxt *zram_get_sysfs(struct zram *z)
+static struct path_cxt *zram_get_sysfs(struct zram *z)
 {
        assert(z);
 
-       if (!z->sysfs.devno) {
-               dev_t devno = sysfs_devname_to_devno(z->devname, NULL);
+       if (!z->sysfs) {
+               dev_t devno = sysfs_devname_to_devno(z->devname);
                if (!devno)
                        return NULL;
-               if (sysfs_init(&z->sysfs, devno, NULL))
+               z->sysfs = ul_new_sysfs_path(devno, NULL, NULL);
+               if (!z->sysfs)
                        return NULL;
-               if (*z->devname != '/') {
-                       /* cannonicalize 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);
-               }
+               if (*z->devname != '/')
+                       /* canonicalize the device name according to /sys */
+                       sysfs_blkdev_get_path(z->sysfs, z->devname, sizeof(z->devname));
        }
 
-       return &z->sysfs;
+       return z->sysfs;
 }
 
 static inline int zram_exist(struct zram *z)
@@ -218,30 +238,30 @@ static inline int zram_exist(struct zram *z)
 
 static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num)
 {
-       struct sysfs_cxt *sysfs = zram_get_sysfs(z);
+       struct path_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);
+       return ul_path_write_u64(sysfs, num, attr);
 }
 
 static int zram_set_strparm(struct zram *z, const char *attr, const char *str)
 {
-       struct sysfs_cxt *sysfs = zram_get_sysfs(z);
+       struct path_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);
+       return ul_path_write_string(sysfs, str, attr);
 }
 
 
 static int zram_used(struct zram *z)
 {
        uint64_t size;
-       struct sysfs_cxt *sysfs = zram_get_sysfs(z);
+       struct path_cxt *sysfs = zram_get_sysfs(z);
 
        if (sysfs &&
-           sysfs_read_u64(sysfs, "disksize", &size) == 0 &&
+           ul_path_read_u64(sysfs, &size, "disksize") == 0 &&
            size > 0) {
 
                DBG(fprintf(stderr, "%s used", z->devname));
@@ -251,6 +271,56 @@ static int zram_used(struct zram *z)
        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 struct path_cxt *zram_get_control(void)
+{
+       if (!__control)
+               __control = ul_new_path(_PATH_SYS_CLASS "/zram-control");
+       return __control;
+}
+
+static int zram_control_add(struct zram *z)
+{
+       int n = 0;
+       struct path_cxt *ctl;
+
+       if (!zram_has_control(z) || !(ctl = zram_get_control()))
+               return -ENOSYS;
+
+       if (ul_path_read_s32(ctl, &n, "hot_add") != 0 || 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)
+{
+       struct path_cxt *ctl;
+       int n;
+
+       if (!zram_has_control(z) || !(ctl = zram_get_control()))
+               return -ENOSYS;
+
+       n = zram_get_devnum(z);
+       if (n < 0)
+               return n;
+
+       DBG(fprintf(stderr, "hot-remove: %d", n));
+       return ul_path_write_u64(ctl, n, "hot_remove");
+}
+
 static struct zram *find_free_zram(void)
 {
        struct zram *z = new_zram(NULL);
@@ -260,7 +330,7 @@ static struct zram *find_free_zram(void)
        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))
+               if (!zram_exist(z) && zram_control_add(z) != 0)
                        break;
                isfree = !zram_used(z);
        }
@@ -271,12 +341,11 @@ static struct zram *find_free_zram(void)
        return z;
 }
 
-#include "path.h"
-
 static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
 {
-       struct sysfs_cxt *sysfs;
+       struct path_cxt *sysfs;
        const char *name;
+       char *str = NULL;
        uint64_t num;
 
        assert(idx < ARRAY_SIZE(mm_stat_names));
@@ -288,17 +357,18 @@ static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
 
        /* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */
        if (!z->mm_stat && !z->mm_stat_probed) {
-               char *str;
-
-               str = sysfs_strdup(sysfs, "mm_stat");
-               if (str) {
+               if (ul_path_read_string(sysfs, &str, "mm_stat") > 0 && 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"));
+
+                       /* make sure kernel provides mm_stat as expected */
+                       if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) {
+                               strv_free(z->mm_stat);
+                               z->mm_stat = NULL;
+                       }
                }
                z->mm_stat_probed = 1;
                free(str);
-
+               str = NULL;
        }
 
        if (z->mm_stat) {
@@ -311,17 +381,22 @@ static char *get_mm_stat(struct zram *z, size_t idx, int bytes)
 
        /* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */
        name = mm_stat_names[idx];
-       if (bytes)
-               return sysfs_strdup(sysfs, name);
-       else if (sysfs_read_u64(sysfs, name, &num) == 0)
+       if (bytes) {
+               ul_path_read_string(sysfs, &str, name);
+               return str;
+
+       }
+
+       if (ul_path_read_u64(sysfs, &num, name) == 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;
+       struct path_cxt *sysfs;
        size_t i;
        uint64_t num;
 
@@ -336,7 +411,7 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
 
        ln = scols_table_new_line(tb, NULL);
        if (!ln)
-               err(EXIT_FAILURE, _("failed to initialize output line"));
+               err(EXIT_FAILURE, _("failed to allocate output line"));
 
        for (i = 0; i < (size_t) ncolumns; i++) {
                char *str = NULL;
@@ -347,23 +422,24 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
                        break;
                case COL_DISKSIZE:
                        if (inbytes)
-                               str = sysfs_strdup(sysfs, "disksize");
-                       else if (sysfs_read_u64(sysfs, "disksize", &num) == 0)
+                               ul_path_read_string(sysfs, &str, "disksize");
+
+                       else if (ul_path_read_u64(sysfs, &num, "disksize") == 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);
+                       char *alg = NULL;
+
+                       ul_path_read_string(sysfs, &alg, "comp_algorithm");
+                       if (alg) {
+                               char* lbr = strrchr(alg, '[');
+                               char* rbr = strrchr(alg, ']');
+
+                               if (lbr != NULL && rbr != NULL && rbr - lbr > 1)
+                                       str = xstrndup(lbr + 1, rbr - lbr - 1);
+                               free(alg);
+                       }
                        break;
                }
                case COL_MOUNTPOINT:
@@ -377,7 +453,7 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
                        break;
                }
                case COL_STREAMS:
-                       str = sysfs_strdup(sysfs, "max_comp_streams");
+                       ul_path_read_string(sysfs, &str, "max_comp_streams");
                        break;
                case COL_ZEROPAGES:
                        str = get_mm_stat(z, MM_ZERO_PAGES, 1);
@@ -401,8 +477,8 @@ static void fill_table_row(struct libscols_table *tb, struct zram *z)
                        str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes);
                        break;
                }
-               if (str)
-                       scols_line_refer_data(ln, i, str);
+               if (str && scols_line_refer_data(ln, i, str))
+                       err(EXIT_FAILURE, _("failed to add output data"));
        }
 }
 
@@ -410,12 +486,14 @@ static void status(struct zram *z)
 {
        struct libscols_table *tb;
        size_t i;
+       DIR *dir;
+       struct dirent *d;
 
        scols_init_debug(0);
 
        tb = scols_new_table();
        if (!tb)
-               err(EXIT_FAILURE, _("failed to initialize output table"));
+               err(EXIT_FAILURE, _("failed to allocate output table"));
 
        scols_table_enable_raw(tb, raw);
        scols_table_enable_noheadings(tb, no_headings);
@@ -427,28 +505,36 @@ static void status(struct zram *z)
                        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);
+       if (z) {
+               /* just one device specified */
+               fill_table_row(tb, z);
+               goto print_table;
        }
 
+       /* list all used devices */
+       z = new_zram(NULL);
+       if (!(dir = opendir(_PATH_DEV)))
+               err(EXIT_FAILURE, _("cannot open %s"), _PATH_DEV);
+
+       while ((d = readdir(dir))) {
+               int n;
+               if (sscanf(d->d_name, "zram%d", &n) != 1)
+                       continue;
+               zram_set_devname(z, NULL, n);
+               if (zram_exist(z) && zram_used(z))
+                       fill_table_row(tb, z);
+       }
+       closedir(dir);
+       free_zram(z);
+
+print_table:
        scols_print_table(tb);
        scols_unref_table(tb);
 }
 
-static void __attribute__ ((__noreturn__)) usage(FILE * out)
+static void __attribute__((__noreturn__)) usage(void)
 {
+       FILE *out = stdout;
        size_t i;
 
        fputs(USAGE_HEADER, out);
@@ -461,26 +547,33 @@ static void __attribute__ ((__noreturn__)) usage(FILE * 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(_(" -a, --algorithm <alg>     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 <list>       columns to use for status output\n"), out);
+       fputs(_("     --output-all          output all columns\n"), out);
        fputs(_("     --raw                 use raw status output format\n"), out);
        fputs(_(" -r, --reset               reset all specified devices\n"), out);
        fputs(_(" -s, --size <size>         device size\n"), out);
        fputs(_(" -t, --streams <number>    number of compression streams\n"), out);
 
        fputs(USAGE_SEPARATOR, out);
-       fputs(USAGE_HELP, out);
-       fputs(USAGE_VERSION, out);
+       fprintf(out, USAGE_HELP_OPTIONS(27));
+
+       fputs(USAGE_ARGUMENTS, out);
+       fprintf(out, USAGE_ARG_SIZE(_("<size>")));
 
-       fputs(_("\nAvailable columns (for --output):\n"), out);
+       fputs(_(" <alg> specify algorithm, supported are:\n"), out);
+       fputs(_("   lzo, lz4, lz4hc, deflate, 842 and zstd\n"), out);
+       fputs(_("   (List may be inaccurate, consult man page.)\n"), out);
+
+       fputs(USAGE_COLUMNS, 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);
+       exit(EXIT_SUCCESS);
 }
 
 /* actions */
@@ -499,7 +592,10 @@ int main(int argc, char **argv)
        int rc = 0, c, find = 0, act = A_NONE;
        struct zram *zram = NULL;
 
-       enum { OPT_RAW = CHAR_MAX + 1 };
+       enum {
+               OPT_RAW = CHAR_MAX + 1,
+               OPT_LIST_TYPES
+       };
 
        static const struct option longopts[] = {
                { "algorithm", required_argument, NULL, 'a' },
@@ -507,6 +603,7 @@ int main(int argc, char **argv)
                { "find",      no_argument, NULL, 'f' },
                { "help",      no_argument, NULL, 'h' },
                { "output",    required_argument, NULL, 'o' },
+               { "output-all",no_argument, NULL, OPT_LIST_TYPES },
                { "noheadings",no_argument, NULL, 'n' },
                { "reset",     no_argument, NULL, 'r' },
                { "raw",       no_argument, NULL, OPT_RAW },
@@ -526,7 +623,7 @@ int main(int argc, char **argv)
        setlocale(LC_ALL, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
+       close_stdout_atexit();
 
        while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) {
 
@@ -534,9 +631,6 @@ int main(int argc, char **argv)
 
                switch (c) {
                case 'a':
-                       if (strcmp(optarg,"lzo") && strcmp(optarg,"lz4"))
-                               errx(EXIT_FAILURE, _("unsupported algorithm: %s"),
-                                            optarg);
                        algorithm = optarg;
                        break;
                case 'b':
@@ -552,6 +646,10 @@ int main(int argc, char **argv)
                        if (ncolumns < 0)
                                return EXIT_FAILURE;
                        break;
+               case OPT_LIST_TYPES:
+                       for (ncolumns = 0; (size_t)ncolumns < ARRAY_SIZE(infos); ncolumns++)
+                               columns[ncolumns] = ncolumns;
+                       break;
                case 's':
                        size = strtosize_or_err(optarg, _("failed to parse size"));
                        act = A_CREATE;
@@ -568,13 +666,13 @@ int main(int argc, char **argv)
                case 'n':
                        no_headings = 1;
                        break;
+
                case 'V':
-                       printf(UTIL_LINUX_VERSION);
-                       return EXIT_SUCCESS;
+                       print_version(EXIT_SUCCESS);
                case 'h':
-                       usage(stdout);
+                       usage();
                default:
-                       usage(stderr);
+                       errtryhelp(EXIT_FAILURE);
                }
        }
 
@@ -591,6 +689,9 @@ int main(int argc, char **argv)
                errx(EXIT_FAILURE, _("options --algorithm and --streams "
                                     "must be combined with --size"));
 
+       ul_path_init_debug();
+       ul_sysfs_init_debug();
+
        switch (act) {
        case A_STATUS:
                if (!ncolumns) {                /* default columns */
@@ -621,6 +722,7 @@ int main(int argc, char **argv)
                                warn(_("%s: failed to reset"), zram->devname);
                                rc = 1;
                        }
+                       zram_control_remove(zram);
                        free_zram(zram);
                        optind++;
                }
@@ -665,5 +767,6 @@ int main(int argc, char **argv)
                break;
        }
 
+       ul_unref_path(__control);
        return rc ? EXIT_FAILURE : EXIT_SUCCESS;
 }