xref: /dragonfly/bin/df/df.c (revision a444603fa660a24ec7f5a0cca47d1f178367d9a2)
1 /*
2  * Copyright (c) 1980, 1990, 1993, 1994
3  *        The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. Neither the name of the University nor the names of its contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * @(#) Copyright (c) 1980, 1990, 1993, 1994 The Regents of the University of California.  All rights reserved.
35  * @(#)df.c         8.9 (Berkeley) 5/8/95
36  * $FreeBSD: src/bin/df/df.c,v 1.23.2.9 2002/07/01 00:14:24 iedowse Exp $
37  */
38 
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/mount.h>
42 #include <sys/sysctl.h>
43 #include <sys/statvfs.h>
44 
45 #include <vfs/ufs/dinode.h>
46 #include <vfs/ufs/ufsmount.h>
47 
48 #include <err.h>
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <fstab.h>
52 #include <libutil.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <sysexits.h>
57 #include <unistd.h>
58 
59 #define UNITS_SI 1
60 #define UNITS_2 2
61 
62 #ifndef HN_FRACTIONAL
63 #define HN_FRACTIONAL         HN_DECIMAL
64 #endif
65 
66 /* Maximum widths of various fields. */
67 struct maxwidths {
68           int mntfrom;
69           int fstype;
70           int total;
71           int used;
72           int avail;
73           int iused;
74           int ifree;
75 };
76 
77 /* vfslist.c */
78 char      **makevfslist(char *);
79 int         checkvfsname(const char *, char **);
80 
81 static char         *getmntpt(char *);
82 static int           quadwidth(int64_t);
83 static char         *makenetvfslist(void);
84 static void          prthuman(struct statvfs *, int64_t);
85 static void          prthumanval(int64_t);
86 static void          prtstat(struct statfs *, struct statvfs *, struct maxwidths *);
87 static long          regetmntinfo(struct statfs **, struct statvfs **, long, char **);
88 static void          update_maxwidths(struct maxwidths *, struct statfs *, struct statvfs *);
89 static void          usage(void);
90 
91 static int          aflag = 0, hflag, iflag, nflag, Tflag;
92 static struct       ufs_args mdev;
93 
94 static __inline int
imax(int a,int b)95 imax(int a, int b)
96 {
97           return (a > b ? a : b);
98 }
99 
100 int
main(int argc,char ** argv)101 main(int argc, char **argv)
102 {
103           struct stat stbuf;
104           struct statfs statfsbuf, *mntbuf;
105           struct statvfs statvfsbuf, *mntvbuf;
106           struct maxwidths maxwidths;
107           const char *fstype;
108           char *mntpath, *mntpt, **vfslist;
109           long mntsize;
110           int ch, i, rv;
111 
112           fstype = "ufs";
113 
114           vfslist = NULL;
115           while ((ch = getopt(argc, argv, "abgHhiklmnPt:T")) != -1)
116                     switch (ch) {
117                     case 'a':
118                               aflag = 1;
119                               break;
120                     case 'b':
121                                         /* FALLTHROUGH */
122                     case 'P':
123                               if (setenv("BLOCKSIZE", "512", 1) != 0)
124                                         warn("setenv: cannot set BLOCKSIZE=512");
125                               hflag = 0;
126                               break;
127                     case 'g':
128                               if (setenv("BLOCKSIZE", "1g", 1) != 0)
129                                         warn("setenv: cannot set BLOCKSIZE=1g");
130                               hflag = 0;
131                               break;
132                     case 'H':
133                               hflag = UNITS_SI;
134                               break;
135                     case 'h':
136                               hflag = UNITS_2;
137                               break;
138                     case 'i':
139                               iflag = 1;
140                               break;
141                     case 'k':
142                               if (setenv("BLOCKSIZE", "1k", 1) != 0)
143                                         warn("setenv: cannot set BLOCKSIZE=1k");
144                               hflag = 0;
145                               break;
146                     case 'l':
147                               if (vfslist != NULL)
148                                         errx(1, "-l and -t are mutually exclusive.");
149                               vfslist = makevfslist(makenetvfslist());
150                               break;
151                     case 'm':
152                               if (setenv("BLOCKSIZE", "1m", 1) != 0)
153                                         warn("setenv: cannot set BLOCKSIZE=1m");
154                               hflag = 0;
155                               break;
156                     case 'n':
157                               nflag = 1;
158                               break;
159                     case 't':
160                               if (vfslist != NULL)
161                                         errx(1, "only one -t option may be specified");
162                               fstype = optarg;
163                               vfslist = makevfslist(optarg);
164                               break;
165                     case 'T':
166                               Tflag = 1;
167                               break;
168                     case '?':
169                     default:
170                               usage();
171                     }
172           argc -= optind;
173           argv += optind;
174 
175           mntsize = getmntvinfo(&mntbuf, &mntvbuf, MNT_NOWAIT);
176           bzero(&maxwidths, sizeof(maxwidths));
177           for (i = 0; i < mntsize; i++)
178                     update_maxwidths(&maxwidths, &mntbuf[i], &mntvbuf[i]);
179 
180           rv = 0;
181           if (!*argv) {
182                     mntsize = regetmntinfo(&mntbuf, &mntvbuf, mntsize, vfslist);
183                     bzero(&maxwidths, sizeof(maxwidths));
184                     for (i = 0; i < mntsize; i++)
185                               update_maxwidths(&maxwidths, &mntbuf[i], &mntvbuf[i]);
186                     for (i = 0; i < mntsize; i++) {
187                               if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0)
188                                         prtstat(&mntbuf[i], &mntvbuf[i], &maxwidths);
189                     }
190                     exit(rv);
191           }
192 
193           for (; *argv; argv++) {
194                     if (stat(*argv, &stbuf) < 0) {
195                               if ((mntpt = getmntpt(*argv)) == NULL) {
196                                         warn("%s", *argv);
197                                         rv = 1;
198                                         continue;
199                               }
200                     } else if (S_ISCHR(stbuf.st_mode)) {
201                               if ((mntpt = getmntpt(*argv)) == NULL) {
202                                         mdev.fspec = *argv;
203                                         mntpath = strdup("/tmp/df.XXXXXX");
204                                         if (mntpath == NULL) {
205                                                   warn("strdup failed");
206                                                   rv = 1;
207                                                   continue;
208                                         }
209                                         mntpt = mkdtemp(mntpath);
210                                         if (mntpt == NULL) {
211                                                   warn("mkdtemp(\"%s\") failed", mntpath);
212                                                   rv = 1;
213                                                   free(mntpath);
214                                                   continue;
215                                         }
216                                         if (mount(fstype, mntpt, MNT_RDONLY,
217                                             &mdev) != 0) {
218                                                   warn("%s", *argv);
219                                                   rv = 1;
220                                                   rmdir(mntpt);
221                                                   free(mntpath);
222                                                   continue;
223                                         } else if (statfs(mntpt, &statfsbuf) == 0 &&
224                                                      statvfs(mntpt, &statvfsbuf) == 0) {
225                                                   statfsbuf.f_mntonname[0] = '\0';
226                                                   prtstat(&statfsbuf, &statvfsbuf, &maxwidths);
227                                         } else {
228                                                   warn("%s", *argv);
229                                                   rv = 1;
230                                         }
231                                         unmount(mntpt, 0);
232                                         rmdir(mntpt);
233                                         free(mntpath);
234                                         continue;
235                               }
236                     } else
237                               mntpt = *argv;
238                     /*
239                      * Statfs does not take a `wait' flag, so we cannot
240                      * implement nflag here.
241                      */
242                     if (statfs(mntpt, &statfsbuf) < 0) {
243                               warn("%s", mntpt);
244                               rv = 1;
245                               continue;
246                     }
247                     if (statvfs(mntpt, &statvfsbuf) < 0) {
248                               warn("%s", mntpt);
249                               rv = 1;
250                               continue;
251                     }
252                     /*
253                      * Check to make sure the arguments we've been given are
254                      * satisfied. Return an error if we have been asked to
255                      * list a mount point that does not match the other args
256                      * we've been given (-l, -t, etc.).
257                      */
258                     if (checkvfsname(statfsbuf.f_fstypename, vfslist)) {
259                               rv = 1;
260                               continue;
261                     }
262 
263                     if (argc == 1) {
264                               bzero(&maxwidths, sizeof(maxwidths));
265                               update_maxwidths(&maxwidths, &statfsbuf, &statvfsbuf);
266                     }
267                     prtstat(&statfsbuf, &statvfsbuf, &maxwidths);
268           }
269           return (rv);
270 }
271 
272 static char *
getmntpt(char * name)273 getmntpt(char *name)
274 {
275           long mntsize, i;
276           struct statfs *mntbuf;
277           struct statvfs *mntvbuf;
278 
279           mntsize = getmntvinfo(&mntbuf, &mntvbuf, MNT_NOWAIT);
280           for (i = 0; i < mntsize; i++) {
281                     if (!strcmp(mntbuf[i].f_mntfromname, name))
282                               return (mntbuf[i].f_mntonname);
283           }
284           return (0);
285 }
286 
287 /*
288  * Make a pass over the filesystem info in ``mntbuf'' filtering out
289  * filesystem types not in vfslist and possibly re-stating to get
290  * current (not cached) info.  Returns the new count of valid statfs bufs.
291  */
292 static long
regetmntinfo(struct statfs ** mntbufp,struct statvfs ** mntvbufp,long mntsize,char ** vfslist)293 regetmntinfo(struct statfs **mntbufp, struct statvfs **mntvbufp, long mntsize, char **vfslist)
294 {
295           int i, j;
296           struct statfs *mntbuf;
297           struct statvfs *mntvbuf;
298 
299           if (vfslist == NULL)
300                     return (nflag ? mntsize : getmntvinfo(mntbufp, mntvbufp, MNT_WAIT));
301 
302           mntbuf = *mntbufp;
303           mntvbuf = *mntvbufp;
304           for (j = 0, i = 0; i < mntsize; i++) {
305                     if (checkvfsname(mntbuf[i].f_fstypename, vfslist))
306                               continue;
307                     if (!nflag) {
308                               statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
309                               statvfs(mntbuf[i].f_mntonname,&mntvbuf[j]);
310                     } else if (i != j) {
311                               mntbuf[j] = mntbuf[i];
312                               mntvbuf[j] = mntvbuf[i];
313                     }
314                     j++;
315           }
316           return (j);
317 }
318 
319 static void
prthuman(struct statvfs * vsfsp,int64_t used)320 prthuman(struct statvfs *vsfsp, int64_t used)
321 {
322           prthumanval(vsfsp->f_blocks * vsfsp->f_bsize);
323           prthumanval(used * vsfsp->f_bsize);
324           prthumanval(vsfsp->f_bavail * vsfsp->f_bsize);
325 }
326 
327 static void
prthumanval(int64_t bytes)328 prthumanval(int64_t bytes)
329 {
330           char buf[7];
331           int flags;
332 
333           flags = HN_B | HN_NOSPACE | HN_FRACTIONAL;
334           if (hflag == UNITS_SI)
335                     flags |= HN_DIVISOR_1000;
336 
337           humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1),
338               bytes, "", HN_AUTOSCALE, flags);
339 
340           printf(" %6s", buf);
341 }
342 
343 /*
344  * Print an inode count in "human-readable" format.
345  */
346 static void
prthumanvalinode(int64_t bytes)347 prthumanvalinode(int64_t bytes)
348 {
349           char buf[6];
350           int flags;
351 
352           flags = HN_NOSPACE | HN_FRACTIONAL | HN_DIVISOR_1000;
353 
354           humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1),
355               bytes, "", HN_AUTOSCALE, flags);
356 
357           printf(" %5s", buf);
358 }
359 
360 /*
361  * Convert statfs returned filesystem size into BLOCKSIZE units.
362  * Attempts to avoid overflow for large filesystems.
363  */
364 static intmax_t
fsbtoblk(int64_t num,uint64_t bsize,u_long reqbsize)365 fsbtoblk(int64_t num, uint64_t bsize, u_long reqbsize)
366 {
367           if (bsize != 0 && bsize < reqbsize)
368                     return (num / (intmax_t)(reqbsize / bsize));
369           else
370                     return (num * (intmax_t)(bsize / reqbsize));
371 }
372 
373 /*
374  * Print out status about a filesystem.
375  */
376 static void
prtstat(struct statfs * sfsp,struct statvfs * vsfsp,struct maxwidths * mwp)377 prtstat(struct statfs *sfsp, struct statvfs *vsfsp, struct maxwidths *mwp)
378 {
379           static long blocksize;
380           static int headerlen, timesthrough;
381           static const char *header;
382           int64_t used, availblks, inodes;
383 
384           if (++timesthrough == 1) {
385                     mwp->mntfrom = imax(mwp->mntfrom, strlen("Filesystem"));
386                     mwp->fstype = imax(mwp->fstype, strlen("Type"));
387                     if (hflag) {
388                               header = "  Size";
389                               mwp->total = mwp->used = mwp->avail = strlen(header);
390                     } else {
391                               header = getbsize(&headerlen, &blocksize);
392                               mwp->total = imax(mwp->total, headerlen);
393                     }
394                     mwp->used = imax(mwp->used, strlen("Used"));
395                     mwp->avail = imax(mwp->avail, strlen("Avail"));
396 
397                     printf("%-*s", mwp->mntfrom, "Filesystem");
398                     if (Tflag)
399                               printf("  %-*s", mwp->fstype, "Type");
400                     printf(" %-*s %*s %*s Capacity", mwp->total, header, mwp->used,
401                         "Used", mwp->avail, "Avail");
402                     if (iflag) {
403                               mwp->iused = imax(hflag ? 0 : mwp->iused,
404                                   (int)strlen("  iused"));
405                               mwp->ifree = imax(hflag ? 0 : mwp->ifree,
406                                   (int)strlen("ifree"));
407                               printf(" %*s %*s %%iused", mwp->iused - 2,
408                                   "iused", mwp->ifree, "ifree");
409                     }
410                     printf("  Mounted on\n");
411           }
412           printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname);
413           if (Tflag)
414                     printf("  %-*s", mwp->fstype, sfsp->f_fstypename);
415           used = vsfsp->f_blocks - vsfsp->f_bfree;
416           availblks = vsfsp->f_bavail + used;
417           if (hflag) {
418                     prthuman(vsfsp, used);
419           } else {
420                     printf(" %*jd %*jd %*jd", mwp->total,
421                       fsbtoblk(vsfsp->f_blocks, vsfsp->f_bsize, blocksize),
422                         mwp->used, fsbtoblk(used, vsfsp->f_bsize, blocksize),
423                       mwp->avail, fsbtoblk(vsfsp->f_bavail, vsfsp->f_bsize,
424                         blocksize));
425           }
426           printf(" %5.0f%%",
427               availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
428           if (iflag) {
429                     inodes = vsfsp->f_files;
430                     used = inodes - vsfsp->f_ffree;
431                     if (hflag) {
432                               printf("  ");
433                               prthumanvalinode(used);
434                               prthumanvalinode(vsfsp->f_ffree);
435                     } else {
436                               printf(" %*jd %*jd", mwp->iused, (intmax_t)used,
437                                   mwp->ifree, (intmax_t)vsfsp->f_ffree);
438                     }
439                     printf(" %4.0f%% ", inodes == 0 ? 100.0 :
440                         (double)used / (double)inodes * 100.0);
441           } else
442                     printf("  ");
443           printf("  %s\n", sfsp->f_mntonname);
444 }
445 
446 /*
447  * Update the maximum field-width information in `mwp' based on
448  * the filesystem specified by `sfsp'.
449  */
450 static void
update_maxwidths(struct maxwidths * mwp,struct statfs * sfsp,struct statvfs * vsfsp)451 update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp, struct statvfs *vsfsp)
452 {
453           static long blocksize;
454           int dummy;
455 
456           if (blocksize == 0)
457                     getbsize(&dummy, &blocksize);
458 
459           mwp->mntfrom = imax(mwp->mntfrom, strlen(sfsp->f_mntfromname));
460           mwp->fstype = imax(mwp->fstype, strlen(sfsp->f_fstypename));
461           mwp->total = imax(mwp->total, quadwidth(fsbtoblk(vsfsp->f_blocks,
462               vsfsp->f_bsize, blocksize)));
463           mwp->used = imax(mwp->used, quadwidth(fsbtoblk(vsfsp->f_blocks -
464               vsfsp->f_bfree, vsfsp->f_bsize, blocksize)));
465           mwp->avail = imax(mwp->avail, quadwidth(fsbtoblk(vsfsp->f_bavail,
466               vsfsp->f_bsize, blocksize)));
467           mwp->iused = imax(mwp->iused, quadwidth(vsfsp->f_files -
468               vsfsp->f_ffree));
469           mwp->ifree = imax(mwp->ifree, quadwidth(vsfsp->f_ffree));
470 }
471 
472 /* Return the width in characters of the specified long. */
473 static int
quadwidth(int64_t val)474 quadwidth(int64_t val)
475 {
476           int len;
477 
478           len = 0;
479           /* Negative or zero values require one extra digit. */
480           if (val <= 0) {
481                     val = -val;
482                     len++;
483           }
484           while (val > 0) {
485                     len++;
486                     val /= 10;
487           }
488           return (len);
489 }
490 
491 static void
usage(void)492 usage(void)
493 {
494 
495           fprintf(stderr,
496               "usage: df [-b | -H | -h | -k | -m | -P] [-ailnT] [-t type] [file | filesystem ...]\n");
497           exit(EX_USAGE);
498 }
499 
500 static char *
makenetvfslist(void)501 makenetvfslist(void)
502 {
503           char *str, *strptr, **listptr;
504           int mib[3], maxvfsconf, cnt=0, i;
505           size_t miblen;
506           struct ovfsconf *ptr;
507 
508           mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM;
509           miblen=sizeof(maxvfsconf);
510           if (sysctl(mib, (unsigned int)(NELEM(mib)),
511               &maxvfsconf, &miblen, NULL, 0)) {
512                     warnx("sysctl failed");
513                     return (NULL);
514           }
515 
516           if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) {
517                     warnx("malloc failed");
518                     return (NULL);
519           }
520 
521           for (ptr = getvfsent(); ptr; ptr = getvfsent())
522                     if (ptr->vfc_flags & VFCF_NETWORK) {
523                               listptr[cnt++] = strdup(ptr->vfc_name);
524                               if (listptr[cnt-1] == NULL) {
525                                         warnx("malloc failed");
526                                         free(listptr);
527                                         return (NULL);
528                               }
529                     }
530 
531           if (cnt == 0 ||
532               (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) {
533                     if (cnt > 0)
534                               warnx("malloc failed");
535                     free(listptr);
536                     return (NULL);
537           }
538 
539           *str = 'n'; *(str + 1) = 'o';
540           for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) {
541                     strncpy(strptr, listptr[i], 32);
542                     strptr += strlen(listptr[i]);
543                     *strptr = ',';
544                     free(listptr[i]);
545           }
546           *(--strptr) = '\0';
547 
548           free(listptr);
549           return (str);
550 }
551