]>
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 | { | |
294 | int n; | |
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 | ||
388 | } else if (ul_path_read_u64(sysfs, &num, name) == 0) | |
5388c862 | 389 | return size_to_human_string(SIZE_SUFFIX_1LETTER, num); |
a6f0da16 | 390 | |
5388c862 KZ |
391 | return NULL; |
392 | } | |
393 | ||
0624d840 KZ |
394 | static void fill_table_row(struct libscols_table *tb, struct zram *z) |
395 | { | |
396 | static struct libscols_line *ln; | |
a6f0da16 | 397 | struct path_cxt *sysfs; |
0624d840 KZ |
398 | size_t i; |
399 | uint64_t num; | |
400 | ||
401 | assert(tb); | |
402 | assert(z); | |
403 | ||
404 | DBG(fprintf(stderr, "%s: filling status table", z->devname)); | |
405 | ||
406 | sysfs = zram_get_sysfs(z); | |
407 | if (!sysfs) | |
408 | return; | |
409 | ||
410 | ln = scols_table_new_line(tb, NULL); | |
411 | if (!ln) | |
780ce22c | 412 | err(EXIT_FAILURE, _("failed to allocate output line")); |
0624d840 KZ |
413 | |
414 | for (i = 0; i < (size_t) ncolumns; i++) { | |
415 | char *str = NULL; | |
416 | ||
417 | switch (get_column_id(i)) { | |
418 | case COL_NAME: | |
419 | str = xstrdup(z->devname); | |
420 | break; | |
421 | case COL_DISKSIZE: | |
422 | if (inbytes) | |
a6f0da16 KZ |
423 | ul_path_read_string(sysfs, &str, "disksize"); |
424 | ||
425 | else if (ul_path_read_u64(sysfs, &num, "disksize") == 0) | |
0624d840 KZ |
426 | str = size_to_human_string(SIZE_SUFFIX_1LETTER, num); |
427 | break; | |
0624d840 KZ |
428 | case COL_ALGORITHM: |
429 | { | |
a6f0da16 | 430 | char *alg = NULL; |
a3612b81 | 431 | |
a6f0da16 KZ |
432 | ul_path_read_string(sysfs, &alg, "comp_algorithm"); |
433 | if (alg) { | |
63bf2439 KZ |
434 | char* lbr = strrchr(alg, '['); |
435 | char* rbr = strrchr(alg, ']'); | |
a3612b81 | 436 | |
63bf2439 KZ |
437 | if (lbr != NULL && rbr != NULL && rbr - lbr > 1) |
438 | str = xstrndup(lbr + 1, rbr - lbr - 1); | |
a3612b81 LB |
439 | free(alg); |
440 | } | |
0624d840 KZ |
441 | break; |
442 | } | |
443 | case COL_MOUNTPOINT: | |
444 | { | |
445 | char path[PATH_MAX] = { '\0' }; | |
446 | int fl; | |
447 | ||
448 | check_mount_point(z->devname, &fl, path, sizeof(path)); | |
449 | if (*path) | |
450 | str = xstrdup(path); | |
451 | break; | |
452 | } | |
453 | case COL_STREAMS: | |
a6f0da16 | 454 | ul_path_read_string(sysfs, &str, "max_comp_streams"); |
0624d840 KZ |
455 | break; |
456 | case COL_ZEROPAGES: | |
5388c862 | 457 | str = get_mm_stat(z, MM_ZERO_PAGES, 1); |
0624d840 | 458 | break; |
bffc9174 KZ |
459 | case COL_ORIG_SIZE: |
460 | str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes); | |
461 | break; | |
462 | case COL_COMP_SIZE: | |
463 | str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes); | |
464 | break; | |
0624d840 | 465 | case COL_MEMTOTAL: |
5388c862 | 466 | str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes); |
0624d840 | 467 | break; |
bffc9174 KZ |
468 | case COL_MEMLIMIT: |
469 | str = get_mm_stat(z, MM_MEM_LIMIT, inbytes); | |
470 | break; | |
471 | case COL_MEMUSED: | |
472 | str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes); | |
473 | break; | |
474 | case COL_MIGRATED: | |
475 | str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes); | |
476 | break; | |
0624d840 | 477 | } |
780ce22c | 478 | if (str && scols_line_refer_data(ln, i, str)) |
699ad3a1 | 479 | err(EXIT_FAILURE, _("failed to add output data")); |
0624d840 KZ |
480 | } |
481 | } | |
482 | ||
483 | static void status(struct zram *z) | |
484 | { | |
485 | struct libscols_table *tb; | |
486 | size_t i; | |
e1b1c7b0 JS |
487 | DIR *dir; |
488 | struct dirent *d; | |
0624d840 KZ |
489 | |
490 | scols_init_debug(0); | |
491 | ||
492 | tb = scols_new_table(); | |
493 | if (!tb) | |
780ce22c | 494 | err(EXIT_FAILURE, _("failed to allocate output table")); |
0624d840 KZ |
495 | |
496 | scols_table_enable_raw(tb, raw); | |
497 | scols_table_enable_noheadings(tb, no_headings); | |
498 | ||
499 | for (i = 0; i < (size_t) ncolumns; i++) { | |
500 | const struct colinfo *col = get_column_info(i); | |
501 | ||
502 | if (!scols_table_new_column(tb, col->name, col->whint, col->flags)) | |
503 | err(EXIT_FAILURE, _("failed to initialize output column")); | |
504 | } | |
505 | ||
e1b1c7b0 JS |
506 | if (z) { |
507 | /* just one device specified */ | |
508 | fill_table_row(tb, z); | |
509 | goto print_table; | |
510 | } | |
0624d840 | 511 | |
e1b1c7b0 JS |
512 | /* list all used devices */ |
513 | z = new_zram(NULL); | |
514 | if (!(dir = opendir(_PATH_DEV))) | |
515 | err(EXIT_FAILURE, _("cannot open %s"), _PATH_DEV); | |
516 | ||
517 | while ((d = readdir(dir))) { | |
518 | int n; | |
519 | if (sscanf(d->d_name, "zram%d", &n) != 1) | |
520 | continue; | |
521 | zram_set_devname(z, NULL, n); | |
522 | if (zram_exist(z) && zram_used(z)) | |
523 | fill_table_row(tb, z); | |
0624d840 | 524 | } |
e1b1c7b0 JS |
525 | closedir(dir); |
526 | free_zram(z); | |
0624d840 | 527 | |
e1b1c7b0 | 528 | print_table: |
0624d840 KZ |
529 | scols_print_table(tb); |
530 | scols_unref_table(tb); | |
531 | } | |
532 | ||
86be6a32 | 533 | static void __attribute__((__noreturn__)) usage(void) |
0624d840 | 534 | { |
86be6a32 | 535 | FILE *out = stdout; |
0624d840 KZ |
536 | size_t i; |
537 | ||
538 | fputs(USAGE_HEADER, out); | |
539 | fprintf(out, _( " %1$s [options] <device>\n" | |
1c35e625 | 540 | " %1$s -r <device> [...]\n" |
0624d840 KZ |
541 | " %1$s [options] -f | <device> -s <size>\n"), |
542 | program_invocation_short_name); | |
543 | ||
451dbcfa BS |
544 | fputs(USAGE_SEPARATOR, out); |
545 | fputs(_("Set up and control zram devices.\n"), out); | |
546 | ||
0624d840 | 547 | fputs(USAGE_OPTIONS, out); |
a3612b81 | 548 | fputs(_(" -a, --algorithm lzo|lz4|lz4hc|deflate|842 compression algorithm to use\n"), out); |
0624d840 | 549 | fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out); |
1c35e625 | 550 | fputs(_(" -f, --find find a free device\n"), out); |
0624d840 KZ |
551 | fputs(_(" -n, --noheadings don't print headings\n"), out); |
552 | fputs(_(" -o, --output <list> columns to use for status output\n"), out); | |
2e7ccec7 | 553 | fputs(_(" --output-all output all columns\n"), out); |
1c35e625 | 554 | fputs(_(" --raw use raw status output format\n"), out); |
0624d840 KZ |
555 | fputs(_(" -r, --reset reset all specified devices\n"), out); |
556 | fputs(_(" -s, --size <size> device size\n"), out); | |
423c0d75 | 557 | fputs(_(" -t, --streams <number> number of compression streams\n"), out); |
0624d840 KZ |
558 | |
559 | fputs(USAGE_SEPARATOR, out); | |
f45f3ec3 | 560 | printf(USAGE_HELP_OPTIONS(27)); |
0624d840 | 561 | |
c3a4cfc5 | 562 | fputs(USAGE_COLUMNS, out); |
0624d840 KZ |
563 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
564 | fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); | |
565 | ||
f45f3ec3 | 566 | printf(USAGE_MAN_TAIL("zramctl(8)")); |
86be6a32 | 567 | exit(EXIT_SUCCESS); |
0624d840 KZ |
568 | } |
569 | ||
570 | /* actions */ | |
571 | enum { | |
572 | A_NONE = 0, | |
573 | A_STATUS, | |
574 | A_CREATE, | |
575 | A_FINDONLY, | |
576 | A_RESET | |
577 | }; | |
578 | ||
579 | int main(int argc, char **argv) | |
580 | { | |
581 | uintmax_t size = 0, nstreams = 0; | |
582 | char *algorithm = NULL; | |
583 | int rc = 0, c, find = 0, act = A_NONE; | |
584 | struct zram *zram = NULL; | |
585 | ||
2e7ccec7 SK |
586 | enum { |
587 | OPT_RAW = CHAR_MAX + 1, | |
588 | OPT_LIST_TYPES | |
589 | }; | |
0624d840 KZ |
590 | |
591 | static const struct option longopts[] = { | |
592 | { "algorithm", required_argument, NULL, 'a' }, | |
1c35e625 | 593 | { "bytes", no_argument, NULL, 'b' }, |
0624d840 KZ |
594 | { "find", no_argument, NULL, 'f' }, |
595 | { "help", no_argument, NULL, 'h' }, | |
0624d840 | 596 | { "output", required_argument, NULL, 'o' }, |
2e7ccec7 | 597 | { "output-all",no_argument, NULL, OPT_LIST_TYPES }, |
0624d840 KZ |
598 | { "noheadings",no_argument, NULL, 'n' }, |
599 | { "reset", no_argument, NULL, 'r' }, | |
600 | { "raw", no_argument, NULL, OPT_RAW }, | |
601 | { "size", required_argument, NULL, 's' }, | |
602 | { "streams", required_argument, NULL, 't' }, | |
603 | { "version", no_argument, NULL, 'V' }, | |
604 | { NULL, 0, NULL, 0 } | |
605 | }; | |
606 | ||
607 | static const ul_excl_t excl[] = { | |
608 | { 'f', 'o', 'r' }, | |
609 | { 'o', 'r', 's' }, | |
610 | { 0 } | |
611 | }; | |
612 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
613 | ||
614 | setlocale(LC_ALL, ""); | |
615 | bindtextdomain(PACKAGE, LOCALEDIR); | |
616 | textdomain(PACKAGE); | |
2c308875 | 617 | close_stdout_atexit(); |
0624d840 KZ |
618 | |
619 | while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) { | |
620 | ||
621 | err_exclusive_options(c, longopts, excl, excl_st); | |
622 | ||
623 | switch (c) { | |
624 | case 'a': | |
0624d840 KZ |
625 | algorithm = optarg; |
626 | break; | |
627 | case 'b': | |
628 | inbytes = 1; | |
629 | break; | |
630 | case 'f': | |
631 | find = 1; | |
632 | break; | |
633 | case 'o': | |
634 | ncolumns = string_to_idarray(optarg, | |
635 | columns, ARRAY_SIZE(columns), | |
636 | column_name_to_id); | |
637 | if (ncolumns < 0) | |
638 | return EXIT_FAILURE; | |
639 | break; | |
2e7ccec7 SK |
640 | case OPT_LIST_TYPES: |
641 | for (ncolumns = 0; (size_t)ncolumns < ARRAY_SIZE(infos); ncolumns++) | |
642 | columns[ncolumns] = ncolumns; | |
643 | break; | |
0624d840 KZ |
644 | case 's': |
645 | size = strtosize_or_err(optarg, _("failed to parse size")); | |
646 | act = A_CREATE; | |
647 | break; | |
648 | case 't': | |
649 | nstreams = strtou64_or_err(optarg, _("failed to parse streams")); | |
650 | break; | |
651 | case 'r': | |
652 | act = A_RESET; | |
653 | break; | |
654 | case OPT_RAW: | |
655 | raw = 1; | |
656 | break; | |
657 | case 'n': | |
658 | no_headings = 1; | |
659 | break; | |
2c308875 | 660 | |
0624d840 | 661 | case 'V': |
2c308875 | 662 | print_version(EXIT_SUCCESS); |
0624d840 | 663 | case 'h': |
86be6a32 | 664 | usage(); |
0624d840 | 665 | default: |
677ec86c | 666 | errtryhelp(EXIT_FAILURE); |
0624d840 KZ |
667 | } |
668 | } | |
669 | ||
670 | if (find && optind < argc) | |
671 | errx(EXIT_FAILURE, _("option --find is mutually exclusive " | |
1c35e625 | 672 | "with <device>")); |
0624d840 KZ |
673 | if (act == A_NONE) |
674 | act = find ? A_FINDONLY : A_STATUS; | |
675 | ||
676 | if (act != A_RESET && optind + 1 < argc) | |
929c7b28 | 677 | errx(EXIT_FAILURE, _("only one <device> at a time is allowed")); |
0624d840 | 678 | |
1fa6c3e0 SK |
679 | if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams)) |
680 | errx(EXIT_FAILURE, _("options --algorithm and --streams " | |
681 | "must be combined with --size")); | |
682 | ||
a6f0da16 KZ |
683 | ul_path_init_debug(); |
684 | ul_sysfs_init_debug(); | |
685 | ||
0624d840 KZ |
686 | switch (act) { |
687 | case A_STATUS: | |
0624d840 KZ |
688 | if (!ncolumns) { /* default columns */ |
689 | columns[ncolumns++] = COL_NAME; | |
690 | columns[ncolumns++] = COL_ALGORITHM; | |
691 | columns[ncolumns++] = COL_DISKSIZE; | |
692 | columns[ncolumns++] = COL_ORIG_SIZE; | |
693 | columns[ncolumns++] = COL_COMP_SIZE; | |
694 | columns[ncolumns++] = COL_MEMTOTAL; | |
695 | columns[ncolumns++] = COL_STREAMS; | |
696 | columns[ncolumns++] = COL_MOUNTPOINT; | |
697 | } | |
116c9ce2 | 698 | if (optind < argc) { |
0624d840 | 699 | zram = new_zram(argv[optind++]); |
116c9ce2 | 700 | if (!zram_exist(zram)) |
0a55b319 | 701 | err(EXIT_FAILURE, "%s", zram->devname); |
116c9ce2 | 702 | } |
0624d840 KZ |
703 | status(zram); |
704 | free_zram(zram); | |
705 | break; | |
706 | case A_RESET: | |
707 | if (optind == argc) | |
708 | errx(EXIT_FAILURE, _("no device specified")); | |
709 | while (optind < argc) { | |
710 | zram = new_zram(argv[optind]); | |
116c9ce2 KZ |
711 | if (!zram_exist(zram) |
712 | || zram_set_u64parm(zram, "reset", 1)) { | |
0624d840 KZ |
713 | warn(_("%s: failed to reset"), zram->devname); |
714 | rc = 1; | |
715 | } | |
62725810 | 716 | zram_control_remove(zram); |
0624d840 KZ |
717 | free_zram(zram); |
718 | optind++; | |
719 | } | |
720 | break; | |
721 | case A_FINDONLY: | |
722 | zram = find_free_zram(); | |
723 | if (!zram) | |
1c35e625 | 724 | errx(EXIT_FAILURE, _("no free zram device found")); |
0624d840 KZ |
725 | printf("%s\n", zram->devname); |
726 | free_zram(zram); | |
727 | break; | |
728 | case A_CREATE: | |
729 | if (find) { | |
730 | zram = find_free_zram(); | |
731 | if (!zram) | |
1c35e625 | 732 | errx(EXIT_FAILURE, _("no free zram device found")); |
0624d840 KZ |
733 | } else if (optind == argc) |
734 | errx(EXIT_FAILURE, _("no device specified")); | |
116c9ce2 | 735 | else { |
0624d840 | 736 | zram = new_zram(argv[optind]); |
116c9ce2 | 737 | if (!zram_exist(zram)) |
0a55b319 | 738 | err(EXIT_FAILURE, "%s", zram->devname); |
116c9ce2 | 739 | } |
0624d840 KZ |
740 | |
741 | if (zram_set_u64parm(zram, "reset", 1)) | |
742 | err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname); | |
743 | ||
744 | if (nstreams && | |
745 | zram_set_u64parm(zram, "max_comp_streams", nstreams)) | |
746 | err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname); | |
747 | ||
748 | if (algorithm && | |
749 | zram_set_strparm(zram, "comp_algorithm", algorithm)) | |
750 | err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname); | |
751 | ||
752 | if (zram_set_u64parm(zram, "disksize", size)) | |
753 | err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"), | |
754 | zram->devname, size); | |
755 | if (find) | |
756 | printf("%s\n", zram->devname); | |
757 | free_zram(zram); | |
758 | break; | |
759 | } | |
760 | ||
a6f0da16 | 761 | ul_unref_path(__control); |
0624d840 KZ |
762 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; |
763 | } |