]>
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]; | |
114 | struct sysfs_cxt sysfs; | |
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 KZ |
122 | static unsigned int raw, no_headings, inbytes; |
123 | ||
0624d840 KZ |
124 | static int get_column_id(int num) |
125 | { | |
126 | assert(num < ncolumns); | |
127 | assert(columns[num] < (int) ARRAY_SIZE(infos)); | |
128 | return columns[num]; | |
129 | } | |
130 | ||
131 | static const struct colinfo *get_column_info(int num) | |
132 | { | |
133 | return &infos[ get_column_id(num) ]; | |
134 | } | |
135 | ||
136 | static int column_name_to_id(const char *name, size_t namesz) | |
137 | { | |
138 | size_t i; | |
139 | ||
140 | for (i = 0; i < ARRAY_SIZE(infos); i++) { | |
141 | const char *cn = infos[i].name; | |
142 | ||
143 | if (!strncasecmp(name, cn, namesz) && !*(cn + namesz)) | |
144 | return i; | |
145 | } | |
146 | warnx(_("unknown column: %s"), name); | |
147 | return -1; | |
148 | } | |
149 | ||
1ceb4077 KZ |
150 | static void zram_reset_stat(struct zram *z) |
151 | { | |
152 | if (z) { | |
153 | strv_free(z->mm_stat); | |
154 | z->mm_stat = NULL; | |
155 | z->mm_stat_probed = 0; | |
156 | } | |
157 | } | |
158 | ||
0624d840 KZ |
159 | static void zram_set_devname(struct zram *z, const char *devname, size_t n) |
160 | { | |
161 | assert(z); | |
162 | ||
163 | if (!devname) | |
164 | snprintf(z->devname, sizeof(z->devname), "/dev/zram%zu", n); | |
165 | else { | |
166 | strncpy(z->devname, devname, sizeof(z->devname)); | |
167 | z->devname[sizeof(z->devname) - 1] = '\0'; | |
168 | } | |
169 | ||
170 | DBG(fprintf(stderr, "set devname: %s", z->devname)); | |
171 | sysfs_deinit(&z->sysfs); | |
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)); | |
201 | sysfs_deinit(&z->sysfs); | |
1ceb4077 | 202 | zram_reset_stat(z); |
0624d840 KZ |
203 | free(z); |
204 | } | |
205 | ||
206 | static struct sysfs_cxt *zram_get_sysfs(struct zram *z) | |
207 | { | |
208 | assert(z); | |
209 | ||
210 | if (!z->sysfs.devno) { | |
211 | dev_t devno = sysfs_devname_to_devno(z->devname, NULL); | |
212 | if (!devno) | |
213 | return NULL; | |
214 | if (sysfs_init(&z->sysfs, devno, NULL)) | |
215 | return NULL; | |
342436c6 | 216 | if (*z->devname != '/') { |
9e930041 | 217 | /* canonicalize the device name according to /sys */ |
342436c6 KZ |
218 | char name[PATH_MAX]; |
219 | if (sysfs_get_devname(&z->sysfs, name, sizeof(name))) | |
220 | snprintf(z->devname, sizeof(z->devname), "/dev/%s", name); | |
221 | } | |
0624d840 KZ |
222 | } |
223 | ||
224 | return &z->sysfs; | |
225 | } | |
226 | ||
227 | static inline int zram_exist(struct zram *z) | |
228 | { | |
229 | assert(z); | |
230 | ||
116c9ce2 KZ |
231 | errno = 0; |
232 | if (zram_get_sysfs(z) == NULL) { | |
233 | errno = ENODEV; | |
0624d840 | 234 | return 0; |
116c9ce2 | 235 | } |
0624d840 KZ |
236 | |
237 | DBG(fprintf(stderr, "%s exists", z->devname)); | |
238 | return 1; | |
239 | } | |
240 | ||
241 | static int zram_set_u64parm(struct zram *z, const char *attr, uint64_t num) | |
242 | { | |
243 | struct sysfs_cxt *sysfs = zram_get_sysfs(z); | |
244 | if (!sysfs) | |
245 | return -EINVAL; | |
246 | DBG(fprintf(stderr, "%s writing %ju to %s", z->devname, num, attr)); | |
247 | return sysfs_write_u64(sysfs, attr, num); | |
248 | } | |
249 | ||
250 | static int zram_set_strparm(struct zram *z, const char *attr, const char *str) | |
251 | { | |
252 | struct sysfs_cxt *sysfs = zram_get_sysfs(z); | |
253 | if (!sysfs) | |
254 | return -EINVAL; | |
255 | DBG(fprintf(stderr, "%s writing %s to %s", z->devname, str, attr)); | |
256 | return sysfs_write_string(sysfs, attr, str); | |
257 | } | |
258 | ||
259 | ||
260 | static int zram_used(struct zram *z) | |
261 | { | |
262 | uint64_t size; | |
263 | struct sysfs_cxt *sysfs = zram_get_sysfs(z); | |
264 | ||
265 | if (sysfs && | |
266 | sysfs_read_u64(sysfs, "disksize", &size) == 0 && | |
267 | size > 0) { | |
268 | ||
269 | DBG(fprintf(stderr, "%s used", z->devname)); | |
270 | return 1; | |
271 | } | |
272 | DBG(fprintf(stderr, "%s unused", z->devname)); | |
273 | return 0; | |
274 | } | |
275 | ||
62725810 KZ |
276 | static int zram_has_control(struct zram *z) |
277 | { | |
278 | if (!z->control_probed) { | |
279 | z->has_control = access(_PATH_SYS_CLASS "/zram-control/", F_OK) == 0 ? 1 : 0; | |
280 | z->control_probed = 1; | |
281 | DBG(fprintf(stderr, "zram-control: %s", z->has_control ? "yes" : "no")); | |
282 | } | |
283 | ||
284 | return z->has_control; | |
285 | } | |
286 | ||
287 | static int zram_control_add(struct zram *z) | |
288 | { | |
289 | int n; | |
290 | ||
291 | if (!zram_has_control(z)) | |
292 | return -ENOSYS; | |
293 | ||
294 | n = path_read_s32(_PATH_SYS_CLASS "/zram-control/hot_add"); | |
295 | if (n < 0) | |
296 | return n; | |
297 | ||
298 | DBG(fprintf(stderr, "hot-add: %d", n)); | |
299 | zram_set_devname(z, NULL, n); | |
300 | return 0; | |
301 | } | |
302 | ||
303 | static int zram_control_remove(struct zram *z) | |
304 | { | |
305 | char str[sizeof stringify_value(INT_MAX)]; | |
306 | int n; | |
307 | ||
308 | if (!zram_has_control(z)) | |
309 | return -ENOSYS; | |
310 | ||
311 | n = zram_get_devnum(z); | |
312 | if (n < 0) | |
313 | return n; | |
314 | ||
315 | DBG(fprintf(stderr, "hot-remove: %d", n)); | |
316 | snprintf(str, sizeof(str), "%d", n); | |
317 | return path_write_str(str, _PATH_SYS_CLASS "/zram-control/hot_remove"); | |
318 | } | |
319 | ||
0624d840 KZ |
320 | static struct zram *find_free_zram(void) |
321 | { | |
322 | struct zram *z = new_zram(NULL); | |
323 | size_t i; | |
324 | int isfree = 0; | |
325 | ||
326 | for (i = 0; isfree == 0; i++) { | |
327 | DBG(fprintf(stderr, "find free: checking zram%zu", i)); | |
328 | zram_set_devname(z, NULL, i); | |
62725810 | 329 | if (!zram_exist(z) && zram_control_add(z) != 0) |
0624d840 KZ |
330 | break; |
331 | isfree = !zram_used(z); | |
332 | } | |
333 | if (!isfree) { | |
334 | free_zram(z); | |
335 | z = NULL; | |
336 | } | |
337 | return z; | |
338 | } | |
339 | ||
624e147b | 340 | static char *get_mm_stat(struct zram *z, size_t idx, int bytes) |
5388c862 KZ |
341 | { |
342 | struct sysfs_cxt *sysfs; | |
343 | const char *name; | |
344 | uint64_t num; | |
345 | ||
346 | assert(idx < ARRAY_SIZE(mm_stat_names)); | |
347 | assert(z); | |
348 | ||
349 | sysfs = zram_get_sysfs(z); | |
350 | if (!sysfs) | |
351 | return NULL; | |
352 | ||
353 | /* Linux >= 4.1 uses /sys/block/zram<id>/mm_stat */ | |
354 | if (!z->mm_stat && !z->mm_stat_probed) { | |
355 | char *str; | |
356 | ||
357 | str = sysfs_strdup(sysfs, "mm_stat"); | |
358 | if (str) { | |
359 | z->mm_stat = strv_split(str, " "); | |
2546d54b KZ |
360 | |
361 | /* make sure kernel provides mm_stat as expected */ | |
362 | if (strv_length(z->mm_stat) < ARRAY_SIZE(mm_stat_names)) { | |
363 | strv_free(z->mm_stat); | |
364 | z->mm_stat = NULL; | |
365 | } | |
5388c862 KZ |
366 | } |
367 | z->mm_stat_probed = 1; | |
368 | free(str); | |
369 | ||
370 | } | |
371 | ||
372 | if (z->mm_stat) { | |
624e147b | 373 | if (bytes) |
5388c862 KZ |
374 | return xstrdup(z->mm_stat[idx]); |
375 | ||
376 | num = strtou64_or_err(z->mm_stat[idx], _("Failed to parse mm_stat")); | |
377 | return size_to_human_string(SIZE_SUFFIX_1LETTER, num); | |
378 | } | |
379 | ||
380 | /* Linux < 4.1 uses /sys/block/zram<id>/<attrname> */ | |
381 | name = mm_stat_names[idx]; | |
624e147b | 382 | if (bytes) |
5388c862 KZ |
383 | return sysfs_strdup(sysfs, name); |
384 | else if (sysfs_read_u64(sysfs, name, &num) == 0) | |
385 | return size_to_human_string(SIZE_SUFFIX_1LETTER, num); | |
386 | return NULL; | |
387 | } | |
388 | ||
0624d840 KZ |
389 | static void fill_table_row(struct libscols_table *tb, struct zram *z) |
390 | { | |
391 | static struct libscols_line *ln; | |
392 | struct sysfs_cxt *sysfs; | |
393 | size_t i; | |
394 | uint64_t num; | |
395 | ||
396 | assert(tb); | |
397 | assert(z); | |
398 | ||
399 | DBG(fprintf(stderr, "%s: filling status table", z->devname)); | |
400 | ||
401 | sysfs = zram_get_sysfs(z); | |
402 | if (!sysfs) | |
403 | return; | |
404 | ||
405 | ln = scols_table_new_line(tb, NULL); | |
406 | if (!ln) | |
780ce22c | 407 | err(EXIT_FAILURE, _("failed to allocate output line")); |
0624d840 KZ |
408 | |
409 | for (i = 0; i < (size_t) ncolumns; i++) { | |
410 | char *str = NULL; | |
411 | ||
412 | switch (get_column_id(i)) { | |
413 | case COL_NAME: | |
414 | str = xstrdup(z->devname); | |
415 | break; | |
416 | case COL_DISKSIZE: | |
417 | if (inbytes) | |
418 | str = sysfs_strdup(sysfs, "disksize"); | |
419 | else if (sysfs_read_u64(sysfs, "disksize", &num) == 0) | |
420 | str = size_to_human_string(SIZE_SUFFIX_1LETTER, num); | |
421 | break; | |
0624d840 KZ |
422 | case COL_ALGORITHM: |
423 | { | |
424 | char *alg = sysfs_strdup(sysfs, "comp_algorithm"); | |
425 | if (!alg) | |
426 | break; | |
427 | if (strstr(alg, "[lzo]") == NULL) { | |
428 | if (strstr(alg, "[lz4]") == NULL) | |
429 | ; | |
430 | else | |
431 | str = xstrdup("lz4"); | |
432 | } else | |
433 | str = xstrdup("lzo"); | |
434 | free(alg); | |
435 | break; | |
436 | } | |
437 | case COL_MOUNTPOINT: | |
438 | { | |
439 | char path[PATH_MAX] = { '\0' }; | |
440 | int fl; | |
441 | ||
442 | check_mount_point(z->devname, &fl, path, sizeof(path)); | |
443 | if (*path) | |
444 | str = xstrdup(path); | |
445 | break; | |
446 | } | |
447 | case COL_STREAMS: | |
448 | str = sysfs_strdup(sysfs, "max_comp_streams"); | |
449 | break; | |
450 | case COL_ZEROPAGES: | |
5388c862 | 451 | str = get_mm_stat(z, MM_ZERO_PAGES, 1); |
0624d840 | 452 | break; |
bffc9174 KZ |
453 | case COL_ORIG_SIZE: |
454 | str = get_mm_stat(z, MM_ORIG_DATA_SIZE, inbytes); | |
455 | break; | |
456 | case COL_COMP_SIZE: | |
457 | str = get_mm_stat(z, MM_COMPR_DATA_SIZE, inbytes); | |
458 | break; | |
0624d840 | 459 | case COL_MEMTOTAL: |
5388c862 | 460 | str = get_mm_stat(z, MM_MEM_USED_TOTAL, inbytes); |
0624d840 | 461 | break; |
bffc9174 KZ |
462 | case COL_MEMLIMIT: |
463 | str = get_mm_stat(z, MM_MEM_LIMIT, inbytes); | |
464 | break; | |
465 | case COL_MEMUSED: | |
466 | str = get_mm_stat(z, MM_MEM_USED_MAX, inbytes); | |
467 | break; | |
468 | case COL_MIGRATED: | |
469 | str = get_mm_stat(z, MM_NUM_MIGRATED, inbytes); | |
470 | break; | |
0624d840 | 471 | } |
780ce22c | 472 | if (str && scols_line_refer_data(ln, i, str)) |
699ad3a1 | 473 | err(EXIT_FAILURE, _("failed to add output data")); |
0624d840 KZ |
474 | } |
475 | } | |
476 | ||
477 | static void status(struct zram *z) | |
478 | { | |
479 | struct libscols_table *tb; | |
480 | size_t i; | |
e1b1c7b0 JS |
481 | DIR *dir; |
482 | struct dirent *d; | |
0624d840 KZ |
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 | ||
e1b1c7b0 JS |
500 | if (z) { |
501 | /* just one device specified */ | |
502 | fill_table_row(tb, z); | |
503 | goto print_table; | |
504 | } | |
0624d840 | 505 | |
e1b1c7b0 JS |
506 | /* list all used devices */ |
507 | z = new_zram(NULL); | |
508 | if (!(dir = opendir(_PATH_DEV))) | |
509 | err(EXIT_FAILURE, _("cannot open %s"), _PATH_DEV); | |
510 | ||
511 | while ((d = readdir(dir))) { | |
512 | int n; | |
513 | if (sscanf(d->d_name, "zram%d", &n) != 1) | |
514 | continue; | |
515 | zram_set_devname(z, NULL, n); | |
516 | if (zram_exist(z) && zram_used(z)) | |
517 | fill_table_row(tb, z); | |
0624d840 | 518 | } |
e1b1c7b0 JS |
519 | closedir(dir); |
520 | free_zram(z); | |
0624d840 | 521 | |
e1b1c7b0 | 522 | print_table: |
0624d840 KZ |
523 | scols_print_table(tb); |
524 | scols_unref_table(tb); | |
525 | } | |
526 | ||
86be6a32 | 527 | static void __attribute__((__noreturn__)) usage(void) |
0624d840 | 528 | { |
86be6a32 | 529 | FILE *out = stdout; |
0624d840 KZ |
530 | size_t i; |
531 | ||
532 | fputs(USAGE_HEADER, out); | |
533 | fprintf(out, _( " %1$s [options] <device>\n" | |
1c35e625 | 534 | " %1$s -r <device> [...]\n" |
0624d840 KZ |
535 | " %1$s [options] -f | <device> -s <size>\n"), |
536 | program_invocation_short_name); | |
537 | ||
451dbcfa BS |
538 | fputs(USAGE_SEPARATOR, out); |
539 | fputs(_("Set up and control zram devices.\n"), out); | |
540 | ||
0624d840 | 541 | fputs(USAGE_OPTIONS, out); |
1c35e625 | 542 | fputs(_(" -a, --algorithm lzo|lz4 compression algorithm to use\n"), out); |
0624d840 | 543 | fputs(_(" -b, --bytes print sizes in bytes rather than in human readable format\n"), out); |
1c35e625 | 544 | fputs(_(" -f, --find find a free device\n"), out); |
0624d840 KZ |
545 | fputs(_(" -n, --noheadings don't print headings\n"), out); |
546 | fputs(_(" -o, --output <list> columns to use for status output\n"), out); | |
1c35e625 | 547 | fputs(_(" --raw use raw status output format\n"), out); |
0624d840 KZ |
548 | fputs(_(" -r, --reset reset all specified devices\n"), out); |
549 | fputs(_(" -s, --size <size> device size\n"), out); | |
423c0d75 | 550 | fputs(_(" -t, --streams <number> number of compression streams\n"), out); |
0624d840 KZ |
551 | |
552 | fputs(USAGE_SEPARATOR, out); | |
f45f3ec3 | 553 | printf(USAGE_HELP_OPTIONS(27)); |
0624d840 | 554 | |
c3a4cfc5 | 555 | fputs(USAGE_COLUMNS, out); |
0624d840 KZ |
556 | for (i = 0; i < ARRAY_SIZE(infos); i++) |
557 | fprintf(out, " %11s %s\n", infos[i].name, _(infos[i].help)); | |
558 | ||
f45f3ec3 | 559 | printf(USAGE_MAN_TAIL("zramctl(8)")); |
86be6a32 | 560 | exit(EXIT_SUCCESS); |
0624d840 KZ |
561 | } |
562 | ||
563 | /* actions */ | |
564 | enum { | |
565 | A_NONE = 0, | |
566 | A_STATUS, | |
567 | A_CREATE, | |
568 | A_FINDONLY, | |
569 | A_RESET | |
570 | }; | |
571 | ||
572 | int main(int argc, char **argv) | |
573 | { | |
574 | uintmax_t size = 0, nstreams = 0; | |
575 | char *algorithm = NULL; | |
576 | int rc = 0, c, find = 0, act = A_NONE; | |
577 | struct zram *zram = NULL; | |
578 | ||
579 | enum { OPT_RAW = CHAR_MAX + 1 }; | |
580 | ||
581 | static const struct option longopts[] = { | |
582 | { "algorithm", required_argument, NULL, 'a' }, | |
1c35e625 | 583 | { "bytes", no_argument, NULL, 'b' }, |
0624d840 KZ |
584 | { "find", no_argument, NULL, 'f' }, |
585 | { "help", no_argument, NULL, 'h' }, | |
0624d840 KZ |
586 | { "output", required_argument, NULL, 'o' }, |
587 | { "noheadings",no_argument, NULL, 'n' }, | |
588 | { "reset", no_argument, NULL, 'r' }, | |
589 | { "raw", no_argument, NULL, OPT_RAW }, | |
590 | { "size", required_argument, NULL, 's' }, | |
591 | { "streams", required_argument, NULL, 't' }, | |
592 | { "version", no_argument, NULL, 'V' }, | |
593 | { NULL, 0, NULL, 0 } | |
594 | }; | |
595 | ||
596 | static const ul_excl_t excl[] = { | |
597 | { 'f', 'o', 'r' }, | |
598 | { 'o', 'r', 's' }, | |
599 | { 0 } | |
600 | }; | |
601 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
602 | ||
603 | setlocale(LC_ALL, ""); | |
604 | bindtextdomain(PACKAGE, LOCALEDIR); | |
605 | textdomain(PACKAGE); | |
606 | atexit(close_stdout); | |
607 | ||
608 | while ((c = getopt_long(argc, argv, "a:bfho:nrs:t:V", longopts, NULL)) != -1) { | |
609 | ||
610 | err_exclusive_options(c, longopts, excl, excl_st); | |
611 | ||
612 | switch (c) { | |
613 | case 'a': | |
614 | if (strcmp(optarg,"lzo") && strcmp(optarg,"lz4")) | |
615 | errx(EXIT_FAILURE, _("unsupported algorithm: %s"), | |
616 | optarg); | |
617 | algorithm = optarg; | |
618 | break; | |
619 | case 'b': | |
620 | inbytes = 1; | |
621 | break; | |
622 | case 'f': | |
623 | find = 1; | |
624 | break; | |
625 | case 'o': | |
626 | ncolumns = string_to_idarray(optarg, | |
627 | columns, ARRAY_SIZE(columns), | |
628 | column_name_to_id); | |
629 | if (ncolumns < 0) | |
630 | return EXIT_FAILURE; | |
631 | break; | |
632 | case 's': | |
633 | size = strtosize_or_err(optarg, _("failed to parse size")); | |
634 | act = A_CREATE; | |
635 | break; | |
636 | case 't': | |
637 | nstreams = strtou64_or_err(optarg, _("failed to parse streams")); | |
638 | break; | |
639 | case 'r': | |
640 | act = A_RESET; | |
641 | break; | |
642 | case OPT_RAW: | |
643 | raw = 1; | |
644 | break; | |
645 | case 'n': | |
646 | no_headings = 1; | |
647 | break; | |
648 | case 'V': | |
649 | printf(UTIL_LINUX_VERSION); | |
650 | return EXIT_SUCCESS; | |
651 | case 'h': | |
86be6a32 | 652 | usage(); |
0624d840 | 653 | default: |
677ec86c | 654 | errtryhelp(EXIT_FAILURE); |
0624d840 KZ |
655 | } |
656 | } | |
657 | ||
658 | if (find && optind < argc) | |
659 | errx(EXIT_FAILURE, _("option --find is mutually exclusive " | |
1c35e625 | 660 | "with <device>")); |
0624d840 KZ |
661 | if (act == A_NONE) |
662 | act = find ? A_FINDONLY : A_STATUS; | |
663 | ||
664 | if (act != A_RESET && optind + 1 < argc) | |
929c7b28 | 665 | errx(EXIT_FAILURE, _("only one <device> at a time is allowed")); |
0624d840 | 666 | |
1fa6c3e0 SK |
667 | if ((act == A_STATUS || act == A_FINDONLY) && (algorithm || nstreams)) |
668 | errx(EXIT_FAILURE, _("options --algorithm and --streams " | |
669 | "must be combined with --size")); | |
670 | ||
0624d840 KZ |
671 | switch (act) { |
672 | case A_STATUS: | |
0624d840 KZ |
673 | if (!ncolumns) { /* default columns */ |
674 | columns[ncolumns++] = COL_NAME; | |
675 | columns[ncolumns++] = COL_ALGORITHM; | |
676 | columns[ncolumns++] = COL_DISKSIZE; | |
677 | columns[ncolumns++] = COL_ORIG_SIZE; | |
678 | columns[ncolumns++] = COL_COMP_SIZE; | |
679 | columns[ncolumns++] = COL_MEMTOTAL; | |
680 | columns[ncolumns++] = COL_STREAMS; | |
681 | columns[ncolumns++] = COL_MOUNTPOINT; | |
682 | } | |
116c9ce2 | 683 | if (optind < argc) { |
0624d840 | 684 | zram = new_zram(argv[optind++]); |
116c9ce2 | 685 | if (!zram_exist(zram)) |
0a55b319 | 686 | err(EXIT_FAILURE, "%s", zram->devname); |
116c9ce2 | 687 | } |
0624d840 KZ |
688 | status(zram); |
689 | free_zram(zram); | |
690 | break; | |
691 | case A_RESET: | |
692 | if (optind == argc) | |
693 | errx(EXIT_FAILURE, _("no device specified")); | |
694 | while (optind < argc) { | |
695 | zram = new_zram(argv[optind]); | |
116c9ce2 KZ |
696 | if (!zram_exist(zram) |
697 | || zram_set_u64parm(zram, "reset", 1)) { | |
0624d840 KZ |
698 | warn(_("%s: failed to reset"), zram->devname); |
699 | rc = 1; | |
700 | } | |
62725810 | 701 | zram_control_remove(zram); |
0624d840 KZ |
702 | free_zram(zram); |
703 | optind++; | |
704 | } | |
705 | break; | |
706 | case A_FINDONLY: | |
707 | zram = find_free_zram(); | |
708 | if (!zram) | |
1c35e625 | 709 | errx(EXIT_FAILURE, _("no free zram device found")); |
0624d840 KZ |
710 | printf("%s\n", zram->devname); |
711 | free_zram(zram); | |
712 | break; | |
713 | case A_CREATE: | |
714 | if (find) { | |
715 | zram = find_free_zram(); | |
716 | if (!zram) | |
1c35e625 | 717 | errx(EXIT_FAILURE, _("no free zram device found")); |
0624d840 KZ |
718 | } else if (optind == argc) |
719 | errx(EXIT_FAILURE, _("no device specified")); | |
116c9ce2 | 720 | else { |
0624d840 | 721 | zram = new_zram(argv[optind]); |
116c9ce2 | 722 | if (!zram_exist(zram)) |
0a55b319 | 723 | err(EXIT_FAILURE, "%s", zram->devname); |
116c9ce2 | 724 | } |
0624d840 KZ |
725 | |
726 | if (zram_set_u64parm(zram, "reset", 1)) | |
727 | err(EXIT_FAILURE, _("%s: failed to reset"), zram->devname); | |
728 | ||
729 | if (nstreams && | |
730 | zram_set_u64parm(zram, "max_comp_streams", nstreams)) | |
731 | err(EXIT_FAILURE, _("%s: failed to set number of streams"), zram->devname); | |
732 | ||
733 | if (algorithm && | |
734 | zram_set_strparm(zram, "comp_algorithm", algorithm)) | |
735 | err(EXIT_FAILURE, _("%s: failed to set algorithm"), zram->devname); | |
736 | ||
737 | if (zram_set_u64parm(zram, "disksize", size)) | |
738 | err(EXIT_FAILURE, _("%s: failed to set disksize (%ju bytes)"), | |
739 | zram->devname, size); | |
740 | if (find) | |
741 | printf("%s\n", zram->devname); | |
742 | free_zram(zram); | |
743 | break; | |
744 | } | |
745 | ||
746 | return rc ? EXIT_FAILURE : EXIT_SUCCESS; | |
747 | } |