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