Merge branch 'vendor/NCURSES'
[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(int fd, u_int32_t version, char *pfs_snapshots, char *mountedon);
42
43 void
44 hammer_cmd_info(void)
45 {
46         struct statfs *stfsbuf;
47         int mntsize, i, first = 1;
48         char *fstype, *path;
49
50         tzset();
51         mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
52         if (mntsize > 0) {
53                 for (i=0; i < mntsize; i++) {
54                         fstype = stfsbuf[i].f_fstypename;
55                         path = stfsbuf[i].f_mntonname;
56                         if ((strcmp(fstype, "hammer")) == 0) {
57                                 if (first)
58                                         first = 0;
59                                 else
60                                         fprintf(stdout, "\n");
61                                 show_info(path);
62                         }
63                 }
64         } else {
65                 fprintf(stdout, "No mounted filesystems found\n");
66         }
67
68 }
69
70 void
71 show_info(char *path)
72 {
73         struct      hammer_pseudofs_data pfs_od;
74         struct      hammer_ioc_pseudofs_rw pfs;
75         int64_t     usedbigblocks, bytes;
76         struct      hammer_ioc_info info;
77         int         fd, pfs_id, ismaster;
78         char        *fsid;
79         char        *mountedon;
80         char        buf[6];
81         u_int32_t   sc;
82
83         fsid = mountedon = NULL;
84         usedbigblocks = 0;
85         pfs_id = 0;           /* Include PFS#0 */
86         bytes = 0;
87         sc = 0;
88
89         bzero(&info, sizeof(struct hammer_ioc_info));
90
91         /* Try to get a file descriptor based on the path given */
92         fd = open(path, O_RDONLY);
93         if (fd < 0) {
94                 perror("show_info");
95                 exit(EXIT_FAILURE);
96         }
97
98         if ((ioctl(fd, HAMMERIOC_GET_INFO, &info)) < 0) {
99                 perror("show_info");
100                 exit(EXIT_FAILURE);
101         }
102
103         /* Find out the UUID strings */
104         uuid_to_string(&info.vol_fsid, &fsid, NULL);
105
106         /* Volume information */
107         fprintf(stdout, "Volume identification\n");
108         fprintf(stdout, "\tLabel               %s\n", info.vol_name);
109         fprintf(stdout, "\tNo. Volumes         %d\n", info.nvolumes);
110         fprintf(stdout, "\tFSID                %s\n", fsid);
111         fprintf(stdout, "\tHAMMER Version      %d\n", info.version);
112
113         /* Big blocks information */
114         usedbigblocks = info.bigblocks - info.freebigblocks;
115
116         fprintf(stdout, "Big block information\n");
117         fprintf(stdout, "\tTotal      %10jd\n", (intmax_t)info.bigblocks);
118         fprintf(stdout, "\tUsed       %10jd (%.2lf%%)\n"
119                         "\tReserved   %10jd (%.2lf%%)\n"
120                         "\tFree       %10jd (%.2lf%%)\n",
121                         (intmax_t)usedbigblocks,
122                         percent(usedbigblocks, info.bigblocks),
123                         (intmax_t)info.rsvbigblocks,
124                         percent(info.rsvbigblocks, info.bigblocks),
125                         (intmax_t)(info.freebigblocks - info.rsvbigblocks),
126                         percent(info.freebigblocks - info.rsvbigblocks,
127                                 info.bigblocks));
128         fprintf(stdout, "Space information\n");
129
130         /* Space information */
131         fprintf(stdout, "\tNo. Inodes %10jd\n", (intmax_t)info.inodes);
132         bytes = (info.bigblocks << HAMMER_LARGEBLOCK_BITS);
133         humanize_number(buf, sizeof(buf)  - (bytes < 0 ? 0 : 1), bytes, "",
134                         HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
135         fprintf(stdout, "\tTotal size     %6s (%jd bytes)\n",
136                 buf, (intmax_t)bytes);
137
138         bytes = (usedbigblocks << HAMMER_LARGEBLOCK_BITS);
139         humanize_number(buf, sizeof(buf)  - (bytes < 0 ? 0 : 1), bytes, "",
140                         HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
141         fprintf(stdout, "\tUsed space     %6s\n", buf);
142
143         bytes = (info.rsvbigblocks << HAMMER_LARGEBLOCK_BITS);
144         humanize_number(buf, sizeof(buf)  - (bytes < 0 ? 0 : 1), bytes, "",
145                         HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
146         fprintf(stdout, "\tReserved space %6s\n", buf);
147
148         bytes = ((info.freebigblocks - info.rsvbigblocks) << HAMMER_LARGEBLOCK_BITS);
149         humanize_number(buf, sizeof(buf)  - (bytes < 0 ? 0 : 1), bytes, "",
150                         HN_AUTOSCALE, HN_DECIMAL | HN_NOSPACE | HN_B);
151         fprintf(stdout, "\tFree space     %6s\n", buf);
152
153         /* Pseudo-filesystem information */
154         fprintf(stdout, "PFS information\n");
155         fprintf(stdout, "\tPFS-Id  Mode    Snaps  Mounted-on\n");
156
157         while(pfs_id < HAMMER_MAX_PFS) {
158                 bzero(&pfs, sizeof(pfs));
159                 bzero(&pfs_od, sizeof(pfs_od));
160                 pfs.pfs_id = pfs_id;
161                 pfs.ondisk = &pfs_od;
162                 pfs.bytes = sizeof(pfs_od);
163                 pfs.version = HAMMER_IOC_PSEUDOFS_VERSION;
164                 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) >= 0) {
165                         ismaster = (pfs_od.mirror_flags & HAMMER_PFSD_SLAVE) ? 0 : 1;
166                         if (pfs_id == 0)
167                                 mountedon = strdup(path);
168                         else
169                                 mountedon = find_pfs_mount(pfs_id, info.vol_fsid, ismaster);
170
171                         sc = count_snapshots(fd, info.version, pfs_od.snapshots, mountedon);
172
173                         fprintf(stdout, "\t%6d  %-6s %6d  ",
174                                 pfs_id, (ismaster ? "MASTER" : "SLAVE"), sc);
175                         if (mountedon)
176                                 fprintf(stdout, "%s", mountedon);
177                         else
178                                 fprintf(stdout, "not-mounted");
179                         fprintf(stdout, "\n");
180                 }
181                 pfs_id++;
182         }
183
184         free(fsid);
185         free(mountedon);
186 }
187
188 char *
189 find_pfs_mount(int pfsid, uuid_t parentuuid, int ismaster)
190 {
191         struct hammer_ioc_info hi;
192         struct statfs *mntbuf;
193         int mntsize;
194         int curmount;
195         int fd;
196         size_t  mntbufsize;
197         char *trailstr;
198         char *retval;
199
200         retval = NULL;
201
202         /* Do not continue if there are no mounted filesystems */
203         mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
204         if (mntsize <= 0)
205                 return retval;
206
207         mntbufsize = (mntsize) * sizeof(struct statfs);
208         mntbuf = malloc(mntbufsize);
209         if (mntbuf == NULL) {
210                 perror("show_info");
211                 exit(EXIT_FAILURE);
212         }
213
214         mntsize = getfsstat(mntbuf, (long)mntbufsize, MNT_NOWAIT);
215         curmount = mntsize - 1;
216
217         asprintf(&trailstr, ":%05d", pfsid);
218
219         /*
220          * Iterate all the mounted points looking for the PFS passed to
221          * this function.
222          */
223         while(curmount >= 0) {
224                 /*
225                  * We need to avoid that PFS belonging to other HAMMER
226                  * filesystems are showed as mounted, so we compare
227                  * against the FSID, which is presumable to be unique.
228                  */
229                 bzero(&hi, sizeof(hi));
230                 if ((fd = open(mntbuf[curmount].f_mntfromname, O_RDONLY)) < 0) {
231                         curmount--;
232                         continue;
233                 }
234
235                 if ((ioctl(fd, HAMMERIOC_GET_INFO, &hi)) < 0) {
236                         curmount--;
237                         continue;
238                 }
239
240                 if (strstr(mntbuf[curmount].f_mntfromname, trailstr) != NULL &&
241                     (uuid_compare(&hi.vol_fsid, &parentuuid, NULL)) == 0) {
242                         if (ismaster) {
243                                 if (strstr(mntbuf[curmount].f_mntfromname, "@@-1") != NULL) {
244                                         retval = strdup(mntbuf[curmount].f_mntonname);
245                                         break;
246                                 }
247                         } else {
248                                 if (strstr(mntbuf[curmount].f_mntfromname, "@@0x") != NULL ) {
249                                         retval = strdup(mntbuf[curmount].f_mntonname);
250                                         break;
251                                 }
252                         }
253                 }
254                 curmount--;
255         }
256         free(trailstr);
257         return retval;
258 }
259
260 double
261 percent(int64_t value, int64_t total)
262 {
263         /* Avoid divide-by-zero */
264         if (total == 0)
265                 return 100.0;
266
267         return ((value * 100.0) / (double)total);
268 }
269
270 u_int32_t
271 count_snapshots(int fd, u_int32_t version, char *pfs_snapshots, char *mountedon)
272 {
273         struct hammer_ioc_snapshot snapinfo;
274         char *snapshots_path, *fpath;
275         struct dirent *den;
276         struct stat st;
277         DIR *dir;
278         u_int32_t snapshot_count = 0;
279
280         bzero(&snapinfo, sizeof(struct hammer_ioc_snapshot));
281         if (version < 3) {
282                 /*
283                  * old style: count the number of softlinks in the snapshots dir
284                  */
285                 if (pfs_snapshots[0])
286                         snapshots_path = pfs_snapshots;
287                 else
288                         asprintf(&snapshots_path, "%s/snapshots", mountedon);
289                 if ((dir = opendir(snapshots_path)) != NULL) {
290                         while ((den = readdir(dir)) != NULL) {
291                                 if (den->d_name[0] == '.')
292                                         continue;
293                                 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
294                                 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
295                                         snapshot_count++;
296                                 free(fpath);
297                         }
298                         closedir(dir);
299                 }
300         } else {
301                 /*
302                  * new style: file system meta-data
303                  */
304                 do {
305                         if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapinfo) < 0) {
306                                 perror("count_snapshots");
307                                 exit(EXIT_FAILURE);
308                         }
309                         snapshot_count += snapinfo.count;
310                 } while (snapinfo.head.error == 0 && snapinfo.count);
311         }
312         return snapshot_count;
313 }