]> git.sesse.net Git - bcachefs-tools-debian/blob - bcacheadm.c
bcacheadm: add "list-cachesets --internal_uuid UUID" option
[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
38
39 /* make-bcache globals */
40 int bdev = -1;
41 int devs = 0;
42 char *cache_devices[MAX_DEVS];
43 int tier_mapping[MAX_DEVS];
44 unsigned replacement_policy_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, writearound = 0, discard = 0, wipe_bcache = 0;
52 unsigned replication_set = 0;
53 char *replacement_policy = 0;
54 uint64_t data_offset = BDEV_DATA_START_DEFAULT;
55 char *label = NULL;
56 struct cache_sb *cache_set_sb = NULL;
57 const char *cache_set_uuid = 0;
58 const char *csum_type = 0;
59 char *metadata_replicas = 0;
60 char *data_replicas = 0;
61 char *tier = 0;
62
63 /* rm-dev globals */
64 bool force_remove = false;
65
66 /* Modify globals */
67 bool modify_list_attrs = false;
68 static const char *modify_set_uuid = NULL;
69 static const char *modify_dev_uuid = NULL;
70
71 /* query-dev globals */
72 bool force_csum = false;
73 bool uuid_only = false;
74 bool query_brief = false;
75
76 /* probe globals */
77 bool udev = false;
78
79 /* list globals */
80 char *cset_dir = "/sys/fs/bcache";
81 bool list_devs = false;
82 static const char *internal_uuid = NULL;
83
84 /* status globals */
85 bool status_all = false;
86
87 /* capacity globals */
88 static const char *capacity_uuid = NULL;
89 bool capacity_devs = false;
90
91 /* stats globals */
92 bool stats_all = false;
93 bool stats_list = false;
94 static const char *stats_uuid = NULL;
95 static const char *stats_dev_uuid = NULL;
96 static const char *stats_cache_num = NULL;
97 bool stats_five_min = false;
98 bool stats_hour = false;
99 bool stats_day = false;
100 bool stats_total = false;
101
102 /* set_failed globals */
103 static const char *dev_failed_uuid = NULL;
104
105 /* make-bcache option setters */
106 static int set_block_size(NihOption *option, const char *arg)
107 {
108         block_size = hatoi_validate(arg, "block size");
109         return 0;
110 }
111
112 static int set_cache(NihOption *option, const char *arg)
113 {
114         bdev = 0;
115         cache_devices[nr_cache_devices] = strdup(arg);
116         if(!tier)
117                 tier_mapping[nr_cache_devices] = 0;
118         else {
119                 int ntier = atoi(tier);
120                 if(ntier == 0 || ntier == 1)
121                         tier_mapping[nr_cache_devices] = ntier;
122                 else
123                         printf("Invalid tier %s\n", tier);
124         }
125
126         if (!replacement_policy)
127                 replacement_policy_mapping[nr_cache_devices] = 0;
128         else {
129                 int i = 0;
130
131                 while (replacement_policies[i] != NULL) {
132                         if (!strcmp(replacement_policy,
133                                     replacement_policies[i])) {
134                                 replacement_policy_mapping[nr_cache_devices] = i;
135                                 break;
136                         }
137                         i++;
138                 }
139
140                 if (replacement_policies[i] == NULL)
141                         printf("Invalid replacement policy: %s\n",
142                                replacement_policy);
143         }
144
145         devs++;
146         nr_cache_devices++;
147
148         return 0;
149 }
150
151 static int set_bdev(NihOption *option, const char *arg)
152 {
153         bdev = 1;
154
155         if(label)
156                 backing_dev_labels[nr_backing_devices] = strdup(label);
157
158         backing_devices[nr_backing_devices] = strdup(arg);
159
160         nr_backing_devices++;
161         devs++;
162
163         return 0;
164 }
165
166 static int set_bucket_sizes(NihOption *option, const char *arg)
167 {
168         bucket_sizes[num_bucket_sizes]=hatoi_validate(arg, "bucket size");
169         num_bucket_sizes++;
170         return 0;
171 }
172
173 /* probe setters */
174 static int set_udev(NihOption *option, const char *arg)
175 {
176         if (strcmp("udev", arg)) {
177                 printf("Invalid output format %s\n", arg);
178                 exit(EXIT_FAILURE);
179         }
180         udev = true;
181         return 0;
182 }
183
184
185 /* options */
186 static NihOption make_bcache_options[] = {
187 //      {int shortoption, char* longoption, char* help, NihOptionGroup, char* argname, void *value, NihOptionSetter}
188         {'C', "cache",  N_("Format a cache device"), NULL, "dev", NULL, set_cache},
189         {'B', "bdev",   N_("Format a backing device"), NULL, "dev", NULL, set_bdev},
190         {'l', "label",  N_("label"), NULL, "label", &label, NULL},
191         //Only one bucket_size supported until a list of bucket sizes is parsed correctly
192         {'b', "bucket", N_("bucket size"), NULL, "size", NULL, set_bucket_sizes},
193         //Does the default setter automatically convert strings to an int?
194         {'w', "block",  N_("block size (hard sector size of SSD, often 2k"), NULL,"size", NULL, set_block_size},
195         {'t', "tier",   N_("tier of subsequent devices"), NULL,"#", &tier, NULL},
196         {'p', "cache_replacement_policy", N_("one of (lru|fifo|random)"), NULL,"policy", &replacement_policy, NULL},
197         {'o', "data_offset", N_("data offset in sectors"), NULL,"offset", &data_offset, NULL},
198
199         {0, "cset-uuid",        N_("UUID for the cache set"),           NULL,   "uuid", &cache_set_uuid, NULL},
200         {0, "csum-type",        N_("One of (none|crc32c|crc64)"),               NULL,   "type", &csum_type, NULL },
201         {0, "replication-set",N_("replication set of subsequent devices"),      NULL,   NULL, &replication_set, NULL },
202         {0, "meta-replicas",N_("number of metadata replicas"),          NULL,   "#", &metadata_replicas, NULL},
203         {0, "data-replicas",N_("number of data replicas"),              NULL,   "#", &data_replicas, NULL },
204
205         {0, "wipe-bcache",      N_("destroy existing bcache data if present"),          NULL, NULL, &wipe_bcache, NULL},
206         {0, "discard",          N_("enable discards"),          NULL, NULL, &discard,           NULL},
207         {0, "writeback",        N_("enable writeback"),         NULL, NULL, &writeback,         NULL},
208         {0, "writearound",      N_("enable writearound"),       NULL, NULL, &writearound,       NULL},
209
210         NIH_OPTION_LAST
211 };
212
213 static NihOption probe_bcache_options[] = {
214         {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
215         NIH_OPTION_LAST
216 };
217
218 static NihOption bcache_register_options[] = {
219         NIH_OPTION_LAST
220 };
221
222 static NihOption bcache_unregister_options[] = {
223         NIH_OPTION_LAST
224 };
225
226 static NihOption bcache_add_device_options[] = {
227         NIH_OPTION_LAST
228 };
229
230 static NihOption bcache_rm_device_options[] = {
231         {'f', "force", N_("force cache removal"), NULL, NULL, &force_remove, NULL},
232         NIH_OPTION_LAST
233 };
234
235 static NihOption bcache_modify_options[] = {
236         {'l', "list", N_("list attributes"), NULL, NULL, &modify_list_attrs, NULL},
237         {'u', "set", N_("cacheset uuid"), NULL, "UUID", &modify_set_uuid, NULL},
238         {'d', "dev", N_("device uuid"), NULL, "UUID", &modify_dev_uuid, NULL},
239         NIH_OPTION_LAST
240 };
241
242 static NihOption query_devs_options[] = {
243         {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
244         {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
245         {'b', "brief", N_("only print out the cluster,server,and disk uuids"), NULL, NULL, &query_brief, NULL},
246         NIH_OPTION_LAST
247 };
248
249 static NihOption list_cachesets_options[] = {
250         {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
251         {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
252         {0, "internal_uuid", N_("Show the internal UUID for the given cacheset UUID"), NULL, "UUID", &internal_uuid, NULL},
253         NIH_OPTION_LAST
254 };
255
256 static NihOption status_options[] = {
257         {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
258         NIH_OPTION_LAST
259 };
260
261 static NihOption capacity_options[] = {
262         {'u', "set", N_("cache_set UUID"), NULL, "UUID", &capacity_uuid, NULL},
263         {'d', "devs", N_("dev UUID"), NULL, NULL, &capacity_devs, NULL},
264         NIH_OPTION_LAST
265 };
266
267 static NihOption stats_options[] = {
268         {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
269         {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
270         {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
271         {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
272         {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
273         {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
274         {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
275         {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
276         {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
277         NIH_OPTION_LAST
278 };
279
280 static NihOption set_failed_options[] = {
281         {'d', "dev", N_("dev UUID"), NULL, "UUID", &dev_failed_uuid, NULL},
282         NIH_OPTION_LAST
283 };
284
285 static NihOption options[] = {
286         NIH_OPTION_LAST
287 };
288
289
290 /* commands */
291 int make_bcache(NihCommand *command, char *const *args)
292 {
293         int cache_dev_fd[devs];
294
295         int backing_dev_fd[devs];
296
297         unsigned cache_mode;
298
299         cache_set_sb = calloc(1, sizeof(*cache_set_sb) +
300                                      sizeof(struct cache_member) * devs);
301
302         uuid_generate(cache_set_sb->set_uuid.b);
303
304         if (cache_set_uuid) {
305                 if(uuid_parse(cache_set_uuid, cache_set_sb->user_uuid.b)) {
306                         fprintf(stderr, "Bad uuid\n");
307                         return -1;
308                 }
309         } else {
310                 uuid_generate(cache_set_sb->user_uuid.b);
311         }
312
313         if (label)
314                 memcpy(cache_set_sb->label, label, sizeof(cache_set_sb->label));
315
316         if (csum_type) {
317                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb,
318                                 read_string_list_or_die(csum_type, csum_types,
319                                         "csum type"));
320         } else {
321                 SET_CACHE_PREFERRED_CSUM_TYPE(cache_set_sb, BCH_CSUM_CRC32C);
322         }
323
324         if (metadata_replicas) {
325                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb,
326                                 strtoul_or_die(metadata_replicas,
327                                         CACHE_SET_META_REPLICAS_WANT_MAX,
328                                         "meta replicas"));
329         } else {
330                 SET_CACHE_SET_META_REPLICAS_WANT(cache_set_sb, 1);
331         }
332
333         if (data_replicas) {
334                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb,
335                         strtoul_or_die(data_replicas,
336                                 CACHE_SET_DATA_REPLICAS_WANT_MAX,
337                                 "data replicas"));
338         } else {
339                 SET_CACHE_SET_DATA_REPLICAS_WANT(cache_set_sb, 1);
340         }
341
342         if (bdev == -1) {
343                 fprintf(stderr, "Please specify -C or -B\n");
344                 exit(EXIT_FAILURE);
345         }
346
347         if (!bucket_sizes[0])
348                 bucket_sizes[0] = 1024;
349
350         for (i = 0; i < nr_cache_devices; i++)
351                 next_cache_device(cache_set_sb,
352                                   replication_set,
353                                   tier_mapping[i],
354                                   replacement_policy_mapping[i],
355                                   discard);
356
357         if (!cache_set_sb->nr_in_set && !nr_backing_devices) {
358                 fprintf(stderr, "Please supply a device\n");
359                 exit(EXIT_FAILURE);
360         }
361
362         i = 0;
363         do {
364                 if (bucket_sizes[i] < block_size) {
365                         fprintf(stderr,
366                         "Bucket size cannot be smaller than block size\n");
367                         exit(EXIT_FAILURE);
368                 }
369                 i++;
370         } while (i < num_bucket_sizes);
371
372         if (!block_size) {
373                 for (i = 0; i < cache_set_sb->nr_in_set; i++)
374                         block_size = max(block_size,
375                                          get_blocksize(cache_devices[i]));
376
377                 for (i = 0; i < nr_backing_devices; i++)
378                         block_size = max(block_size,
379                                          get_blocksize(backing_devices[i]));
380         }
381
382         for (i = 0; i < cache_set_sb->nr_in_set; i++)
383                 cache_dev_fd[i] = dev_open(cache_devices[i], wipe_bcache);
384
385         for (i = 0; i < nr_backing_devices; i++)
386                 backing_dev_fd[i] = dev_open(backing_devices[i], wipe_bcache);
387
388         write_cache_sbs(cache_dev_fd, cache_set_sb, block_size,
389                         bucket_sizes, num_bucket_sizes);
390
391         if (writeback)
392                 cache_mode = CACHE_MODE_WRITEBACK;
393         else if (writearound)
394                 cache_mode = CACHE_MODE_WRITEAROUND;
395         else
396                 cache_mode = CACHE_MODE_WRITETHROUGH;
397
398         for (i = 0; i < nr_backing_devices; i++)
399                 write_backingdev_sb(backing_dev_fd[i],
400                                     block_size, bucket_sizes,
401                                     cache_mode, data_offset,
402                                     backing_dev_labels[i],
403                                     cache_set_sb->user_uuid,
404                                     cache_set_sb->set_uuid);
405
406
407         return 0;
408 }
409
410 int probe_bcache(NihCommand *command, char *const *args)
411 {
412         int i;
413         char *err = NULL;
414
415         for (i = 0; args[i] != NULL; i++) {
416                 err = probe(args[i], udev);
417                 if(err) {
418                         printf("probe_bcache error: %s\n", err);
419                         return -1;
420                 }
421         }
422
423         return 0;
424 }
425
426 int bcache_register(NihCommand *command, char *const *args)
427 {
428         char *err = NULL;
429
430         err = register_bcache(args);
431         if (err) {
432                 printf("bcache_register error: %s\n", err);
433                 return -1;
434         }
435
436         return 0;
437 }
438
439 int bcache_unregister(NihCommand *command, char *const *args)
440 {
441         char *err = NULL;
442
443         err = unregister_bcache(args);
444         if (err) {
445                 printf("bcache_unregister error: %s\n", err);
446                 return -1;
447         }
448
449         return 0;
450 }
451
452 int bcache_add_devices(NihCommand *command, char *const *args)
453 {
454         char *err;
455
456         err = add_devices(args);
457         if (err) {
458                 printf("bcache_add_devices error: %s\n", err);
459                 return -1;
460         }
461
462         return 0;
463 }
464
465 int bcache_rm_device(NihCommand *command, char *const *args)
466 {
467         char *err;
468
469         err = remove_device(args[0], force_remove);
470         if (err) {
471                 printf("bcache_rm_devices error: %s\n", err);
472                 return -1;
473         }
474
475         return 0;
476 }
477
478 int bcache_modify(NihCommand *command, char *const *args)
479 {
480         char *err;
481         char path[MAX_PATH];
482         char *attr = args[0];
483         char *val = NULL;
484         int fd = -1;
485
486         if (modify_list_attrs) {
487                 sysfs_attr_list();
488                 return 0;
489         }
490
491         if (!modify_set_uuid) {
492                 printf("Must provide a cacheset uuid\n");
493                 return -1;
494         }
495
496         snprintf(path, MAX_PATH, "%s/%s", cset_dir, modify_set_uuid);
497
498         if(!attr) {
499                 printf("Must provide the name of an attribute to modify\n");
500                 goto err;
501         }
502
503         enum sysfs_attr type = sysfs_attr_type(attr);
504
505         if (type == -1)
506                 goto err;
507         else if(type == INTERNAL_ATTR)
508                 strcat(path, "/internal");
509         else if(type == CACHE_ATTR) {
510                 if(modify_dev_uuid) {
511                         /* searches all cache# for a matching uuid,
512                          * path gets modified to the correct cache path */
513                         char subdir[10] = "/cache";
514                         err = find_matching_uuid(path, subdir,
515                                         modify_dev_uuid);
516                         if (err) {
517                                 printf("Failed to find "
518                                         "matching dev %s\n", err);
519                                 goto err;
520                         } else {
521                                 strcat(path, subdir);
522                         }
523                 } else {
524                         printf("Must provide a device uuid\n");
525                 }
526         }
527         /* SET_ATTRs are just in the current dir */
528
529         strcat(path, "/");
530         strcat(path, attr);
531
532         val = args[1];
533         if (!val) {
534                 printf("Must provide a value to change the attribute to\n");
535                 goto err;
536         }
537
538         fd = open(path, O_WRONLY);
539         if (fd < 0) {
540                 printf("Unable to open modify attr with path %s\n", path);
541                 goto err;
542         }
543
544         write(fd, val, strlen(val));
545
546 err:
547         if(fd)
548                 close(fd);
549         return 0;
550 }
551
552 int bcache_list_cachesets(NihCommand *command, char *const *args)
553 {
554         char *err = NULL;
555
556         if (internal_uuid) {
557                 char uuid_path[MAX_PATH];
558                 DIR *uuid_dir;
559                 char buf[MAX_PATH];
560
561                 snprintf(uuid_path, MAX_PATH, "%s/%s", cset_dir, internal_uuid);
562
563                 err = "uuid does not exist";
564                 if((uuid_dir = opendir(uuid_path)) == NULL)
565                         goto err;
566
567                 err = read_stat_dir(uuid_dir, uuid_path, "/internal/internal_uuid", buf);
568                 if (err)
569                         goto err;
570                 printf("%s", buf);
571                 return 0;
572         }
573
574         err = list_cachesets(cset_dir, list_devs);
575         if (err)
576                 goto err;
577
578         return 0;
579
580 err:
581         printf("bcache_list_cachesets error :%s\n", err);
582         return -1;
583 }
584
585 int bcache_query_devs(NihCommand *command, char *const *args)
586 {
587         int i;
588
589         if (query_brief)
590                 printf("%-10s%-40s%-40s%-40s\n", "dev name", "disk uuid",
591                                 "server uuid", "cluster uuid");
592
593         for (i = 0; args[i] != NULL; i++) {
594                 char dev_uuid[40];
595                 struct cache_sb *sb = query_dev(args[i], force_csum,
596                                 !query_brief, uuid_only, dev_uuid);
597
598                 if (!sb) {
599                         printf("error opening the superblock for %s\n",
600                                         args[i]);
601                         return -1;
602                 }
603
604                 if (uuid_only) {
605                         printf("%s\n", dev_uuid);
606                 } else if (query_brief) {
607                         char set_uuid_str[40], dev_uuid_str[40];
608                         char *clus_uuid = (char *)sb->label;
609
610                         uuid_unparse(sb->user_uuid.b, set_uuid_str);
611                         uuid_unparse(sb->disk_uuid.b, dev_uuid_str);
612                         if (!strcmp(clus_uuid, ""))
613                                 clus_uuid = "None";
614
615                         printf("%-10s%-40s%-40s%-40s\n", args[i],
616                                         dev_uuid_str,
617                                         set_uuid_str,
618                                         clus_uuid);
619                 }
620                 free(sb);
621         }
622
623         return 0;
624 }
625
626 int bcache_status(NihCommand *command, char *const *args)
627 {
628         int i, dev_count = 0, seq, cache_count = 0;
629         struct cache_sb *seq_sb = NULL;
630         char cache_path[MAX_PATH];
631         char *dev_names[MAX_DEVS];
632         char *dev_uuids[MAX_DEVS];
633         char intbuf[4];
634         char set_uuid[40];
635
636         for (i = 0; args[i] != NULL; i++) {
637                 struct cache_sb *sb = query_dev(args[i], false, false,
638                                 false, NULL);
639
640                 if (!sb) {
641                         printf("Unable to open superblock, bad path\n");
642                         return -1;
643                 }
644
645                 if (!seq_sb || sb->seq > seq) {
646                         seq = sb->seq;
647                         seq_sb = sb;
648                 } else
649                         free(sb);
650         }
651
652         if (!seq_sb) {
653                 printf("Unable to find a superblock\n");
654                 return -1;
655         } else {
656                 uuid_unparse(seq_sb->user_uuid.b, set_uuid);
657                 printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
658         }
659
660         snprintf(intbuf, 4, "%d", i);
661         snprintf(cache_path, MAX_PATH, "%s/%s/%s", cset_dir, set_uuid,
662                         "cache0");
663
664         /*
665          * Get a list of all the devices from sysfs first, then
666          * compare it to the list we get back from the most up
667          * to date superblock. If there are any devices in the superblock
668          * that are not in sysfs, print out 'missing'
669          */
670         while (true) {
671                 char buf[MAX_PATH];
672                 int len;
673                 DIR *cache_dir;
674
675                 if(((cache_dir = opendir(cache_path)) == NULL) &&
676                     cache_count > MAX_DEVS)
677                         break;
678
679                 if (cache_dir)
680                         closedir(cache_dir);
681
682                 if((len = readlink(cache_path, buf, sizeof(buf) - 1)) != -1) {
683                         struct cache_sb *sb;
684                         char dev_uuid[40];
685                         char dev_path[32];
686
687                         buf[len] = '\0';
688                         dev_names[dev_count] = dev_name(buf);
689                         snprintf(dev_path, MAX_PATH, "%s/%s", "/dev",
690                                         dev_names[dev_count]);
691                         sb = query_dev(dev_path, false, false,
692                                         true, dev_uuid);
693                         if (!sb) {
694                                 printf("error reading %s\n", dev_path);
695                                 return -1;
696                         } else
697                                 free(sb);
698
699                         dev_uuids[dev_count] = strdup(dev_uuid);
700                         dev_count++;
701                 }
702
703                 cache_path[strlen(cache_path) - strlen(intbuf)] = 0;
704                 cache_count++;
705
706                 snprintf(intbuf, 4, "%d", cache_count);
707                 strcat(cache_path, intbuf);
708         }
709
710         for (i = 0; i < seq_sb->nr_in_set; i++) {
711                 char uuid_str[40];
712                 struct cache_member *m = seq_sb->members + i;
713                 char dev_state[32];
714                 int j;
715
716                 uuid_unparse(m->uuid.b, uuid_str);
717                 snprintf(dev_state, MAX_PATH, "%s",
718                          cache_state[CACHE_STATE(m)]);
719
720                 for (j = 0; j < dev_count; j++) {
721                         if (!strcmp(uuid_str, dev_uuids[j])) {
722                                 break;
723                         } else if (j == dev_count - 1) {
724                                 if (!strcmp(cache_state[CACHE_STATE(m)], "active"))
725                                         snprintf(dev_state, MAX_PATH, "%s", "missing");
726                                 break;
727                         }
728                 }
729
730                 printf("%-50s%-15s%-4llu\n", uuid_str, dev_state,
731                                 CACHE_TIER(m));
732         }
733
734         if (seq_sb)
735                 free(seq_sb);
736         for (i = 0; i < dev_count; i++) {
737                 free(dev_names[i]);
738                 free(dev_uuids[i]);
739         }
740
741         return 0;
742 }
743
744 int bcache_capacity(NihCommand *command, char *const *args)
745 {
746         char *err = "Must provide a cacheset uuid";
747         if(!capacity_uuid)
748                 goto err;
749
750         err = bcache_get_capacity(cset_dir, capacity_uuid, capacity_devs);
751         if (err)
752                 goto err;
753
754         return 0;
755
756 err:
757         printf("bcache_capacity failed with error: %s\n", err);
758         return -1;
759
760 }
761
762 static char *stats_subdir(char* stats_dir)
763 {
764         char tmp[50] = "/";
765         char *err = NULL;
766         if(stats_dev_uuid) {
767                 strcat(tmp, "cache");
768                 err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
769                 if(err)
770                         goto err;
771         } else if(stats_cache_num) {
772                 strcat(tmp, "cache");
773                 strcat(tmp, stats_cache_num);
774         } else if (stats_five_min)
775                 strcat(tmp, "stats_five_minute");
776         else if (stats_hour)
777                 strcat(tmp, "stats_hour");
778         else if (stats_day)
779                 strcat(tmp, "stats_day");
780         else if (stats_total)
781                 strcat(tmp, "stats_total");
782         else
783                 return err;
784
785         strcat(stats_dir, tmp);
786
787 err:
788         return err;
789 }
790
791 int bcache_stats(NihCommand *command, char *const *args)
792 {
793         int i;
794         char stats_dir[MAX_PATH];
795         DIR *dir = NULL;
796         struct dirent *ent = NULL;
797         char *err = NULL;
798         char buf[MAX_PATH];
799
800         if (stats_uuid) {
801                 snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
802                 err = stats_subdir(stats_dir);
803                 if(err)
804                         goto err;
805
806                 dir = opendir(stats_dir);
807                 if (!dir) {
808                         err = "Failed to open dir";
809                         goto err;
810                 }
811         } else {
812                 err = "Must provide a cacheset uuid";
813                 goto err;
814         }
815
816         if(stats_list || stats_all) {
817                 while ((ent = readdir(dir)) != NULL) {
818                         err = read_stat_dir(dir, stats_dir, ent->d_name, buf);
819                         if (err)
820                                 goto err;
821                         if(stats_list)
822                                 printf("%s\n", ent->d_name);
823                         if(stats_all)
824                                 printf("\t%s\n", buf);
825                 }
826         }
827
828         for (i = 0; args[i] != NULL; i++) {
829                 err = read_stat_dir(dir, stats_dir, args[i], buf);
830                 if (err)
831                         goto err;
832                 printf("%s\n", buf);
833         }
834
835         closedir(dir);
836         return 0;
837
838 err:
839         closedir(dir);
840         printf("bcache_stats error: %s\n", err);
841         return -1;
842 }
843
844 int bcache_set_failed(NihCommand *command, char *const *args)
845 {
846         char *err = NULL;
847
848         if (!dev_failed_uuid) {
849                 printf("Pass in a dev uuid\n");
850                 return -1;
851         }
852
853         err = device_set_failed(dev_failed_uuid);
854         if (err) {
855                 printf("bcache_set_failed_ioctl error: %s\n", err);
856                 return -1;
857         }
858
859         return 0;
860 }
861
862 static NihCommand commands[] = {
863         {"format", N_("format <list of drives>"),
864                   "Format one or a list of devices with bcache datastructures."
865                   " You need to do this before you create a volume",
866                   N_("format drive[s] with bcache"),
867                   NULL, make_bcache_options, make_bcache},
868         {"probe", N_("probe <list of devices>"),
869                   "Does a blkid_probe on a device",
870                   N_("Does a blkid_probe on a device"),
871                   NULL, probe_bcache_options, probe_bcache},
872         {"register", N_("register <list of devices>"),
873                      "Registers a list of devices",
874                      N_("Registers a list of devices"),
875                      NULL, bcache_register_options, bcache_register},
876         {"unregister", N_("unregister <list of devices>"),
877                      "Unregisters a list of devices",
878                      N_("Unregisters a list of devices"),
879                      NULL, bcache_unregister_options, bcache_unregister},
880         {"add-devs", N_("add-devs --tier=# <list of devices>"),
881                 "Adds a list of devices to a cacheset",
882                 N_("Adds a list of devices to a cacheset"),
883                 NULL, bcache_add_device_options, bcache_add_devices},
884         {"rm-dev", N_("rm-dev <dev>"),
885                 "Removes a device from its cacheset",
886                 N_("Removes a device from its cacheset"),
887                 NULL, bcache_rm_device_options, bcache_rm_device},
888         {"modify", N_("modify --set=UUID (dev=UUID) name value"),
889                 "Modifies attributes related to the cacheset",
890                 N_("Modifies attributes related to the cacheset"),
891                 NULL, bcache_modify_options, bcache_modify},
892         {"list-cachesets", N_("list-cachesets"),
893                            "Lists cachesets in /sys/fs/bcache",
894                            N_("Lists cachesets in /sys/fs/bcache"),
895                            NULL, list_cachesets_options, bcache_list_cachesets},
896         {"query-devs", N_("query <list of devices>"),
897                        "Gives info about the superblock of a list of devices",
898                        N_("show superblock on each of the listed drive"),
899                        NULL, query_devs_options, bcache_query_devs},
900         {"status", N_("status <list of devices>"),
901                    "Finds the status of the most up to date superblock",
902                    N_("Finds the status of the most up to date superblock"),
903                    NULL, status_options, bcache_status},
904         {"capacity", N_("capacity --set=UUID"),
905                 "Shows the capacity of the cacheset",
906                 N_("Shows the capacity of the cacheset"),
907                 NULL, capacity_options, bcache_capacity},
908         {"stats", N_("stats <list of devices>"),
909                   "List various bcache statistics",
910                   N_("List various bcache statistics"),
911                   NULL, stats_options, bcache_stats},
912         {"set-failed", N_("set-failed --dev=UUID"),
913                 "Sets a device to the FAILED state",
914                 N_("Sets a device to the FAILED state"),
915                 NULL, set_failed_options, bcache_set_failed},
916         NIH_COMMAND_LAST
917 };
918
919
920 int main(int argc, char *argv[])
921 {
922         int ret = 0;
923         nih_main_init (argv[0]);
924
925         nih_option_set_synopsis (_("Manage bcache devices"));
926         nih_option_set_help (
927                         _("Helps you manage bcache devices"));
928
929         ret = nih_command_parser (NULL, argc, argv, options, commands);
930         if (ret < 0)
931                 exit (1);
932
933         nih_signal_reset();
934
935         return 0;
936 }