]> git.sesse.net Git - bcachefs-tools-debian/blob - bcacheadm.c
b321c98092cab078978c1e77c8809a90042f64b0
[bcachefs-tools-debian] / bcacheadm.c
1 /*
2  * Authors: Kent Overstreet <kmo@daterainc.com>
3  *          Gabriel de Perthuis <g2p.code@gmail.com>
4  *          Jacob Malevich <jam@datera.io>
5  *
6  * GPLv2
7  */
8
9 #include <nih/option.h>
10 #include <nih/command.h>
11 #include <nih/main.h>
12 #include <nih/logging.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <ctype.h>
16 #include <errno.h>
17 #include <inttypes.h>
18 #include <limits.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <blkid.h>
24 #include <string.h>
25 #include <linux/fs.h>
26 #include <sys/ioctl.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <uuid/uuid.h>
30 #include <dirent.h>
31 #include <bcache.h> //libbcache
32
33 #define PACKAGE_NAME "bcacheadm"
34 #define PACKAGE_VERSION "1.0"
35 #define PACKAGE_BUGREPORT "bugreport"
36
37 #define MAX_DEVS MAX_CACHES_PER_SET
38
39
40 /* make-bcache globals */
41 int bdev = -1;
42 int devs = 0;
43 char *cache_devices[MAX_DEVS];
44 int tier_mapping[MAX_DEVS];
45 char *backing_devices[MAX_DEVS];
46 char *backing_dev_labels[MAX_DEVS];
47 size_t i, nr_backing_devices = 0, nr_cache_devices = 0;
48 unsigned block_size = 0;
49 unsigned bucket_sizes[MAX_DEVS];
50 int num_bucket_sizes = 0;
51 int writeback = 0, discard = 0, wipe_bcache = 0;
52 unsigned replication_set = 0, replacement_policy = 0;
53 uint64_t data_offset = BDEV_DATA_START_DEFAULT;
54 char *label = NULL;
55 struct cache_sb *cache_set_sb = NULL;
56 const char *cache_set_uuid = 0;
57 const char *csum_type = 0;
58 char *metadata_replicas = 0;
59 char *data_replicas = 0;
60 char *tier = 0;
61
62 /* add-dev globals */
63 char *add_dev_uuid = NULL;
64
65 /* rm-dev globals */
66 bool force_remove = false;
67
68 /* Modify globals */
69 bool modify_list_attrs = false;
70 static const char *modify_set_uuid = NULL;
71 static const char *modify_dev_uuid = NULL;
72
73 /* query-dev globals */
74 bool force_csum = false;
75 bool uuid_only = false;
76 bool query_brief = false;
77
78 /* probe globals */
79 bool udev = false;
80
81 /* list globals */
82 char *cset_dir = "/sys/fs/bcache";
83 bool list_devs = false;
84
85 /* status globals */
86 bool status_all = false;
87
88 /* stats globals */
89 bool stats_all = false;
90 bool stats_list = false;
91 static const char *stats_uuid = NULL;
92 static const char *stats_dev_uuid = NULL;
93 static const char *stats_cache_num = NULL;
94 bool stats_five_min = false;
95 bool stats_hour = false;
96 bool stats_day = false;
97 bool stats_total = false;
98
99 /* make-bcache option setters */
100 static int set_block_size(NihOption *option, const char *arg)
101 {
102         block_size = hatoi_validate(arg, "block size");
103         return 0;
104 }
105
106 static int set_cache(NihOption *option, const char *arg)
107 {
108         bdev = 0;
109         cache_devices[nr_cache_devices] = strdup(arg);
110         if(!tier)
111                 tier_mapping[nr_cache_devices] = 0;
112         else {
113                 int ntier = atoi(tier);
114                 if(ntier == 0 || ntier == 1)
115                         tier_mapping[nr_cache_devices] = ntier;
116                 else
117                         printf("Invalid tier\n");
118         }
119
120         devs++;
121         nr_cache_devices++;
122
123         return 0;
124 }
125
126 static int set_bdev(NihOption *option, const char *arg)
127 {
128         bdev = 1;
129
130         if(label)
131                 backing_dev_labels[nr_backing_devices] = strdup(label);
132
133         backing_devices[nr_backing_devices] = strdup(arg);
134
135         nr_backing_devices++;
136         devs++;
137
138         return 0;
139 }
140
141 static int set_bucket_sizes(NihOption *option, const char *arg)
142 {
143         bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
144         num_bucket_sizes++;
145         return 0;
146 }
147
148 /* probe setters */
149 static int set_udev(NihOption *option, const char *arg)
150 {
151         if (strcmp("udev", arg)) {
152                 printf("Invalid output format %s\n", arg);
153                 exit(EXIT_FAILURE);
154         }
155         udev = true;
156         return 0;
157 }
158
159
160 /* options */
161 static NihOption make_bcache_options[] = {
162 //      {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
163         {'C', "cache",  N_("Format a cache device"), NULL, "dev", NULL, set_cache},
164         {'B', "bdev",   N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
165         {'l', "label",  N_("label"), NULL, "label", &label, NULL},
166         //Only one bucket_size supported until a list of bucket sizes is parsed correctly
167         {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
168         //Does the default setter automatically convert strings to an int?
169         {'w', "block",  N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
170         {'t', "tier",   N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
171         {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
172         {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
173
174         {0, "cset-uuid",        N_("UUID for the cache set"),           NULL,   "uuid", &cache_set_uuid, NULL},
175         {0, "csum-type",        N_("One of (none|crc32c|crc64)"),               NULL,   "type", &csum_type, NULL },
176         {0, "replication-set",N_("replication set of subsequent devices"),      NULL,   NULL, &replication_set, NULL },
177         {0, "meta-replicas",N_("number of metadata replicas"),          NULL,   "#", &metadata_replicas, NULL},
178         {0, "data-replicas",N_("number of data replicas"),              NULL,   "#", &data_replicas, NULL },
179
180         {0, "wipe-bcache",      N_("destroy existing bcache data if present"),          NULL, NULL, &wipe_bcache, NULL},
181         {0, "discard",          N_("enable discards"),          NULL, NULL, &discard,           NULL},
182         {0, "writeback",        N_("enable writeback"),         NULL, NULL, &writeback,         NULL},
183
184         NIH_OPTION_LAST
185 };
186
187 static NihOption probe_bcache_options[] = {
188         {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
189         NIH_OPTION_LAST
190 };
191
192 static NihOption bcache_register_options[] = {
193         NIH_OPTION_LAST
194 };
195
196 static NihOption bcache_unregister_options[] = {
197         NIH_OPTION_LAST
198 };
199
200 static NihOption bcache_add_device_options[] = {
201         {'u', "set", N_("cacheset uuid"), NULL, "UUID", &add_dev_uuid, NULL},
202         NIH_OPTION_LAST
203 };
204
205 static NihOption bcache_rm_device_options[] = {
206         {'f', "force", N_("force cache removal"), NULL, NULL, &force_remove, NULL},
207         NIH_OPTION_LAST
208 };
209
210 static NihOption bcache_modify_options[] = {
211         {'l', "list", N_("list attributes"), NULL, NULL, &modify_list_attrs, NULL},
212         {'u', "set", N_("cacheset uuid"), NULL, "UUID", &modify_set_uuid, NULL},
213         {'d', "dev", N_("device uuid"), NULL, "UUID", &modify_dev_uuid, NULL},
214         NIH_OPTION_LAST
215 };
216
217 static NihOption query_devs_options[] = {
218         {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
219         {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
220         {'b', "brief", N_("only print out the cluster,server,and disk uuids"), NULL, NULL, &query_brief, NULL},
221         NIH_OPTION_LAST
222 };
223
224 static NihOption list_cachesets_options[] = {
225         {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
226         {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
227         NIH_OPTION_LAST
228 };
229
230 static NihOption status_options[] = {
231         {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
232         NIH_OPTION_LAST
233 };
234
235 static NihOption stats_options[] = {
236         {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
237         {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
238         {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
239         {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
240         {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
241         {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
242         {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
243         {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
244         {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
245         NIH_OPTION_LAST
246 };
247
248 static NihOption options[] = {
249         NIH_OPTION_LAST
250 };
251
252
253 /* commands */
254 int make_bcache(NihCommand *command, char *const *args)
255 {
256         int cache_dev_fd[devs];
257
258         int backing_dev_fd[devs];
259
260         cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
261                                      sizeof(struct cache_member) * devs);
262
263         if (cache_set_uuid) {
264                 if(uuid_parse(cache_set_uuid, cache_set_sb->set_uuid.b)) {
265                         fprintf(stderr, "Bad uuid\n");
266                         return -1;
267                 }
268         } else {
269                 uuid_generate(cache_set_sb->set_uuid.b);
270         }
271
272         if (csum_type) {
273                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
274                                 read_string_list_or_die(csum_type, csum_types,
275                                         "csum type"));
276         } else {
277                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
278         }
279
280         if (metadata_replicas) {
281                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
282                                 strtoul_or_die(metadata_replicas,
283                                         CACHE_SET_META_REPLICAS_WANT_MAX,
284                                         "meta replicas"));
285         } else {
286                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
287         }
288
289         if (data_replicas) {
290                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
291                         strtoul_or_die(data_replicas,
292                                 CACHE_SET_DATA_REPLICAS_WANT_MAX,
293                                 "data replicas"));
294         } else {
295                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
296         }
297
298         if (bdev == -1) {
299                 fprintf(stderr, "Please specify -C or -B\n");
300                 exit(EXIT_FAILURE);
301         }
302
303         if(!bucket_sizes[0]) bucket_sizes[0] = 1024;
304
305         for(i = 0; i < nr_cache_devices; i++)
306                 next_cache_device(cache_set_sb,
307                                   replication_set,
308                                   tier_mapping[i],
309                                   replacement_policy,
310                                   discard);
311
312         if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
313                 fprintf(stderr, "Please supply a device\n");
314                 exit(EXIT_FAILURE);
315         }
316
317         i = 0;
318         do {
319                 if (bucket_sizes[i] < block_size) {
320                         fprintf(stderr,
321                         "Bucket size cannot be smaller than block size\n");
322                         exit(EXIT_FAILURE);
323                 }
324                 i++;
325         } while (i < num_bucket_sizes);
326
327         if (!block_size) {
328                 for (i = 0; i < cache_set_sb->nr_in_set; i++)
329                         block_size = max(block_size,
330                                          get_blocksize(cache_devices[i]));
331
332                 for (i = 0; i < nr_backing_devices; i++)
333                         block_size = max(block_size,
334                                          get_blocksize(backing_devices[i]));
335         }
336
337         for (i = 0; i < cache_set_sb->nr_in_set; i++)
338                 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
339
340         for (i = 0; i < nr_backing_devices; i++)
341                 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
342
343         write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
344                                         bucket_sizes, num_bucket_sizes);
345
346         for (i = 0; i < nr_backing_devices; i++)
347                 write_backingdev_sb(backing_dev_fd[i],
348                                     block_size, bucket_sizes,
349                                     writeback, data_offset,
350                                     backing_dev_labels[i],
351                                     cache_set_sb->set_uuid);
352
353
354         return 0;
355 }
356
357 int probe_bcache(NihCommand *command, char *const *args)
358 {
359         int i;
360         char *err = NULL;
361
362         for (i = 0; args[i] != NULL; i++) {
363                 err = probe(args[i], udev);
364                 if(err) {
365                         printf("probe_bcache error: %s\n", err);
366                         return -1;
367                 }
368         }
369
370         return 0;
371 }
372
373 int bcache_register(NihCommand *command, char *const *args)
374 {
375         char *err = NULL;
376
377         err = register_bcache(args);
378         if (err) {
379                 printf("bcache_register error: %s\n", err);
380                 return -1;
381         }
382
383         return 0;
384 }
385
386 int bcache_unregister(NihCommand *command, char *const *args)
387 {
388         char *err = NULL;
389
390         err = unregister_bcache(args);
391         if (err) {
392                 printf("bcache_unregister error: %s\n", err);
393                 return -1;
394         }
395
396         return 0;
397 }
398
399 int bcache_add_devices(NihCommand *command, char *const *args)
400 {
401         char *err;
402
403         if (!add_dev_uuid) {
404                 printf("Must specify a cacheset uuid to add the disk to\n");
405                 return -1;
406         }
407
408         err = add_devices(args, add_dev_uuid);
409         if (err) {
410                 printf("bcache_add_devices error: %s\n", err);
411                 return -1;
412         }
413
414         return 0;
415 }
416
417 int bcache_rm_device(NihCommand *command, char *const *args)
418 {
419         char *err;
420
421         err = remove_device(args[0], force_remove);
422         if (err) {
423                 printf("bcache_rm_devices error: %s\n", err);
424                 return -1;
425         }
426
427         return 0;
428 }
429
430 int bcache_modify(NihCommand *command, char *const *args)
431 {
432         char *err;
433         char path[MAX_PATH];
434         DIR *path_dir;
435         struct stat cache_stat;
436         char *attr = args[0];
437         char *val = NULL;
438         int fd = -1;
439
440         if (modify_list_attrs) {
441                 sysfs_attr_list();
442                 return 0;
443         }
444
445         if (!modify_set_uuid) {
446                 printf("Must provide a cacheset uuid\n");
447                 return -1;
448         }
449
450         snprintf(path, MAX_PATH, "%s/%s", cset_dir, modify_set_uuid);
451
452         if(!attr) {
453                 printf("Must provide the name of an attribute to modify\n");
454                 goto err;
455         }
456
457         enum sysfs_attr type = sysfs_attr_type(attr);
458
459         if (type == -1)
460                 goto err;
461         else if(type == INTERNAL_ATTR)
462                 strcat(path, "/internal");
463         else if(type == CACHE_ATTR) {
464                 if(modify_dev_uuid) {
465                         /* searches all cache# for a matching uuid,
466                          * path gets modified to the correct cache path */
467                         char subdir[10] = "/cache";
468                         err = find_matching_uuid(path, subdir,
469                                         modify_dev_uuid);
470                         if (err) {
471                                 printf("Failed to find "
472                                         "matching dev %s\n", err);
473                                 goto err;
474                         } else {
475                                 strcat(path, subdir);
476                         }
477                 } else {
478                         printf("Must provide a device uuid\n");
479                 }
480         }
481         /* SET_ATTRs are just in the current dir */
482
483         strcat(path, "/");
484         strcat(path, attr);
485
486         val = args[1];
487         if (!val) {
488                 printf("Must provide a value to change the attribute to\n");
489                 goto err;
490         }
491
492         fd = open(path, O_WRONLY);
493         if (fd < 0) {
494                 printf("Unable to open modify attr with path %s\n", path);
495                 goto err;
496         }
497
498         write(fd, val, strlen(val));
499
500 err:
501         if(fd)
502                 close(fd);
503         return 0;
504 }
505
506 int bcache_list_cachesets(NihCommand *command, char *const *args)
507 {
508         char *err = NULL;
509         err = list_cachesets(cset_dir, list_devs);
510         if (err) {
511                 printf("bcache_list_cachesets error :%s\n", err);
512                 return -1;
513         }
514
515         return 0;
516 }
517
518 int bcache_query_devs(NihCommand *command, char *const *args)
519 {
520         int i;
521
522         if (query_brief)
523                 printf("%-10s%-40s%-40s%-40s\n", "dev name", "disk uuid",
524                                 "server uuid", "cluster uuid");
525
526         for (i = 0; args[i] != NULL; i++) {
527                 char dev_uuid[40];
528                 struct cache_sb *sb = query_dev(args[i], force_csum,
529                                 !query_brief, uuid_only, dev_uuid);
530
531                 if (!sb) {
532                         printf("error opening the superblock for %s\n",
533                                         args[i]);
534                         return -1;
535                 }
536
537                 if (uuid_only) {
538                         printf("%s\n", dev_uuid);
539                 } else if (query_brief) {
540                         char set_uuid_str[40], dev_uuid_str[40];
541                         char *clus_uuid = (char *)sb->label;
542
543                         uuid_unparse(sb->set_uuid.b, set_uuid_str);
544                         uuid_unparse(sb->uuid.b, dev_uuid_str);
545                         if (!strcmp(clus_uuid, ""))
546                                 clus_uuid = "None";
547
548                         printf("%-10s%-40s%-40s%-40s\n", args[i],
549                                         dev_uuid_str,
550                                         set_uuid_str,
551                                         clus_uuid);
552                 }
553         }
554
555         return 0;
556 }
557
558 int bcache_status(NihCommand *command, char *const *args)
559 {
560         int i, seq, nr_in_set = 0;
561         struct cache_sb *seq_sb = NULL;
562
563         for (i = 0; args[i] != NULL; i++) {
564                 struct cache_sb *sb = query_dev(args[i], false, false,
565                                 false, NULL);
566
567                 if(!sb) {
568                         printf("Unable to open superblock, bad path\n");
569                         return -1;
570                 }
571
572                 if(!seq_sb || sb->seq > seq) {
573                         seq = sb->seq;
574                         seq_sb = sb;
575                         nr_in_set = sb->nr_in_set;
576                 }
577         }
578
579         if(!seq_sb)
580                 printf("Unable to find a superblock\n");
581         else
582                 printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
583
584         for (i = 0; i < seq_sb->nr_in_set; i++) {
585                 char uuid_str[40];
586                 struct cache_member *m = ((struct cache_member *) seq_sb->d) + i;
587
588                 uuid_unparse(m->uuid.b, uuid_str);
589
590                 printf("%-50s%-15s%-4llu\n", uuid_str,
591                                 cache_state[CACHE_STATE(m)],
592                                 CACHE_TIER(m));
593         }
594
595         return 0;
596 }
597
598 static char *stats_subdir(char* stats_dir)
599 {
600         char tmp[50] = "/";
601         char *err = NULL;
602         if(stats_dev_uuid) {
603                 strcat(tmp, "cache");
604                 err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
605                 if(err)
606                         goto err;
607         } else if(stats_cache_num) {
608                 strcat(tmp, "cache");
609                 strcat(tmp, stats_cache_num);
610         } else if (stats_five_min)
611                 strcat(tmp, "stats_five_minute");
612         else if (stats_hour)
613                 strcat(tmp, "stats_hour");
614         else if (stats_day)
615                 strcat(tmp, "stats_day");
616         else if (stats_total)
617                 strcat(tmp, "stats_total");
618         else
619                 return err;
620
621         strcat(stats_dir, tmp);
622
623 err:
624         return err;
625 }
626
627 int bcache_stats(NihCommand *command, char *const *args)
628 {
629         int i;
630         char stats_dir[MAX_PATH];
631         DIR *dir = NULL;
632         struct dirent *ent = NULL;
633         char *err = NULL;
634
635         if (stats_uuid) {
636                 snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
637                 err = stats_subdir(stats_dir);
638                 if(err)
639                         goto err;
640
641                 dir = opendir(stats_dir);
642                 if (!dir) {
643                         err = "Failed to open dir";
644                         goto err;
645                 }
646         } else {
647                 err = "Must provide a cacheset uuid";
648                 goto err;
649         }
650
651         if(stats_list || stats_all) {
652                 while ((ent = readdir(dir)) != NULL) {
653                         err = read_stat_dir(dir, stats_dir, ent->d_name, stats_all);
654                         if (err)
655                                 goto err;
656                 }
657         }
658
659
660         for (i = 0; args[i] != NULL; i++) {
661                 err = read_stat_dir(dir, stats_dir, args[i], true);
662                 if (err)
663                         goto err;
664         }
665
666         closedir(dir);
667         return 0;
668
669 err:
670         closedir(dir);
671         printf("bcache_stats error: %s\n", err);
672         return -1;
673 }
674
675 static NihCommand commands[] = {
676         {"format", N_("format <list of drives>"),
677                   "Format one or a list of devices with bcache datastructures."
678                   " You need to do this before you create a volume",
679                   N_("format drive[s] with bcache"),
680                   NULL, make_bcache_options, make_bcache},
681         {"probe", N_("probe <list of devices>"),
682                   "Does a blkid_probe on a device",
683                   N_("Does a blkid_probe on a device"),
684                   NULL, probe_bcache_options, probe_bcache},
685         {"register", N_("register <list of devices>"),
686                      "Registers a list of devices",
687                      N_("Registers a list of devices"),
688                      NULL, bcache_register_options, bcache_register},
689         {"unregister", N_("unregister <list of devices>"),
690                      "Unregisters a list of devices",
691                      N_("Unregisters a list of devices"),
692                      NULL, bcache_unregister_options, bcache_unregister},
693         {"add-devs", N_("add-devs --set=UUID --tier=# <list of devices>"),
694                 "Adds a list of devices to a cacheset",
695                 N_("Adds a list of devices to a cacheset"),
696                 NULL, bcache_add_device_options, bcache_add_devices},
697         {"rm-dev", N_("rm-dev <dev>"),
698                 "Removes a device from its cacheset",
699                 N_("Removes a device from its cacheset"),
700                 NULL, bcache_rm_device_options, bcache_rm_device},
701         {"modify", N_("modify --set=UUID (dev=UUID) name value"),
702                 "Modifies attributes related to the cacheset",
703                 N_("Modifies attributes related to the cacheset"),
704                 NULL, bcache_modify_options, bcache_modify},
705         {"list-cachesets", N_("list-cachesets"),
706                            "Lists cachesets in /sys/fs/bcache",
707                            N_("Lists cachesets in /sys/fs/bcache"),
708                            NULL, list_cachesets_options, bcache_list_cachesets},
709         {"query-devs", N_("query <list of devices>"),
710                        "Gives info about the superblock of a list of devices",
711                        N_("show superblock on each of the listed drive"),
712                        NULL, query_devs_options, bcache_query_devs},
713         {"status", N_("status <list of devices>"),
714                    "Finds the status of the most up to date superblock",
715                    N_("Finds the status of the most up to date superblock"),
716                    NULL, status_options, bcache_status},
717         {"stats", N_("stats <list of devices>"),
718                   "List various bcache statistics",
719                   N_("List various bcache statistics"),
720                   NULL, stats_options, bcache_stats},
721         NIH_COMMAND_LAST
722 };
723
724
725 int main(int argc, char *argv[])
726 {
727         int ret = 0;
728         nih_main_init (argv[0]);
729
730         nih_option_set_synopsis (_("Manage bcache devices"));
731         nih_option_set_help (
732                         _("Helps you manage bcache devices"));
733
734         ret = nih_command_parser (NULL, argc, argv, options, commands);
735         if (ret < 0)
736                 exit (1);
737
738         nih_signal_reset();
739
740         return 0;
741 }