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