mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-02-09 00:00:04 +03:00
360 lines
7.7 KiB
C
360 lines
7.7 KiB
C
|
|
#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 <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;
|
|
}
|