]> git.sesse.net Git - bcachefs-tools-debian/blob - bcacheadm.c
Major refactoring, add new settings to bcacheadm format
[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 <sys/ioctl.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <uuid/uuid.h>
29 #include <dirent.h>
30
31 #include "bcache.h"
32 #include "bcacheadm.h"
33
34 #define PACKAGE_NAME "bcacheadm"
35 #define PACKAGE_VERSION "1.0"
36 #define PACKAGE_BUGREPORT "bugreport"
37
38 /* rm-dev globals */
39 bool force_remove = false;
40
41 /* Modify globals */
42 bool modify_list_attrs = false;
43 static const char *modify_set_uuid = NULL;
44 static const char *modify_dev_uuid = NULL;
45
46 /* query-dev globals */
47 bool force_csum = false;
48 bool uuid_only = false;
49 bool query_brief = false;
50
51 /* probe globals */
52 bool udev = false;
53
54 /* list globals */
55 char *cset_dir = "/sys/fs/bcache";
56 bool list_devs = false;
57 static const char *internal_uuid = NULL;
58
59 /* status globals */
60 bool status_all = false;
61
62 /* capacity globals */
63 static const char *capacity_uuid = NULL;
64 bool capacity_devs = false;
65
66 /* stats globals */
67 bool stats_all = false;
68 bool stats_list = false;
69 static const char *stats_uuid = NULL;
70 static const char *stats_dev_uuid = NULL;
71 static const char *stats_cache_num = NULL;
72 bool stats_five_min = false;
73 bool stats_hour = false;
74 bool stats_day = false;
75 bool stats_total = false;
76
77 /* set_failed globals */
78 static const char *dev_failed_uuid = NULL;
79
80 /* probe setters */
81 static int set_udev(NihOption *option, const char *arg)
82 {
83         if (strcmp("udev", arg)) {
84                 printf("Invalid output format %s\n", arg);
85                 exit(EXIT_FAILURE);
86         }
87         udev = true;
88         return 0;
89 }
90
91 /* options */
92 static NihOption probe_bcache_options[] = {
93         {'o', "udev", N_("udev"), NULL, NULL, NULL, set_udev},
94         NIH_OPTION_LAST
95 };
96
97 static NihOption bcache_register_options[] = {
98         NIH_OPTION_LAST
99 };
100
101 static NihOption bcache_unregister_options[] = {
102         NIH_OPTION_LAST
103 };
104
105 static NihOption bcache_add_device_options[] = {
106         NIH_OPTION_LAST
107 };
108
109 static NihOption bcache_rm_device_options[] = {
110         {'f', "force", N_("force cache removal"), NULL, NULL, &force_remove, NULL},
111         NIH_OPTION_LAST
112 };
113
114 static NihOption bcache_modify_options[] = {
115         {'l', "list", N_("list attributes"), NULL, NULL, &modify_list_attrs, NULL},
116         {'u', "set", N_("cacheset uuid"), NULL, "UUID", &modify_set_uuid, NULL},
117         {'d', "dev", N_("device uuid"), NULL, "UUID", &modify_dev_uuid, NULL},
118         NIH_OPTION_LAST
119 };
120
121 static NihOption query_devs_options[] = {
122         {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
123         {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
124         {'b', "brief", N_("only print out the cluster,server,and disk uuids"), NULL, NULL, &query_brief, NULL},
125         NIH_OPTION_LAST
126 };
127
128 static NihOption list_cachesets_options[] = {
129         {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
130         {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
131         {0, "internal_uuid", N_("Show the internal UUID for the given cacheset UUID"), NULL, "UUID", &internal_uuid, NULL},
132         NIH_OPTION_LAST
133 };
134
135 static NihOption status_options[] = {
136         {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
137         NIH_OPTION_LAST
138 };
139
140 static NihOption capacity_options[] = {
141         {'u', "set", N_("cache_set UUID"), NULL, "UUID", &capacity_uuid, NULL},
142         {'d', "devs", N_("Individual device capacities"), NULL, NULL, &capacity_devs, NULL},
143         NIH_OPTION_LAST
144 };
145
146 static NihOption stats_options[] = {
147         {'a', "all", N_("all"), NULL, NULL, &stats_all, NULL},
148         {'l', "list", N_("list"), NULL, NULL, &stats_list, NULL},
149         {'u', "set", N_("cache_set UUID"), NULL, "UUID", &stats_uuid, NULL},
150         {'d', "dev", N_("dev UUID"), NULL, "UUID", &stats_dev_uuid, NULL},
151         {'c', "cache", N_("cache number (starts from 0)"), NULL, "CACHE#", &stats_cache_num, NULL},
152         {0, "five-min-stats", N_("stats accumulated in last 5 minutes"), NULL, NULL, &stats_five_min, NULL},
153         {0, "hour-stats", N_("stats accumulated in last hour"), NULL, NULL, &stats_hour, NULL},
154         {0, "day-stats", N_("stats accumulated in last day"), NULL, NULL, &stats_day, NULL},
155         {0, "total-stats", N_("stats accumulated in total"), NULL, NULL, &stats_total, NULL},
156         NIH_OPTION_LAST
157 };
158
159 static NihOption set_failed_options[] = {
160         {'d', "dev", N_("dev UUID"), NULL, "UUID", &dev_failed_uuid, NULL},
161         NIH_OPTION_LAST
162 };
163
164 static NihOption options[] = {
165         NIH_OPTION_LAST
166 };
167
168 /* commands */
169 int probe_bcache(NihCommand *command, char *const *args)
170 {
171         int i;
172         char *err = NULL;
173
174         for (i = 0; args[i] != NULL; i++) {
175                 err = probe(args[i], udev);
176                 if(err) {
177                         printf("probe_bcache error: %s\n", err);
178                         return -1;
179                 }
180         }
181
182         return 0;
183 }
184
185 int bcache_register(NihCommand *command, char *const *args)
186 {
187         char *err = NULL;
188
189         err = register_bcache(args);
190         if (err) {
191                 printf("bcache_register error: %s\n", err);
192                 return -1;
193         }
194
195         return 0;
196 }
197
198 int bcache_unregister(NihCommand *command, char *const *args)
199 {
200         char *err = NULL;
201
202         err = unregister_bcache(args);
203         if (err) {
204                 printf("bcache_unregister error: %s\n", err);
205                 return -1;
206         }
207
208         return 0;
209 }
210
211 int bcache_add_devices(NihCommand *command, char *const *args)
212 {
213         char *err;
214
215         err = add_devices(args);
216         if (err) {
217                 printf("bcache_add_devices error: %s\n", err);
218                 return -1;
219         }
220
221         return 0;
222 }
223
224 int bcache_rm_device(NihCommand *command, char *const *args)
225 {
226         char *err;
227
228         if (!args[0]) {
229                 printf("Must provide a device name\n");
230                 return -1;
231         }
232
233         err = remove_device(args[0], force_remove);
234         if (err) {
235                 printf("bcache_rm_devices error: %s\n", err);
236                 return -1;
237         }
238
239         return 0;
240 }
241
242 int bcache_modify(NihCommand *command, char *const *args)
243 {
244         char *err;
245         char path[MAX_PATH];
246         char *attr = args[0];
247         char *val = NULL;
248         int fd = -1;
249
250         if (modify_list_attrs) {
251                 sysfs_attr_list();
252                 return 0;
253         }
254
255         if (!modify_set_uuid) {
256                 printf("Must provide a cacheset uuid\n");
257                 return -1;
258         }
259
260         snprintf(path, MAX_PATH, "%s/%s", cset_dir, modify_set_uuid);
261
262         if(!attr) {
263                 printf("Must provide the name of an attribute to modify\n");
264                 goto err;
265         }
266
267         enum sysfs_attr type = sysfs_attr_type(attr);
268
269         if (type == -1)
270                 goto err;
271         else if(type == INTERNAL_ATTR)
272                 strcat(path, "/internal");
273         else if(type == CACHE_ATTR) {
274                 if(modify_dev_uuid) {
275                         /* searches all cache# for a matching uuid,
276                          * path gets modified to the correct cache path */
277                         char subdir[10] = "/cache";
278                         err = find_matching_uuid(path, subdir,
279                                         modify_dev_uuid);
280                         if (err) {
281                                 printf("Failed to find "
282                                         "matching dev %s\n", err);
283                                 goto err;
284                         } else {
285                                 strcat(path, subdir);
286                         }
287                 } else {
288                         printf("Must provide a device uuid\n");
289                 }
290         }
291         /* SET_ATTRs are just in the current dir */
292
293         strcat(path, "/");
294         strcat(path, attr);
295
296         val = args[1];
297         if (!val) {
298                 printf("Must provide a value to change the attribute to\n");
299                 goto err;
300         }
301
302         fd = open(path, O_WRONLY);
303         if (fd < 0) {
304                 printf("Unable to open modify attr with path %s\n", path);
305                 goto err;
306         }
307
308         write(fd, val, strlen(val));
309
310 err:
311         if(fd)
312                 close(fd);
313         return 0;
314 }
315
316 int bcache_list_cachesets(NihCommand *command, char *const *args)
317 {
318         char *err = NULL;
319
320         if (internal_uuid) {
321                 char uuid_path[MAX_PATH];
322                 DIR *uuid_dir;
323                 char buf[MAX_PATH];
324
325                 snprintf(uuid_path, MAX_PATH, "%s/%s", cset_dir, internal_uuid);
326
327                 err = "uuid does not exist";
328                 if((uuid_dir = opendir(uuid_path)) == NULL)
329                         goto err;
330
331                 err = read_stat_dir(uuid_dir, uuid_path, "/internal/internal_uuid", buf);
332                 if (err)
333                         goto err;
334                 printf("%s", buf);
335                 return 0;
336         }
337
338         err = list_cachesets(cset_dir, list_devs);
339         if (err)
340                 goto err;
341
342         return 0;
343
344 err:
345         printf("bcache_list_cachesets error :%s\n", err);
346         return -1;
347 }
348
349 int bcache_query_devs(NihCommand *command, char *const *args)
350 {
351         int i;
352
353         if (query_brief)
354                 printf("%-10s%-40s%-40s%-40s\n", "dev name", "disk uuid",
355                                 "server uuid", "cluster uuid");
356
357         for (i = 0; args[i] != NULL; i++) {
358                 char dev_uuid[40];
359                 struct cache_sb *sb = query_dev(args[i], force_csum,
360                                 !query_brief, uuid_only, dev_uuid);
361
362                 if (!sb) {
363                         printf("error opening the superblock for %s\n",
364                                         args[i]);
365                         return -1;
366                 }
367
368                 if (uuid_only) {
369                         printf("%s\n", dev_uuid);
370                 } else if (query_brief) {
371                         char set_uuid_str[40], dev_uuid_str[40];
372                         char *clus_uuid = (char *)sb->label;
373
374                         uuid_unparse(sb->user_uuid.b, set_uuid_str);
375                         uuid_unparse(sb->disk_uuid.b, dev_uuid_str);
376                         if (!strcmp(clus_uuid, ""))
377                                 clus_uuid = "None";
378
379                         printf("%-10s%-40s%-40s%-40s\n", args[i],
380                                         dev_uuid_str,
381                                         set_uuid_str,
382                                         clus_uuid);
383                 }
384                 free(sb);
385         }
386
387         return 0;
388 }
389
390 int bcache_status(NihCommand *command, char *const *args)
391 {
392         int i, dev_count = 0, seq, cache_count = 0;
393         struct cache_sb *seq_sb = NULL;
394         char cache_path[MAX_PATH];
395         char *dev_names[MAX_DEVS];
396         char *dev_uuids[MAX_DEVS];
397         char intbuf[4];
398         char set_uuid[40];
399
400         for (i = 0; args[i] != NULL; i++) {
401                 struct cache_sb *sb = query_dev(args[i], false, false,
402                                 false, NULL);
403
404                 if (!sb) {
405                         printf("Unable to open superblock, bad path\n");
406                         return -1;
407                 }
408
409                 if (!seq_sb || sb->seq > seq) {
410                         seq = sb->seq;
411                         seq_sb = sb;
412                 } else
413                         free(sb);
414         }
415
416         if (!seq_sb) {
417                 printf("Unable to find a superblock\n");
418                 return -1;
419         } else {
420                 uuid_unparse(seq_sb->user_uuid.b, set_uuid);
421                 printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
422         }
423
424         snprintf(intbuf, 4, "%d", i);
425         snprintf(cache_path, MAX_PATH, "%s/%s/%s", cset_dir, set_uuid,
426                         "cache0");
427
428         /*
429          * Get a list of all the devices from sysfs first, then
430          * compare it to the list we get back from the most up
431          * to date superblock. If there are any devices in the superblock
432          * that are not in sysfs, print out 'missing'
433          */
434         while (true) {
435                 char buf[MAX_PATH];
436                 int len;
437                 DIR *cache_dir;
438
439                 if(((cache_dir = opendir(cache_path)) == NULL) &&
440                     cache_count > MAX_DEVS)
441                         break;
442
443                 if (cache_dir)
444                         closedir(cache_dir);
445
446                 if((len = readlink(cache_path, buf, sizeof(buf) - 1)) != -1) {
447                         struct cache_sb *sb;
448                         char dev_uuid[40];
449                         char dev_path[32];
450
451                         buf[len] = '\0';
452                         dev_names[dev_count] = dev_name(buf);
453                         snprintf(dev_path, MAX_PATH, "%s/%s", "/dev",
454                                         dev_names[dev_count]);
455                         sb = query_dev(dev_path, false, false,
456                                         true, dev_uuid);
457                         if (!sb) {
458                                 printf("error reading %s\n", dev_path);
459                                 return -1;
460                         } else
461                                 free(sb);
462
463                         dev_uuids[dev_count] = strdup(dev_uuid);
464                         dev_count++;
465                 }
466
467                 cache_path[strlen(cache_path) - strlen(intbuf)] = 0;
468                 cache_count++;
469
470                 snprintf(intbuf, 4, "%d", cache_count);
471                 strcat(cache_path, intbuf);
472         }
473
474         for (i = 0; i < seq_sb->nr_in_set; i++) {
475                 char uuid_str[40];
476                 struct cache_member *m = seq_sb->members + i;
477                 char dev_state[32];
478                 int j;
479
480                 uuid_unparse(m->uuid.b, uuid_str);
481                 snprintf(dev_state, MAX_PATH, "%s",
482                          cache_state[CACHE_STATE(m)]);
483
484                 for (j = 0; j < dev_count; j++) {
485                         if (!strcmp(uuid_str, dev_uuids[j])) {
486                                 break;
487                         } else if (j == dev_count - 1) {
488                                 if (!strcmp(cache_state[CACHE_STATE(m)], "active"))
489                                         snprintf(dev_state, MAX_PATH, "%s", "missing");
490                                 break;
491                         }
492                 }
493
494                 printf("%-50s%-15s%-4llu\n", uuid_str, dev_state,
495                                 CACHE_TIER(m));
496         }
497
498         if (seq_sb)
499                 free(seq_sb);
500         for (i = 0; i < dev_count; i++) {
501                 free(dev_names[i]);
502                 free(dev_uuids[i]);
503         }
504
505         return 0;
506 }
507
508 int bcache_capacity(NihCommand *command, char *const *args)
509 {
510         char *err = "Must provide a cacheset uuid";
511         if(!capacity_uuid)
512                 goto err;
513
514         err = bcache_get_capacity(cset_dir, capacity_uuid, capacity_devs);
515         if (err)
516                 goto err;
517
518         return 0;
519
520 err:
521         printf("bcache_capacity failed with error: %s\n", err);
522         return -1;
523
524 }
525
526 static char *stats_subdir(char* stats_dir)
527 {
528         char tmp[50] = "/";
529         char *err = NULL;
530         if(stats_dev_uuid) {
531                 strcat(tmp, "cache");
532                 err = find_matching_uuid(stats_dir, tmp, stats_dev_uuid);
533                 if(err)
534                         goto err;
535         } else if(stats_cache_num) {
536                 strcat(tmp, "cache");
537                 strcat(tmp, stats_cache_num);
538         } else if (stats_five_min)
539                 strcat(tmp, "stats_five_minute");
540         else if (stats_hour)
541                 strcat(tmp, "stats_hour");
542         else if (stats_day)
543                 strcat(tmp, "stats_day");
544         else if (stats_total)
545                 strcat(tmp, "stats_total");
546         else
547                 return err;
548
549         strcat(stats_dir, tmp);
550
551 err:
552         return err;
553 }
554
555 int bcache_stats(NihCommand *command, char *const *args)
556 {
557         int i;
558         char stats_dir[MAX_PATH];
559         DIR *dir = NULL;
560         struct dirent *ent = NULL;
561         char *err = NULL;
562         char buf[MAX_PATH];
563
564         if (stats_uuid) {
565                 snprintf(stats_dir, MAX_PATH, "%s/%s", cset_dir, stats_uuid);
566                 err = stats_subdir(stats_dir);
567                 if(err)
568                         goto err;
569
570                 dir = opendir(stats_dir);
571                 if (!dir) {
572                         err = "Failed to open dir";
573                         goto err;
574                 }
575         } else {
576                 err = "Must provide a cacheset uuid";
577                 goto err;
578         }
579
580         if(stats_list || stats_all) {
581                 while ((ent = readdir(dir)) != NULL) {
582                         err = read_stat_dir(dir, stats_dir, ent->d_name, buf);
583                         if (err)
584                                 goto err;
585                         if(stats_list)
586                                 printf("%s\n", ent->d_name);
587                         if(stats_all)
588                                 printf("\t%s\n", buf);
589                 }
590         }
591
592         for (i = 0; args[i] != NULL; i++) {
593                 err = read_stat_dir(dir, stats_dir, args[i], buf);
594                 if (err)
595                         goto err;
596                 printf("%s\n", buf);
597         }
598
599         closedir(dir);
600         return 0;
601
602 err:
603         closedir(dir);
604         printf("bcache_stats error: %s\n", err);
605         return -1;
606 }
607
608 int bcache_set_failed(NihCommand *command, char *const *args)
609 {
610         char *err = NULL;
611
612         if (!dev_failed_uuid) {
613                 printf("Pass in a dev uuid\n");
614                 return -1;
615         }
616
617         err = device_set_failed(dev_failed_uuid);
618         if (err) {
619                 printf("bcache_set_failed_ioctl error: %s\n", err);
620                 return -1;
621         }
622
623         return 0;
624 }
625
626 static NihCommand commands[] = {
627         {"format", N_("format <list of drives>"),
628                   "Format one or a list of devices with bcache datastructures."
629                   " You need to do this before you create a volume",
630                   N_("format drive[s] with bcache"),
631                   NULL, bcacheadm_format_options, bcacheadm_format},
632         {"probe", N_("probe <list of devices>"),
633                   "Does a blkid_probe on a device",
634                   N_("Does a blkid_probe on a device"),
635                   NULL, probe_bcache_options, probe_bcache},
636         {"register", N_("register <list of devices>"),
637                      "Registers a list of devices",
638                      N_("Registers a list of devices"),
639                      NULL, bcache_register_options, bcache_register},
640         {"unregister", N_("unregister <list of devices>"),
641                      "Unregisters a list of devices",
642                      N_("Unregisters a list of devices"),
643                      NULL, bcache_unregister_options, bcache_unregister},
644         {"add-devs", N_("add-devs --tier=# <list of devices>"),
645                 "Adds a list of devices to a cacheset",
646                 N_("Adds a list of devices to a cacheset"),
647                 NULL, bcache_add_device_options, bcache_add_devices},
648         {"rm-dev", N_("rm-dev <dev>"),
649                 "Removes a device from its cacheset",
650                 N_("Removes a device from its cacheset"),
651                 NULL, bcache_rm_device_options, bcache_rm_device},
652         {"modify", N_("modify --set=UUID (dev=UUID) name value"),
653                 "Modifies attributes related to the cacheset",
654                 N_("Modifies attributes related to the cacheset"),
655                 NULL, bcache_modify_options, bcache_modify},
656         {"list-cachesets", N_("list-cachesets"),
657                            "Lists cachesets in /sys/fs/bcache",
658                            N_("Lists cachesets in /sys/fs/bcache"),
659                            NULL, list_cachesets_options, bcache_list_cachesets},
660         {"query-devs", N_("query <list of devices>"),
661                        "Gives info about the superblock of a list of devices",
662                        N_("show superblock on each of the listed drive"),
663                        NULL, query_devs_options, bcache_query_devs},
664         {"status", N_("status <list of devices>"),
665                    "Finds the status of the most up to date superblock",
666                    N_("Finds the status of the most up to date superblock"),
667                    NULL, status_options, bcache_status},
668         {"capacity", N_("capacity --set=UUID"),
669                 "Shows the capacity of the cacheset",
670                 N_("Shows the capacity of the cacheset"),
671                 NULL, capacity_options, bcache_capacity},
672         {"stats", N_("stats <list of devices>"),
673                   "List various bcache statistics",
674                   N_("List various bcache statistics"),
675                   NULL, stats_options, bcache_stats},
676         {"set-failed", N_("set-failed --dev=UUID"),
677                 "Sets a device to the FAILED state",
678                 N_("Sets a device to the FAILED state"),
679                 NULL, set_failed_options, bcache_set_failed},
680         NIH_COMMAND_LAST
681 };
682
683 int main(int argc, char *argv[])
684 {
685         int ret = 0;
686         nih_main_init (argv[0]);
687
688         nih_option_set_synopsis (_("Manage bcache devices"));
689         nih_option_set_help (
690                         _("Helps you manage bcache devices"));
691
692         ret = nih_command_parser (NULL, argc, argv, options, commands);
693         if (ret < 0)
694                 exit (1);
695
696         nih_signal_reset();
697
698         return 0;
699 }