6e6930527cda5c392b93994e19034769cc11ced8
[dragonfly.git] / sbin / hammer / cmd_info.c
1 /*
2  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Antonio Huete <tuxillo@quantumachine.net>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  */
35 #include "hammer.h"
36 #include <libutil.h>
37
38 void    show_info(char *path);
39 char    *find_pfs_mount(int pfsid, uuid_t parentuuid, int ismaster);
40 double  percent(int64_t value, int64_t total);
41 u_int32_t count_snapshots (u_int32_t version,
42     char *pfs_snapshots, char *mountedon, int *errorp);
43
44 void
45 hammer_cmd_info(void)
46 {
47         struct statfs *stfsbuf;
48         int mntsize, i, first = 1;
49         char *fstype, *path;
50
51         tzset();
52         mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
53         if (mntsize > 0) {
54                 for (i = 0; i < mntsize; i++) {
55                         fstype = stfsbuf[i].f_fstypename;
56                         path = stfsbuf[i].f_mntonname;
57                         if ((strcmp(fstype, "hammer")) == 0) {
58                                 if (first)
59                                         first = 0;
60                                 else
61                                         fprintf(stdout, "\n");
62                                 show_info(path);
63                         }
64                 }
65         } else {
66                 fprintf(stdout, "No mounted filesystems found\n");
67         }
68
69 }
70
71 void
72 show_info(char *path)
73 {
74         struct      hammer_pseudofs_data pfs_od;
75         struct      hammer_ioc_pseudofs_rw pfs;
76         int64_t     usedbigblocks;
77         int64_t     usedbytes, rsvbytes;
78         int64_t     totalbytes, freebytes;
79         struct      hammer_ioc_info info;
80         int         fd, pfs_id, ismaster, error;
81         char        *fsid;
82         char        *mountedon;
83         char        buf[6];
84         u_int32_t   sc;
85
86         fsid = mountedon = NULL;
87         usedbigblocks = 0;
88         pfs_id = 0;           /* Include PFS#0 */
89         usedbytes = totalbytes = rsvbytes = freebytes = 0;
90         sc = error = 0;
91
92         bzero(&info, sizeof(struct hammer_ioc_info));
93
94         /* Try to get a file descriptor based on the path given */
95         fd = open(path, O_RDONLY);
96         if (fd < 0) {
97                 perror("show_info");
98                 exit(EXIT_FAILURE);
99         }
100
101         if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) {
102                 perror("show_info");
103                 exit(EXIT_FAILURE);
104         }
105
106         /* Find out the UUID strings */
107         uuid_to_string(&info.vol_fsid, &fsid, NULL);
108
109         /* Volume information */
110         fprintf(stdout, "Volume identification\n");
111         fprintf(stdout, "\tLabel               %s\n", info.vol_name);
112         fprintf(stdout, "\tNo. Volumes         %d\n", info.nvolumes);
113         fprintf(stdout, "\tFSID                %s\n", fsid);
114         fprintf(stdout, "\tHAMMER Version      %d\n", info.version);
115
116         /* Big blocks information */
117         usedbigblocks = info.bigblocks - info.freebigblocks;
118
119         fprintf(stdout, "Big block information\n");
120         fprintf(stdout, "\tTotal      %10jd\n", (intmax_t)info.bigblocks);
121         fprintf(stdout, "\tUsed       %10jd (%.2lf%%)\n"
122                         "\tReserved   %10jd (%.2lf%%)\n"
123                         "\tFree       %10jd (%.2lf%%)\n",
124                         (intmax_t)usedbigblocks,
125                         percent(usedbigblocks, info.bigblocks),
126                         (intmax_t)info.rsvbigblocks,
127                         percent(info.rsvbigblocks, info.bigblocks),
128                         (intmax_t)(info.freebigblocks - info.rsvbigblocks),
129                         percent(info.freebigblocks - info.rsvbigblocks,
130                                 info.bigblocks));
131         fprintf(stdout, "Space information\n");
132
133         /* Space information */
134         totalbytes = (info.bigblocks << HAMMER_LARGEBLOCK_BITS);
135         usedbytes = (usedbigblocks << HAMMER_LARGEBLOCK_BITS);
136         rsvbytes = (info.rsvbigblocks << HAMMER_LARGEBLOCK_BITS);
137         freebytes = ((info.freebigblocks - info.rsvbigblocks)
138             << HAMMER_LARGEBLOCK_BITS);
139
140         fprintf(stdout, "\tNo. Inodes %10jd\n", (intmax_t)info.inodes);
141         humanize_number(buf, sizeof(buf)  - (totalbytes < 0 ? 0 : 1),
142             totalbytes, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
143         fprintf(stdout, "\tTotal size     %6s (%jd bytes)\n",
144             buf, (intmax_t)totalbytes);
145
146         humanize_number(buf, sizeof(buf)  - (usedbytes < 0 ? 0 : 1),
147             usedbytes, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
148         fprintf(stdout, "\tUsed           %6s (%.2lf%%)\n", buf,
149             percent(usedbytes, totalbytes));
150
151         humanize_number(buf, sizeof(buf)  - (rsvbytes < 0 ? 0 : 1),
152             rsvbytes, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
153         fprintf(stdout, "\tReserved       %6s (%.2lf%%)\n", buf,
154             percent(rsvbytes, totalbytes));
155
156         humanize_number(buf, sizeof(buf)  - (freebytes < 0 ? 0 : 1),
157             freebytes, "", HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
158         fprintf(stdout, "\tFree           %6s (%.2lf%%)\n", buf,
159             percent(freebytes, totalbytes));
160
161         /* Pseudo-filesystem information */
162         fprintf(stdout, "PFS information\n");
163         fprintf(stdout, "\tPFS ID  Mode    Snaps  Mounted on\n");
164
165         while(pfs_id < HAMMER_MAX_PFS) {
166                 bzero(&pfs, sizeof(pfs));
167                 bzero(&pfs_od, sizeof(pfs_od));
168                 pfs.pfs_id = pfs_id;
169                 pfs.ondisk = &pfs_od;
170                 pfs.bytes = sizeof(pfs_od);
171                 pfs.version = HAMMER_IOC_PSEUDOFS_VERSION;
172                 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) >= 0) {
173                         ismaster = (pfs_od.mirror_flags & HAMMER_PFSD_SLAVE)
174                             ? 0 : 1;
175                         if (pfs_id == 0)
176                                 mountedon = strdup(path);
177                         else
178                                 mountedon = find_pfs_mount(pfs_id,
179                                     info.vol_fsid, ismaster);
180
181                         sc = count_snapshots(info.version, pfs_od.snapshots,
182                             mountedon, &error);
183
184                         fprintf(stdout, "\t%6d  %-6s",
185                             pfs_id, (ismaster ? "MASTER" : "SLAVE"));
186
187                         snprintf(buf, 6, "%d", sc);
188                         fprintf(stdout, " %6s  ", (error && sc == 0) ? "-" : buf);
189
190                         if (mountedon)
191                                 fprintf(stdout, "%s", mountedon);
192                         else
193                                 fprintf(stdout, "not mounted");
194                         fprintf(stdout, "\n");
195                 }
196                 pfs_id++;
197         }
198
199         free(fsid);
200         free(mountedon);
201 }
202
203 char *
204 find_pfs_mount(int pfsid, uuid_t parentuuid, int ismaster)
205 {
206         struct hammer_ioc_info hi;
207         struct statfs *mntbuf;
208         int mntsize;
209         int curmount;
210         int fd;
211         size_t  mntbufsize;
212         char *trailstr;
213         char *retval;
214
215         retval = NULL;
216
217         /* Do not continue if there are no mounted filesystems */
218         mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
219         if (mntsize <= 0)
220                 return retval;
221
222         mntbufsize = (mntsize) * sizeof(struct statfs);
223         mntbuf = malloc(mntbufsize);
224         if (mntbuf == NULL) {
225                 perror("show_info");
226                 exit(EXIT_FAILURE);
227         }
228
229         mntsize = getfsstat(mntbuf, (long)mntbufsize, MNT_NOWAIT);
230         curmount = mntsize - 1;
231
232         asprintf(&trailstr, ":%05d", pfsid);
233
234         /*
235          * Iterate all the mounted points looking for the PFS passed to
236          * this function.
237          */
238         while(curmount >= 0) {
239                 /*
240                  * We need to avoid that PFS belonging to other HAMMER
241                  * filesystems are showed as mounted, so we compare
242                  * against the FSID, which is presumable to be unique.
243                  */
244                 bzero(&hi, sizeof(hi));
245                 if ((fd = open(mntbuf[curmount].f_mntfromname, O_RDONLY)) < 0) {
246                         curmount--;
247                         continue;
248                 }
249
250                 if ((ioctl(fd, HAMMERIOC_GET_INFO, &hi)) < 0) {
251                         curmount--;
252                         continue;
253                 }
254
255                 if (strstr(mntbuf[curmount].f_mntfromname, trailstr) != NULL &&
256                     (uuid_compare(&hi.vol_fsid, &parentuuid, NULL)) == 0) {
257                         if (ismaster) {
258                                 if (strstr(mntbuf[curmount].f_mntfromname,
259                                     "@@-1") != NULL) {
260                                         retval =
261                                             strdup(mntbuf[curmount].f_mntonname);
262                                         break;
263                                 }
264                         } else {
265                                 if (strstr(mntbuf[curmount].f_mntfromname,
266                                     "@@0x") != NULL ) {
267                                         retval =
268                                             strdup(mntbuf[curmount].f_mntonname);
269                                         break;
270                                 }
271                         }
272                 }
273                 curmount--;
274         }
275         free(trailstr);
276         return retval;
277 }
278
279 double
280 percent(int64_t value, int64_t total)
281 {
282         /* Avoid divide-by-zero */
283         if (total == 0)
284                 return 100.0;
285
286         return ((value * 100.0) / (double)total);
287 }
288
289 u_int32_t
290 count_snapshots(u_int32_t version, char *pfs_snapshots, char *mountedon, int *errorp)
291 {
292         struct hammer_ioc_snapshot snapinfo;
293         char *snapshots_path, *fpath;
294         struct dirent *den;
295         struct stat st;
296         DIR *dir;
297         u_int32_t snapshot_count = 0;
298         int fd;
299
300         bzero(&snapinfo, sizeof(struct hammer_ioc_snapshot));
301
302         fd = open(mountedon, O_RDONLY);
303         if (fd < 0) {
304                 *errorp = errno;
305                 return 0;
306         }
307
308         if (version < 3) {
309                 /*
310                  * old style: count the number of softlinks in the snapshots dir
311                  */
312                 if (pfs_snapshots[0])
313                         snapshots_path = pfs_snapshots;
314                 else
315                         asprintf(&snapshots_path, "%s/snapshots", mountedon);
316                 if ((dir = opendir(snapshots_path)) != NULL) {
317                         while ((den = readdir(dir)) != NULL) {
318                                 if (den->d_name[0] == '.')
319                                         continue;
320                                 asprintf(&fpath, "%s/%s", snapshots_path,
321                                     den->d_name);
322                                 if (lstat(fpath, &st) == 0 &&
323                                     S_ISLNK(st.st_mode))
324                                         snapshot_count++;
325                                 free(fpath);
326                         }
327                         closedir(dir);
328                 }
329         } else {
330                 /*
331                  * new style: file system meta-data
332                  */
333                 do {
334                         if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapinfo) < 0) {
335                                 *errorp = errno;
336                                 return 0;
337                         }
338                         snapshot_count += snapinfo.count;
339                 } while (snapinfo.head.error == 0 && snapinfo.count);
340         }
341         return snapshot_count;
342 }