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