diff options
Diffstat (limited to 'bcacheadm-query.c')
-rw-r--r-- | bcacheadm-query.c | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/bcacheadm-query.c b/bcacheadm-query.c new file mode 100644 index 00000000..b3f5e390 --- /dev/null +++ b/bcacheadm-query.c @@ -0,0 +1,361 @@ + +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <unistd.h> + +#include <nih/command.h> +#include <nih/option.h> + +#include <linux/bcache-ioctl.h> + +#include <uuid/uuid.h> + +#include "bcache.h" +#include "bcacheadm-query.h" + +static char *cset_dir = "/sys/fs/bcache"; +static bool list_devs = false; +static const char *internal_uuid = NULL; + +NihOption opts_list[] = { + {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL}, + {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL}, + {0, "internal_uuid", N_("Show the internal UUID for the given cacheset UUID"), NULL, "UUID", &internal_uuid, NULL}, + NIH_OPTION_LAST +}; + +static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_name) +{ + DIR *cachedir, *dir; + struct stat cache_stat; + char entry[MAX_PATH]; + struct dirent *ent; + snprintf(entry, MAX_PATH, "%s/%s", cset_dir, cset_name); + + if((dir = opendir(entry)) != NULL) { + while((ent = readdir(dir)) != NULL) { + char buf[MAX_PATH]; + int len; + char *tmp; + + /* + * We are looking for all cache# directories + * do a strlen < 9 to skip over other entries + * that also start with "cache" + */ + if(strncmp(ent->d_name, "cache", 5) || + !(strlen(ent->d_name) < 9)) + continue; + + snprintf(entry, MAX_PATH, "%s/%s/%s", + cset_dir, + cset_name, + ent->d_name); + + if((cachedir = opendir(entry)) == NULL) + continue; + + if(stat(entry, &cache_stat)) + continue; + + if((len = readlink(entry, buf, sizeof(buf) - 1)) != + -1) { + buf[len] = '\0'; + if(parse_dev_name) { + tmp = dev_name(buf); + printf("/dev%s\n", tmp); + free(tmp); + } else { + printf("\t%s\n", buf); + } + } + } + } +} + +static char *list_cachesets(char *cset_dir, bool list_devs) +{ + struct dirent *ent; + DIR *dir; + char *err = NULL; + + dir = opendir(cset_dir); + if (!dir) { + err = "Failed to open cacheset dir"; + goto err; + } + + while ((ent = readdir(dir)) != NULL) { + struct stat statbuf; + char entry[MAX_PATH]; + + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + + snprintf(entry, MAX_PATH, "%s/%s", cset_dir, ent->d_name); + if(stat(entry, &statbuf) == -1) { + err = "Failed to stat cacheset subdir"; + goto err; + } + + if (S_ISDIR(statbuf.st_mode)) { + printf("%s\n", ent->d_name); + + if(list_devs) { + list_cacheset_devs(cset_dir, ent->d_name, true); + } + } + } + +err: + closedir(dir); + return err; +} + +static char *read_stat_dir(DIR *dir, char *stats_dir, char *stat_name, char *ret) +{ + struct stat statbuf; + char entry[MAX_PATH]; + char *err = NULL; + + snprintf(entry, MAX_PATH, "%s/%s", stats_dir, stat_name); + if(stat(entry, &statbuf) == -1) { + char tmp[MAX_PATH]; + snprintf(tmp, MAX_PATH, "Failed to stat %s\n", entry); + err = strdup(tmp); + goto err; + } + + if (S_ISREG(statbuf.st_mode)) { + FILE *fp = NULL; + + fp = fopen(entry, "r"); + if(!fp) { + /* If we can't open the file, this is probably because + * of permissions, just move to the next file */ + return NULL; + } + + while(fgets(ret, MAX_PATH, fp)); + fclose(fp); + } +err: + return err; +} + +int cmd_list(NihCommand *command, char *const *args) +{ + char *err = NULL; + + if (internal_uuid) { + char uuid_path[MAX_PATH]; + DIR *uuid_dir; + char buf[MAX_PATH]; + + snprintf(uuid_path, MAX_PATH, "%s/%s", cset_dir, internal_uuid); + + err = "uuid does not exist"; + if((uuid_dir = opendir(uuid_path)) == NULL) + goto err; + + err = read_stat_dir(uuid_dir, uuid_path, "/internal/internal_uuid", buf); + if (err) + goto err; + printf("%s", buf); + return 0; + } + + err = list_cachesets(cset_dir, list_devs); + if (err) + goto err; + + return 0; + +err: + printf("bcache_list_cachesets error :%s\n", err); + return -1; +} + +static bool force_csum = false; +static bool uuid_only = false; +static bool query_brief = false; + +NihOption opts_query[] = { + {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL}, + {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL}, + {'b', "brief", N_("only print out the cluster,server,and disk uuids"), NULL, NULL, &query_brief, NULL}, + NIH_OPTION_LAST +}; + +int cmd_query(NihCommand *command, char *const *args) +{ + int i; + + if (query_brief) + printf("%-10s%-40s%-40s%-40s\n", "dev name", "disk uuid", + "server uuid", "cluster uuid"); + + for (i = 0; args[i] != NULL; i++) { + char dev_uuid[40]; + struct cache_sb *sb = query_dev(args[i], force_csum, + !query_brief, uuid_only, dev_uuid); + + if (!sb) { + printf("error opening the superblock for %s\n", + args[i]); + return -1; + } + + if (uuid_only) { + printf("%s\n", dev_uuid); + } else if (query_brief) { + char set_uuid_str[40], dev_uuid_str[40]; + char *clus_uuid = (char *)sb->label; + + uuid_unparse(sb->user_uuid.b, set_uuid_str); + uuid_unparse(sb->disk_uuid.b, dev_uuid_str); + if (!strcmp(clus_uuid, "")) + clus_uuid = "None"; + + printf("%-10s%-40s%-40s%-40s\n", args[i], + dev_uuid_str, + set_uuid_str, + clus_uuid); + } + free(sb); + } + + return 0; +} + +static bool status_all = false; + +NihOption opts_status[] = { + {'a', "all", N_("all"), NULL, NULL, &status_all, NULL}, + NIH_OPTION_LAST +}; + +int cmd_status(NihCommand *command, char *const *args) +{ + int i, dev_count = 0, seq, cache_count = 0; + struct cache_sb *seq_sb = NULL; + char cache_path[MAX_PATH]; + char *dev_names[MAX_DEVS]; + char *dev_uuids[MAX_DEVS]; + char intbuf[4]; + char set_uuid[40]; + + for (i = 0; args[i] != NULL; i++) { + struct cache_sb *sb = query_dev(args[i], false, false, + false, NULL); + + if (!sb) { + printf("Unable to open superblock, bad path\n"); + return -1; + } + + if (!seq_sb || sb->seq > seq) { + seq = sb->seq; + seq_sb = sb; + } else + free(sb); + } + + if (!seq_sb) { + printf("Unable to find a superblock\n"); + return -1; + } else { + uuid_unparse(seq_sb->user_uuid.b, set_uuid); + printf("%-50s%-15s%-4s\n", "uuid", "state", "tier"); + } + + snprintf(intbuf, 4, "%d", i); + snprintf(cache_path, MAX_PATH, "%s/%s/%s", cset_dir, set_uuid, + "cache0"); + + /* + * Get a list of all the devices from sysfs first, then + * compare it to the list we get back from the most up + * to date superblock. If there are any devices in the superblock + * that are not in sysfs, print out 'missing' + */ + while (true) { + char buf[MAX_PATH]; + int len; + DIR *cache_dir; + + if(((cache_dir = opendir(cache_path)) == NULL) && + cache_count > MAX_DEVS) + break; + + if (cache_dir) + closedir(cache_dir); + + if((len = readlink(cache_path, buf, sizeof(buf) - 1)) != -1) { + struct cache_sb *sb; + char dev_uuid[40]; + char dev_path[32]; + + buf[len] = '\0'; + dev_names[dev_count] = dev_name(buf); + snprintf(dev_path, MAX_PATH, "%s/%s", "/dev", + dev_names[dev_count]); + sb = query_dev(dev_path, false, false, + true, dev_uuid); + if (!sb) { + printf("error reading %s\n", dev_path); + return -1; + } else + free(sb); + + dev_uuids[dev_count] = strdup(dev_uuid); + dev_count++; + } + + cache_path[strlen(cache_path) - strlen(intbuf)] = 0; + cache_count++; + + snprintf(intbuf, 4, "%d", cache_count); + strcat(cache_path, intbuf); + } + + for (i = 0; i < seq_sb->nr_in_set; i++) { + char uuid_str[40]; + struct cache_member *m = seq_sb->members + i; + char dev_state[32]; + int j; + + uuid_unparse(m->uuid.b, uuid_str); + snprintf(dev_state, MAX_PATH, "%s", + cache_state[CACHE_STATE(m)]); + + for (j = 0; j < dev_count; j++) { + if (!strcmp(uuid_str, dev_uuids[j])) { + break; + } else if (j == dev_count - 1) { + if (!strcmp(cache_state[CACHE_STATE(m)], "active")) + snprintf(dev_state, MAX_PATH, "%s", "missing"); + break; + } + } + + printf("%-50s%-15s%-4llu\n", uuid_str, dev_state, + CACHE_TIER(m)); + } + + if (seq_sb) + free(seq_sb); + for (i = 0; i < dev_count; i++) { + free(dev_names[i]); + free(dev_uuids[i]); + } + + return 0; +} |