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