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 * 4. 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 35 #if 0 36 #ifndef lint 37 static const char copyright[] = 38 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ 39 The Regents of the University of California. All rights reserved.\n"; 40 #endif /* not lint */ 41 42 #ifndef lint 43 static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; 44 #endif /* not lint */ 45 #endif 46 #include <sys/cdefs.h> 47 __FBSDID("$FreeBSD$"); 48 49 #include <sys/param.h> 50 #include <sys/stat.h> 51 #include <sys/mount.h> 52 #include <sys/sysctl.h> 53 #include <ufs/ufs/ufsmount.h> 54 #include <err.h> 55 #include <libutil.h> 56 #include <locale.h> 57 #include <stdint.h> 58 #include <stdio.h> 59 #include <stdlib.h> 60 #include <string.h> 61 #include <sysexits.h> 62 #include <unistd.h> 63 64 #include "extern.h" 65 66 #define UNITS_SI 1 67 #define UNITS_2 2 68 69 /* Maximum widths of various fields. */ 70 struct maxwidths { 71 int mntfrom; 72 int fstype; 73 int total; 74 int used; 75 int avail; 76 int iused; 77 int ifree; 78 }; 79 80 static void addstat(struct statfs *, struct statfs *); 81 static char *getmntpt(const char *); 82 static int int64width(int64_t); 83 static char *makenetvfslist(void); 84 static void prthuman(const struct statfs *, int64_t); 85 static void prthumanval(int64_t); 86 static intmax_t fsbtoblk(int64_t, uint64_t, u_long); 87 static void prtstat(struct statfs *, struct maxwidths *); 88 static size_t regetmntinfo(struct statfs **, long, const char **); 89 static void update_maxwidths(struct maxwidths *, const struct statfs *); 90 static void usage(void); 91 92 static __inline int 93 imax(int a, int b) 94 { 95 return (a > b ? a : b); 96 } 97 98 static int aflag = 0, cflag, hflag, iflag, kflag, lflag = 0, nflag, Tflag; 99 static int thousands; 100 static struct ufs_args mdev; 101 102 int 103 main(int argc, char *argv[]) 104 { 105 struct stat stbuf; 106 struct statfs statfsbuf, totalbuf; 107 struct maxwidths maxwidths; 108 struct statfs *mntbuf; 109 const char *fstype; 110 char *mntpath, *mntpt; 111 const char **vfslist; 112 int i, mntsize; 113 int ch, rv; 114 115 fstype = "ufs"; 116 (void)setlocale(LC_ALL, ""); 117 memset(&totalbuf, 0, sizeof(totalbuf)); 118 totalbuf.f_bsize = DEV_BSIZE; 119 strlcpy(totalbuf.f_mntfromname, "total", MNAMELEN); 120 vfslist = NULL; 121 while ((ch = getopt(argc, argv, "abcgHhiklmnPt:T,")) != -1) 122 switch (ch) { 123 case 'a': 124 aflag = 1; 125 break; 126 case 'b': 127 /* FALLTHROUGH */ 128 case 'P': 129 /* 130 * POSIX specifically discusses the behavior of 131 * both -k and -P. It states that the blocksize should 132 * be set to 1024. Thus, if this occurs, simply break 133 * rather than clobbering the old blocksize. 134 */ 135 if (kflag) 136 break; 137 setenv("BLOCKSIZE", "512", 1); 138 hflag = 0; 139 break; 140 case 'c': 141 cflag = 1; 142 break; 143 case 'g': 144 setenv("BLOCKSIZE", "1g", 1); 145 hflag = 0; 146 break; 147 case 'H': 148 hflag = UNITS_SI; 149 break; 150 case 'h': 151 hflag = UNITS_2; 152 break; 153 case 'i': 154 iflag = 1; 155 break; 156 case 'k': 157 kflag++; 158 setenv("BLOCKSIZE", "1024", 1); 159 hflag = 0; 160 break; 161 case 'l': 162 if (vfslist != NULL) 163 errx(1, "-l and -t are mutually exclusive."); 164 vfslist = makevfslist(makenetvfslist()); 165 lflag = 1; 166 break; 167 case 'm': 168 setenv("BLOCKSIZE", "1m", 1); 169 hflag = 0; 170 break; 171 case 'n': 172 nflag = 1; 173 break; 174 case 't': 175 if (lflag) 176 errx(1, "-l and -t are mutually exclusive."); 177 if (vfslist != NULL) 178 errx(1, "only one -t option may be specified"); 179 fstype = optarg; 180 vfslist = makevfslist(optarg); 181 break; 182 case 'T': 183 Tflag = 1; 184 break; 185 case ',': 186 thousands = 1; 187 break; 188 case '?': 189 default: 190 usage(); 191 } 192 argc -= optind; 193 argv += optind; 194 195 rv = 0; 196 if (!*argv) { 197 /* everything (modulo -t) */ 198 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 199 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); 200 } else { 201 /* just the filesystems specified on the command line */ 202 mntbuf = malloc(argc * sizeof(*mntbuf)); 203 if (mntbuf == 0) 204 err(1, "malloc()"); 205 mntsize = 0; 206 /* continued in for loop below */ 207 } 208 209 /* iterate through specified filesystems */ 210 for (; *argv; argv++) { 211 if (stat(*argv, &stbuf) < 0) { 212 if ((mntpt = getmntpt(*argv)) == 0) { 213 warn("%s", *argv); 214 rv = 1; 215 continue; 216 } 217 } else if (S_ISCHR(stbuf.st_mode)) { 218 if ((mntpt = getmntpt(*argv)) == 0) { 219 mdev.fspec = *argv; 220 mntpath = strdup("/tmp/df.XXXXXX"); 221 if (mntpath == NULL) { 222 warn("strdup failed"); 223 rv = 1; 224 continue; 225 } 226 mntpt = mkdtemp(mntpath); 227 if (mntpt == NULL) { 228 warn("mkdtemp(\"%s\") failed", mntpath); 229 rv = 1; 230 free(mntpath); 231 continue; 232 } 233 if (mount(fstype, mntpt, MNT_RDONLY, 234 &mdev) != 0) { 235 warn("%s", *argv); 236 rv = 1; 237 (void)rmdir(mntpt); 238 free(mntpath); 239 continue; 240 } else if (statfs(mntpt, &statfsbuf) == 0) { 241 statfsbuf.f_mntonname[0] = '\0'; 242 prtstat(&statfsbuf, &maxwidths); 243 if (cflag) 244 addstat(&totalbuf, &statfsbuf); 245 } else { 246 warn("%s", *argv); 247 rv = 1; 248 } 249 (void)unmount(mntpt, 0); 250 (void)rmdir(mntpt); 251 free(mntpath); 252 continue; 253 } 254 } else 255 mntpt = *argv; 256 257 /* 258 * Statfs does not take a `wait' flag, so we cannot 259 * implement nflag here. 260 */ 261 if (statfs(mntpt, &statfsbuf) < 0) { 262 warn("%s", mntpt); 263 rv = 1; 264 continue; 265 } 266 267 /* 268 * Check to make sure the arguments we've been given are 269 * satisfied. Return an error if we have been asked to 270 * list a mount point that does not match the other args 271 * we've been given (-l, -t, etc.). 272 */ 273 if (checkvfsname(statfsbuf.f_fstypename, vfslist)) { 274 rv = 1; 275 continue; 276 } 277 278 /* the user asked for it, so ignore the ignore flag */ 279 statfsbuf.f_flags &= ~MNT_IGNORE; 280 281 /* add to list */ 282 mntbuf[mntsize++] = statfsbuf; 283 } 284 285 bzero(&maxwidths, sizeof(maxwidths)); 286 for (i = 0; i < mntsize; i++) { 287 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) { 288 update_maxwidths(&maxwidths, &mntbuf[i]); 289 if (cflag) 290 addstat(&totalbuf, &mntbuf[i]); 291 } 292 } 293 for (i = 0; i < mntsize; i++) 294 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) 295 prtstat(&mntbuf[i], &maxwidths); 296 if (cflag) 297 prtstat(&totalbuf, &maxwidths); 298 return (rv); 299 } 300 301 static char * 302 getmntpt(const char *name) 303 { 304 size_t mntsize, i; 305 struct statfs *mntbuf; 306 307 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 308 for (i = 0; i < mntsize; i++) { 309 if (!strcmp(mntbuf[i].f_mntfromname, name)) 310 return (mntbuf[i].f_mntonname); 311 } 312 return (0); 313 } 314 315 /* 316 * Make a pass over the file system info in ``mntbuf'' filtering out 317 * file system types not in vfslist and possibly re-stating to get 318 * current (not cached) info. Returns the new count of valid statfs bufs. 319 */ 320 static size_t 321 regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist) 322 { 323 int error, i, j; 324 struct statfs *mntbuf; 325 326 if (vfslist == NULL) 327 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 328 329 mntbuf = *mntbufp; 330 for (j = 0, i = 0; i < mntsize; i++) { 331 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 332 continue; 333 /* 334 * XXX statfs(2) can fail for various reasons. It may be 335 * possible that the user does not have access to the 336 * pathname, if this happens, we will fall back on 337 * "stale" filesystem statistics. 338 */ 339 error = statfs(mntbuf[i].f_mntonname, &mntbuf[j]); 340 if (nflag || error < 0) 341 if (i != j) { 342 if (error < 0) 343 warnx("%s stats possibly stale", 344 mntbuf[i].f_mntonname); 345 mntbuf[j] = mntbuf[i]; 346 } 347 j++; 348 } 349 return (j); 350 } 351 352 static void 353 prthuman(const struct statfs *sfsp, int64_t used) 354 { 355 356 prthumanval(sfsp->f_blocks * sfsp->f_bsize); 357 prthumanval(used * sfsp->f_bsize); 358 prthumanval(sfsp->f_bavail * sfsp->f_bsize); 359 } 360 361 static void 362 prthumanval(int64_t bytes) 363 { 364 char buf[6]; 365 int flags; 366 367 flags = HN_B | HN_NOSPACE | HN_DECIMAL; 368 if (hflag == UNITS_SI) 369 flags |= HN_DIVISOR_1000; 370 371 humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 372 bytes, "", HN_AUTOSCALE, flags); 373 374 (void)printf(" %6s", buf); 375 } 376 377 /* 378 * Print an inode count in "human-readable" format. 379 */ 380 static void 381 prthumanvalinode(int64_t bytes) 382 { 383 char buf[6]; 384 int flags; 385 386 flags = HN_NOSPACE | HN_DECIMAL | HN_DIVISOR_1000; 387 388 humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), 389 bytes, "", HN_AUTOSCALE, flags); 390 391 (void)printf(" %5s", buf); 392 } 393 394 /* 395 * Convert statfs returned file system size into BLOCKSIZE units. 396 */ 397 static intmax_t 398 fsbtoblk(int64_t num, uint64_t fsbs, u_long bs) 399 { 400 return (num * (intmax_t) fsbs / (int64_t) bs); 401 } 402 403 /* 404 * Print out status about a file system. 405 */ 406 static void 407 prtstat(struct statfs *sfsp, struct maxwidths *mwp) 408 { 409 static long blocksize; 410 static int headerlen, timesthrough = 0; 411 static const char *header; 412 int64_t used, availblks, inodes; 413 const char *format; 414 415 if (++timesthrough == 1) { 416 mwp->mntfrom = imax(mwp->mntfrom, (int)strlen("Filesystem")); 417 mwp->fstype = imax(mwp->fstype, (int)strlen("Type")); 418 if (thousands) { /* make space for commas */ 419 mwp->total += (mwp->total - 1) / 3; 420 mwp->used += (mwp->used - 1) / 3; 421 mwp->avail += (mwp->avail - 1) / 3; 422 mwp->iused += (mwp->iused - 1) / 3; 423 mwp->ifree += (mwp->ifree - 1) / 3; 424 } 425 if (hflag) { 426 header = " Size"; 427 mwp->total = mwp->used = mwp->avail = 428 (int)strlen(header); 429 } else { 430 header = getbsize(&headerlen, &blocksize); 431 mwp->total = imax(mwp->total, headerlen); 432 } 433 mwp->used = imax(mwp->used, (int)strlen("Used")); 434 mwp->avail = imax(mwp->avail, (int)strlen("Avail")); 435 436 (void)printf("%-*s", mwp->mntfrom, "Filesystem"); 437 if (Tflag) 438 (void)printf(" %-*s", mwp->fstype, "Type"); 439 (void)printf(" %*s %*s %*s Capacity", mwp->total, header, 440 mwp->used, "Used", mwp->avail, "Avail"); 441 if (iflag) { 442 mwp->iused = imax(hflag ? 0 : mwp->iused, 443 (int)strlen(" iused")); 444 mwp->ifree = imax(hflag ? 0 : mwp->ifree, 445 (int)strlen("ifree")); 446 (void)printf(" %*s %*s %%iused", 447 mwp->iused - 2, "iused", mwp->ifree, "ifree"); 448 } 449 (void)printf(" Mounted on\n"); 450 } 451 /* Check for 0 block size. Can this happen? */ 452 if (sfsp->f_bsize == 0) { 453 warnx ("File system %s does not have a block size, assuming 512.", 454 sfsp->f_mntonname); 455 sfsp->f_bsize = 512; 456 } 457 (void)printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname); 458 if (Tflag) 459 (void)printf(" %-*s", mwp->fstype, sfsp->f_fstypename); 460 used = sfsp->f_blocks - sfsp->f_bfree; 461 availblks = sfsp->f_bavail + used; 462 if (hflag) { 463 prthuman(sfsp, used); 464 } else { 465 if (thousands) 466 format = " %*j'd %*j'd %*j'd"; 467 else 468 format = " %*jd %*jd %*jd"; 469 (void)printf(format, 470 mwp->total, fsbtoblk(sfsp->f_blocks, 471 sfsp->f_bsize, blocksize), 472 mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize), 473 mwp->avail, fsbtoblk(sfsp->f_bavail, 474 sfsp->f_bsize, blocksize)); 475 } 476 (void)printf(" %5.0f%%", 477 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); 478 if (iflag) { 479 inodes = sfsp->f_files; 480 used = inodes - sfsp->f_ffree; 481 if (hflag) { 482 (void)printf(" "); 483 prthumanvalinode(used); 484 prthumanvalinode(sfsp->f_ffree); 485 } else { 486 if (thousands) 487 format = " %*j'd %*j'd"; 488 else 489 format = " %*jd %*jd"; 490 (void)printf(format, mwp->iused, (intmax_t)used, 491 mwp->ifree, (intmax_t)sfsp->f_ffree); 492 } 493 (void)printf(" %4.0f%% ", inodes == 0 ? 100.0 : 494 (double)used / (double)inodes * 100.0); 495 } else 496 (void)printf(" "); 497 if (strncmp(sfsp->f_mntfromname, "total", MNAMELEN) != 0) 498 (void)printf(" %s", sfsp->f_mntonname); 499 (void)printf("\n"); 500 } 501 502 static void 503 addstat(struct statfs *totalfsp, struct statfs *statfsp) 504 { 505 uint64_t bsize; 506 507 bsize = statfsp->f_bsize / totalfsp->f_bsize; 508 totalfsp->f_blocks += statfsp->f_blocks * bsize; 509 totalfsp->f_bfree += statfsp->f_bfree * bsize; 510 totalfsp->f_bavail += statfsp->f_bavail * bsize; 511 totalfsp->f_files += statfsp->f_files; 512 totalfsp->f_ffree += statfsp->f_ffree; 513 } 514 515 /* 516 * Update the maximum field-width information in `mwp' based on 517 * the file system specified by `sfsp'. 518 */ 519 static void 520 update_maxwidths(struct maxwidths *mwp, const struct statfs *sfsp) 521 { 522 static long blocksize = 0; 523 int dummy; 524 525 if (blocksize == 0) 526 getbsize(&dummy, &blocksize); 527 528 mwp->mntfrom = imax(mwp->mntfrom, (int)strlen(sfsp->f_mntfromname)); 529 mwp->fstype = imax(mwp->fstype, (int)strlen(sfsp->f_fstypename)); 530 mwp->total = imax(mwp->total, int64width( 531 fsbtoblk((int64_t)sfsp->f_blocks, sfsp->f_bsize, blocksize))); 532 mwp->used = imax(mwp->used, 533 int64width(fsbtoblk((int64_t)sfsp->f_blocks - 534 (int64_t)sfsp->f_bfree, sfsp->f_bsize, blocksize))); 535 mwp->avail = imax(mwp->avail, int64width(fsbtoblk(sfsp->f_bavail, 536 sfsp->f_bsize, blocksize))); 537 mwp->iused = imax(mwp->iused, int64width((int64_t)sfsp->f_files - 538 sfsp->f_ffree)); 539 mwp->ifree = imax(mwp->ifree, int64width(sfsp->f_ffree)); 540 } 541 542 /* Return the width in characters of the specified value. */ 543 static int 544 int64width(int64_t val) 545 { 546 int len; 547 548 len = 0; 549 /* Negative or zero values require one extra digit. */ 550 if (val <= 0) { 551 val = -val; 552 len++; 553 } 554 while (val > 0) { 555 len++; 556 val /= 10; 557 } 558 559 return (len); 560 } 561 562 static void 563 usage(void) 564 { 565 566 (void)fprintf(stderr, 567 "usage: df [-b | -g | -H | -h | -k | -m | -P] [-acilnT] [-t type] [-,]\n" 568 " [file | filesystem ...]\n"); 569 exit(EX_USAGE); 570 } 571 572 static char * 573 makenetvfslist(void) 574 { 575 char *str, *strptr, **listptr; 576 struct xvfsconf *xvfsp, *keep_xvfsp; 577 size_t buflen; 578 int cnt, i, maxvfsconf; 579 580 if (sysctlbyname("vfs.conflist", NULL, &buflen, NULL, 0) < 0) { 581 warn("sysctl(vfs.conflist)"); 582 return (NULL); 583 } 584 xvfsp = malloc(buflen); 585 if (xvfsp == NULL) { 586 warnx("malloc failed"); 587 return (NULL); 588 } 589 keep_xvfsp = xvfsp; 590 if (sysctlbyname("vfs.conflist", xvfsp, &buflen, NULL, 0) < 0) { 591 warn("sysctl(vfs.conflist)"); 592 free(keep_xvfsp); 593 return (NULL); 594 } 595 maxvfsconf = buflen / sizeof(struct xvfsconf); 596 597 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) { 598 warnx("malloc failed"); 599 free(keep_xvfsp); 600 return (NULL); 601 } 602 603 for (cnt = 0, i = 0; i < maxvfsconf; i++) { 604 if (xvfsp->vfc_flags & VFCF_NETWORK) { 605 listptr[cnt++] = strdup(xvfsp->vfc_name); 606 if (listptr[cnt-1] == NULL) { 607 warnx("malloc failed"); 608 free(listptr); 609 free(keep_xvfsp); 610 return (NULL); 611 } 612 } 613 xvfsp++; 614 } 615 616 if (cnt == 0 || 617 (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { 618 if (cnt > 0) 619 warnx("malloc failed"); 620 free(listptr); 621 free(keep_xvfsp); 622 return (NULL); 623 } 624 625 *str = 'n'; *(str + 1) = 'o'; 626 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { 627 strlcpy(strptr, listptr[i], 32); 628 strptr += strlen(listptr[i]); 629 *strptr = ','; 630 free(listptr[i]); 631 } 632 *(--strptr) = '\0'; 633 634 free(keep_xvfsp); 635 free(listptr); 636 return (str); 637 } 638