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