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