]> git.sesse.net Git - bcachefs-tools-debian/blob - bcacheadm-query.c
6e3499c864fffe08e8cbe22e99ef22b9e0754cc4
[bcachefs-tools-debian] / bcacheadm-query.c
1
2 #include <errno.h>
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7
8 #include <fcntl.h>
9 #include <sys/ioctl.h>
10 #include <sys/stat.h>
11 #include <unistd.h>
12
13 #include <nih/command.h>
14 #include <nih/option.h>
15
16 #include <uuid/uuid.h>
17
18 #include "bcache.h"
19 #include "bcacheadm-query.h"
20
21 static char *cset_dir = "/sys/fs/bcache";
22 static bool list_devs = false;
23 static const char *internal_uuid = NULL;
24
25 NihOption opts_list[] = {
26         {'d', "dir", N_("directory"), NULL, NULL, &cset_dir, NULL},
27         {0, "list-devs", N_("list all devices in the cache sets as well"), NULL, NULL, &list_devs, NULL},
28         {0, "internal_uuid", N_("Show the internal UUID for the given cacheset UUID"), NULL, "UUID", &internal_uuid, NULL},
29         NIH_OPTION_LAST
30 };
31
32 static void list_cacheset_devs(char *cset_dir, char *cset_name, bool parse_dev_name)
33 {
34         DIR *cachedir, *dir;
35         struct stat cache_stat;
36         char entry[MAX_PATH];
37         struct dirent *ent;
38         snprintf(entry, MAX_PATH, "%s/%s", cset_dir, cset_name);
39
40         if((dir = opendir(entry)) != NULL) {
41                 while((ent = readdir(dir)) != NULL) {
42                         char buf[MAX_PATH];
43                         int len;
44                         char *tmp;
45
46                         /*
47                          * We are looking for all cache# directories
48                          * do a strlen < 9 to skip over other entries
49                          * that also start with "cache"
50                          */
51                         if(strncmp(ent->d_name, "cache", 5) ||
52                                         !(strlen(ent->d_name) < 9))
53                                 continue;
54
55                         snprintf(entry, MAX_PATH, "%s/%s/%s",
56                                         cset_dir,
57                                         cset_name,
58                                         ent->d_name);
59
60                         if((cachedir = opendir(entry)) == NULL)
61                                 continue;
62
63                         if(stat(entry, &cache_stat))
64                                 continue;
65
66                         if((len = readlink(entry, buf, sizeof(buf) - 1)) !=
67                                         -1) {
68                                 buf[len] = '\0';
69                                 if(parse_dev_name) {
70                                         tmp = dev_name(buf);
71                                         printf("/dev%s\n", tmp);
72                                         free(tmp);
73                                 } else {
74                                         printf("\t%s\n", buf);
75                                 }
76                         }
77                 }
78         }
79 }
80
81 static char *list_cachesets(char *cset_dir, bool list_devs)
82 {
83         struct dirent *ent;
84         DIR *dir;
85         char *err = NULL;
86
87         dir = opendir(cset_dir);
88         if (!dir) {
89                 err = "Failed to open cacheset dir";
90                 goto err;
91         }
92
93         while ((ent = readdir(dir)) != NULL) {
94                 struct stat statbuf;
95                 char entry[MAX_PATH];
96
97                 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
98                         continue;
99
100                 snprintf(entry, MAX_PATH, "%s/%s", cset_dir, ent->d_name);
101                 if(stat(entry, &statbuf) == -1) {
102                         err = "Failed to stat cacheset subdir";
103                         goto err;
104                 }
105
106                 if (S_ISDIR(statbuf.st_mode)) {
107                         printf("%s\n", ent->d_name);
108
109                         if(list_devs) {
110                                 list_cacheset_devs(cset_dir, ent->d_name, true);
111                         }
112                 }
113         }
114
115 err:
116         closedir(dir);
117         return err;
118 }
119
120 static char *read_stat_dir(DIR *dir, char *stats_dir, char *stat_name, char *ret)
121 {
122         struct stat statbuf;
123         char entry[MAX_PATH];
124         char *err = NULL;
125
126         snprintf(entry, MAX_PATH, "%s/%s", stats_dir, stat_name);
127         if(stat(entry, &statbuf) == -1) {
128                 char tmp[MAX_PATH];
129                 snprintf(tmp, MAX_PATH, "Failed to stat %s\n", entry);
130                 err = strdup(tmp);
131                 goto err;
132         }
133
134         if (S_ISREG(statbuf.st_mode)) {
135                 FILE *fp = NULL;
136
137                 fp = fopen(entry, "r");
138                 if(!fp) {
139                         /* If we can't open the file, this is probably because
140                          * of permissions, just move to the next file */
141                         return NULL;
142                 }
143
144                 while(fgets(ret, MAX_PATH, fp));
145                 fclose(fp);
146         }
147 err:
148         return err;
149 }
150
151 int cmd_list(NihCommand *command, char *const *args)
152 {
153         char *err = NULL;
154
155         if (internal_uuid) {
156                 char uuid_path[MAX_PATH];
157                 DIR *uuid_dir;
158                 char buf[MAX_PATH];
159
160                 snprintf(uuid_path, MAX_PATH, "%s/%s", cset_dir, internal_uuid);
161
162                 err = "uuid does not exist";
163                 if((uuid_dir = opendir(uuid_path)) == NULL)
164                         goto err;
165
166                 err = read_stat_dir(uuid_dir, uuid_path, "/internal/internal_uuid", buf);
167                 if (err)
168                         goto err;
169                 printf("%s", buf);
170                 return 0;
171         }
172
173         err = list_cachesets(cset_dir, list_devs);
174         if (err)
175                 goto err;
176
177         return 0;
178
179 err:
180         printf("bcache_list_cachesets error :%s\n", err);
181         return -1;
182 }
183
184 static bool force_csum = false;
185 static bool uuid_only = false;
186 static bool query_brief = false;
187
188 NihOption opts_query[] = {
189         {'f', "force_csum", N_("force_csum"), NULL, NULL, &force_csum, NULL},
190         {'u', "uuid-only", N_("only print out the uuid for the devices, not the whole superblock"), NULL, NULL, &uuid_only, NULL},
191         {'b', "brief", N_("only print out the cluster,server,and disk uuids"), NULL, NULL, &query_brief, NULL},
192         NIH_OPTION_LAST
193 };
194
195 int cmd_query(NihCommand *command, char *const *args)
196 {
197         int i;
198
199         if (query_brief)
200                 printf("%-10s%-40s%-40s%-40s\n", "dev name", "disk uuid",
201                                 "server uuid", "cluster uuid");
202
203         for (i = 0; args[i] != NULL; i++) {
204                 char dev_uuid[40];
205                 struct cache_sb *sb = query_dev(args[i], force_csum,
206                                 !query_brief, uuid_only, dev_uuid);
207
208                 if (!sb) {
209                         printf("error opening the superblock for %s\n",
210                                         args[i]);
211                         return -1;
212                 }
213
214                 if (uuid_only) {
215                         printf("%s\n", dev_uuid);
216                 } else if (query_brief) {
217                         char set_uuid_str[40], dev_uuid_str[40];
218                         char *clus_uuid = (char *)sb->label;
219
220                         uuid_unparse(sb->user_uuid.b, set_uuid_str);
221                         uuid_unparse(sb->disk_uuid.b, dev_uuid_str);
222                         if (!strcmp(clus_uuid, ""))
223                                 clus_uuid = "None";
224
225                         printf("%-10s%-40s%-40s%-40s\n", args[i],
226                                         dev_uuid_str,
227                                         set_uuid_str,
228                                         clus_uuid);
229                 }
230                 free(sb);
231         }
232
233         return 0;
234 }
235
236 static bool status_all = false;
237
238 NihOption opts_status[] = {
239         {'a', "all", N_("all"), NULL, NULL, &status_all, NULL},
240         NIH_OPTION_LAST
241 };
242
243 int cmd_status(NihCommand *command, char *const *args)
244 {
245         int i, dev_count = 0, seq, cache_count = 0;
246         struct cache_sb *seq_sb = NULL;
247         char cache_path[MAX_PATH];
248         char *dev_names[MAX_DEVS];
249         char *dev_uuids[MAX_DEVS];
250         char intbuf[4];
251         char set_uuid[40];
252
253         for (i = 0; args[i] != NULL; i++) {
254                 struct cache_sb *sb = query_dev(args[i], false, false,
255                                 false, NULL);
256
257                 if (!sb) {
258                         printf("Unable to open superblock, bad path\n");
259                         return -1;
260                 }
261
262                 if (!seq_sb || sb->seq > seq) {
263                         seq = sb->seq;
264                         seq_sb = sb;
265                 } else
266                         free(sb);
267         }
268
269         if (!seq_sb) {
270                 printf("Unable to find a superblock\n");
271                 return -1;
272         } else {
273                 uuid_unparse(seq_sb->user_uuid.b, set_uuid);
274                 printf("%-50s%-15s%-4s\n", "uuid", "state", "tier");
275         }
276
277         snprintf(intbuf, 4, "%d", i);
278         snprintf(cache_path, MAX_PATH, "%s/%s/%s", cset_dir, set_uuid,
279                         "cache0");
280
281         /*
282          * Get a list of all the devices from sysfs first, then
283          * compare it to the list we get back from the most up
284          * to date superblock. If there are any devices in the superblock
285          * that are not in sysfs, print out 'missing'
286          */
287         while (true) {
288                 char buf[MAX_PATH];
289                 int len;
290                 DIR *cache_dir;
291
292                 if(((cache_dir = opendir(cache_path)) == NULL) &&
293                     cache_count > MAX_DEVS)
294                         break;
295
296                 if (cache_dir)
297                         closedir(cache_dir);
298
299                 if((len = readlink(cache_path, buf, sizeof(buf) - 1)) != -1) {
300                         struct cache_sb *sb;
301                         char dev_uuid[40];
302                         char dev_path[32];
303
304                         buf[len] = '\0';
305                         dev_names[dev_count] = dev_name(buf);
306                         snprintf(dev_path, sizeof(dev_path), "%s/%s", "/dev",
307                                         dev_names[dev_count]);
308                         sb = query_dev(dev_path, false, false,
309                                         true, dev_uuid);
310                         if (!sb) {
311                                 printf("error reading %s\n", dev_path);
312                                 return -1;
313                         } else
314                                 free(sb);
315
316                         dev_uuids[dev_count] = strdup(dev_uuid);
317                         dev_count++;
318                 }
319
320                 cache_path[strlen(cache_path) - strlen(intbuf)] = 0;
321                 cache_count++;
322
323                 snprintf(intbuf, 4, "%d", cache_count);
324                 strcat(cache_path, intbuf);
325         }
326
327         for (i = 0; i < seq_sb->nr_in_set; i++) {
328                 char uuid_str[40];
329                 struct cache_member *m = seq_sb->members + i;
330                 char dev_state[32];
331                 int j;
332
333                 uuid_unparse(m->uuid.b, uuid_str);
334                 snprintf(dev_state, sizeof(dev_state), "%s",
335                          cache_state[CACHE_STATE(m)]);
336
337                 for (j = 0; j < dev_count; j++) {
338                         if (!strcmp(uuid_str, dev_uuids[j])) {
339                                 break;
340                         } else if (j == dev_count - 1) {
341                                 if (!strcmp(cache_state[CACHE_STATE(m)], "active"))
342                                         snprintf(dev_state, sizeof(dev_state), "%s", "missing");
343                                 break;
344                         }
345                 }
346
347                 printf("%-50s%-15s%-4llu\n", uuid_str, dev_state,
348                                 CACHE_TIER(m));
349         }
350
351         if (seq_sb)
352                 free(seq_sb);
353         for (i = 0; i < dev_count; i++) {
354                 free(dev_names[i]);
355                 free(dev_uuids[i]);
356         }
357
358         return 0;
359 }