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 39 #ifndef lint 40 static const char copyright[] = 41 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ 42 The Regents of the University of California. All rights reserved.\n"; 43 #endif /* not lint */ 44 45 #ifndef lint 46 #if 0 47 static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; 48 #endif 49 #endif /* not lint */ 50 #include <sys/cdefs.h> 51 __FBSDID("$FreeBSD$"); 52 53 #include <sys/param.h> 54 #include <sys/stat.h> 55 #include <sys/mount.h> 56 #include <sys/sysctl.h> 57 #include <ufs/ufs/ufsmount.h> 58 #include <err.h> 59 #include <math.h> 60 #include <stdio.h> 61 #include <stdlib.h> 62 #include <string.h> 63 #include <sysexits.h> 64 #include <unistd.h> 65 66 #include "extern.h" 67 68 #define UNITS_SI 1 69 #define UNITS_2 2 70 71 #define KILO_SZ(n) (n) 72 #define MEGA_SZ(n) ((n) * (n)) 73 #define GIGA_SZ(n) ((n) * (n) * (n)) 74 #define TERA_SZ(n) ((n) * (n) * (n) * (n)) 75 #define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) 76 77 #define KILO_2_SZ (KILO_SZ(1024ULL)) 78 #define MEGA_2_SZ (MEGA_SZ(1024ULL)) 79 #define GIGA_2_SZ (GIGA_SZ(1024ULL)) 80 #define TERA_2_SZ (TERA_SZ(1024ULL)) 81 #define PETA_2_SZ (PETA_SZ(1024ULL)) 82 83 #define KILO_SI_SZ (KILO_SZ(1000ULL)) 84 #define MEGA_SI_SZ (MEGA_SZ(1000ULL)) 85 #define GIGA_SI_SZ (GIGA_SZ(1000ULL)) 86 #define TERA_SI_SZ (TERA_SZ(1000ULL)) 87 #define PETA_SI_SZ (PETA_SZ(1000ULL)) 88 89 /* Maximum widths of various fields. */ 90 struct maxwidths { 91 int mntfrom; 92 int total; 93 int used; 94 int avail; 95 int iused; 96 int ifree; 97 }; 98 99 unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; 100 unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; 101 unsigned long long *valp; 102 103 typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; 104 105 unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; 106 107 static char *getmntpt(char *); 108 static int longwidth(long); 109 static char *makenetvfslist(void); 110 static void prthuman(struct statfs *, long); 111 static void prthumanval(double); 112 static void prtstat(struct statfs *, struct maxwidths *); 113 static long regetmntinfo(struct statfs **, long, const char **); 114 static unit_t unit_adjust(double *); 115 static void update_maxwidths(struct maxwidths *, struct statfs *); 116 static void usage(void); 117 118 static __inline int 119 imax(int a, int b) 120 { 121 return (MAX(a, b)); 122 } 123 124 static int aflag = 0, hflag, iflag, nflag; 125 static struct ufs_args mdev; 126 127 int 128 main(int argc, char *argv[]) 129 { 130 struct stat stbuf; 131 struct statfs statfsbuf, *mntbuf; 132 struct maxwidths maxwidths; 133 const char *fstype; 134 char *mntpath, *mntpt; 135 const char **vfslist; 136 long mntsize; 137 int ch, i, rv; 138 139 fstype = "ufs"; 140 141 vfslist = NULL; 142 while ((ch = getopt(argc, argv, "abgHhiklmnPt:")) != -1) 143 switch (ch) { 144 case 'a': 145 aflag = 1; 146 break; 147 case 'b': 148 /* FALLTHROUGH */ 149 case 'P': 150 putenv("BLOCKSIZE=512"); 151 hflag = 0; 152 break; 153 case 'g': 154 putenv("BLOCKSIZE=1g"); 155 hflag = 0; 156 break; 157 case 'H': 158 hflag = UNITS_SI; 159 valp = vals_si; 160 break; 161 case 'h': 162 hflag = UNITS_2; 163 valp = vals_base2; 164 break; 165 case 'i': 166 iflag = 1; 167 break; 168 case 'k': 169 putenv("BLOCKSIZE=1k"); 170 hflag = 0; 171 break; 172 case 'l': 173 if (vfslist != NULL) 174 errx(1, "-l and -t are mutually exclusive."); 175 vfslist = makevfslist(makenetvfslist()); 176 break; 177 case 'm': 178 putenv("BLOCKSIZE=1m"); 179 hflag = 0; 180 break; 181 case 'n': 182 nflag = 1; 183 break; 184 case 't': 185 if (vfslist != NULL) 186 errx(1, "only one -t option may be specified"); 187 fstype = optarg; 188 vfslist = makevfslist(optarg); 189 break; 190 case '?': 191 default: 192 usage(); 193 } 194 argc -= optind; 195 argv += optind; 196 197 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 198 bzero(&maxwidths, sizeof(maxwidths)); 199 for (i = 0; i < mntsize; i++) 200 update_maxwidths(&maxwidths, &mntbuf[i]); 201 202 rv = 0; 203 if (!*argv) { 204 mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); 205 bzero(&maxwidths, sizeof(maxwidths)); 206 for (i = 0; i < mntsize; i++) 207 update_maxwidths(&maxwidths, &mntbuf[i]); 208 for (i = 0; i < mntsize; i++) { 209 if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) 210 prtstat(&mntbuf[i], &maxwidths); 211 } 212 exit(rv); 213 } 214 215 for (; *argv; argv++) { 216 if (stat(*argv, &stbuf) < 0) { 217 if ((mntpt = getmntpt(*argv)) == 0) { 218 warn("%s", *argv); 219 rv = 1; 220 continue; 221 } 222 } else if (S_ISCHR(stbuf.st_mode)) { 223 if ((mntpt = getmntpt(*argv)) == 0) { 224 mdev.fspec = *argv; 225 mntpath = strdup("/tmp/df.XXXXXX"); 226 if (mntpath == NULL) { 227 warn("strdup failed"); 228 rv = 1; 229 continue; 230 } 231 mntpt = mkdtemp(mntpath); 232 if (mntpt == NULL) { 233 warn("mkdtemp(\"%s\") failed", mntpath); 234 rv = 1; 235 free(mntpath); 236 continue; 237 } 238 if (mount(fstype, mntpt, MNT_RDONLY, 239 &mdev) != 0) { 240 warn("%s", *argv); 241 rv = 1; 242 (void)rmdir(mntpt); 243 free(mntpath); 244 continue; 245 } else if (statfs(mntpt, &statfsbuf) == 0) { 246 statfsbuf.f_mntonname[0] = '\0'; 247 prtstat(&statfsbuf, &maxwidths); 248 } else { 249 warn("%s", *argv); 250 rv = 1; 251 } 252 (void)unmount(mntpt, 0); 253 (void)rmdir(mntpt); 254 free(mntpath); 255 continue; 256 } 257 } else 258 mntpt = *argv; 259 /* 260 * Statfs does not take a `wait' flag, so we cannot 261 * implement nflag here. 262 */ 263 if (statfs(mntpt, &statfsbuf) < 0) { 264 warn("%s", mntpt); 265 rv = 1; 266 continue; 267 } 268 if (argc == 1) { 269 bzero(&maxwidths, sizeof(maxwidths)); 270 update_maxwidths(&maxwidths, &statfsbuf); 271 } 272 prtstat(&statfsbuf, &maxwidths); 273 } 274 return (rv); 275 } 276 277 static char * 278 getmntpt(char *name) 279 { 280 long mntsize, i; 281 struct statfs *mntbuf; 282 283 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 284 for (i = 0; i < mntsize; i++) { 285 if (!strcmp(mntbuf[i].f_mntfromname, name)) 286 return (mntbuf[i].f_mntonname); 287 } 288 return (0); 289 } 290 291 /* 292 * Make a pass over the filesystem info in ``mntbuf'' filtering out 293 * filesystem types not in vfslist and possibly re-stating to get 294 * current (not cached) info. Returns the new count of valid statfs bufs. 295 */ 296 static long 297 regetmntinfo(struct statfs **mntbufp, long mntsize, const char **vfslist) 298 { 299 int i, j; 300 struct statfs *mntbuf; 301 302 if (vfslist == NULL) 303 return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); 304 305 mntbuf = *mntbufp; 306 for (j = 0, i = 0; i < mntsize; i++) { 307 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 308 continue; 309 if (!nflag) 310 (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); 311 else if (i != j) 312 mntbuf[j] = mntbuf[i]; 313 j++; 314 } 315 return (j); 316 } 317 318 /* 319 * Output in "human-readable" format. Uses 3 digits max and puts 320 * unit suffixes at the end. Makes output compact and easy to read, 321 * especially on huge disks. 322 * 323 */ 324 static unit_t 325 unit_adjust(double *val) 326 { 327 double abval; 328 unit_t unit; 329 unsigned int unit_sz; 330 331 abval = fabs(*val); 332 333 unit_sz = abval ? ilogb(abval) / 10 : 0; 334 335 if (unit_sz >= UNIT_MAX) { 336 unit = NONE; 337 } else { 338 unit = unitp[unit_sz]; 339 *val /= (double)valp[unit_sz]; 340 } 341 342 return (unit); 343 } 344 345 static void 346 prthuman(struct statfs *sfsp, long used) 347 { 348 349 prthumanval((double)sfsp->f_blocks * (double)sfsp->f_bsize); 350 prthumanval((double)used * (double)sfsp->f_bsize); 351 prthumanval((double)sfsp->f_bavail * (double)sfsp->f_bsize); 352 } 353 354 static void 355 prthumanval(double bytes) 356 { 357 358 unit_t unit; 359 unit = unit_adjust(&bytes); 360 361 if (bytes == 0) 362 (void)printf(" 0B"); 363 else if (bytes > 10) 364 (void)printf(" %5.0f%c", bytes, "BKMGTPE"[unit]); 365 else 366 (void)printf(" %5.1f%c", bytes, "BKMGTPE"[unit]); 367 } 368 369 /* 370 * Convert statfs returned filesystem size into BLOCKSIZE units. 371 * Attempts to avoid overflow for large filesystems. 372 */ 373 #define fsbtoblk(num, fsbs, bs) \ 374 (((fsbs) != 0 && (fsbs) < (bs)) ? \ 375 (num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs))) 376 377 /* 378 * Print out status about a filesystem. 379 */ 380 static void 381 prtstat(struct statfs *sfsp, struct maxwidths *mwp) 382 { 383 static long blocksize; 384 static int headerlen, timesthrough; 385 static const char *header; 386 long used, availblks, inodes; 387 388 if (++timesthrough == 1) { 389 mwp->mntfrom = imax(mwp->mntfrom, strlen("Filesystem")); 390 if (hflag) { 391 header = " Size"; 392 mwp->total = mwp->used = mwp->avail = strlen(header); 393 } else { 394 header = getbsize(&headerlen, &blocksize); 395 mwp->total = imax(mwp->total, headerlen); 396 } 397 mwp->used = imax(mwp->used, strlen("Used")); 398 mwp->avail = imax(mwp->avail, strlen("Avail")); 399 400 (void)printf("%-*s %-*s %*s %*s Capacity", mwp->mntfrom, 401 "Filesystem", mwp->total, header, mwp->used, "Used", 402 mwp->avail, "Avail"); 403 if (iflag) { 404 mwp->iused = imax(mwp->iused, strlen(" iused")); 405 mwp->ifree = imax(mwp->ifree, strlen("ifree")); 406 (void)printf(" %*s %*s %%iused", mwp->iused - 2, 407 "iused", mwp->ifree, "ifree"); 408 } 409 (void)printf(" Mounted on\n"); 410 } 411 (void)printf("%-*s", mwp->mntfrom, sfsp->f_mntfromname); 412 used = sfsp->f_blocks - sfsp->f_bfree; 413 availblks = sfsp->f_bavail + used; 414 if (hflag) { 415 prthuman(sfsp, used); 416 } else { 417 (void)printf(" %*ld %*ld %*ld", mwp->total, 418 fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), 419 mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize), 420 mwp->avail, fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, 421 blocksize)); 422 } 423 (void)printf(" %5.0f%%", 424 availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); 425 if (iflag) { 426 inodes = sfsp->f_files; 427 used = inodes - sfsp->f_ffree; 428 (void)printf(" %*ld %*ld %4.0f%% ", mwp->iused, used, 429 mwp->ifree, sfsp->f_ffree, inodes == 0 ? 100.0 : 430 (double)used / (double)inodes * 100.0); 431 } else 432 (void)printf(" "); 433 (void)printf(" %s\n", sfsp->f_mntonname); 434 } 435 436 /* 437 * Update the maximum field-width information in `mwp' based on 438 * the filesystem specified by `sfsp'. 439 */ 440 static void 441 update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp) 442 { 443 static long blocksize; 444 int dummy; 445 446 if (blocksize == 0) 447 getbsize(&dummy, &blocksize); 448 449 mwp->mntfrom = imax(mwp->mntfrom, strlen(sfsp->f_mntfromname)); 450 mwp->total = imax(mwp->total, longwidth(fsbtoblk(sfsp->f_blocks, 451 sfsp->f_bsize, blocksize))); 452 mwp->used = imax(mwp->used, longwidth(fsbtoblk(sfsp->f_blocks - 453 sfsp->f_bfree, sfsp->f_bsize, blocksize))); 454 mwp->avail = imax(mwp->avail, longwidth(fsbtoblk(sfsp->f_bavail, 455 sfsp->f_bsize, blocksize))); 456 mwp->iused = imax(mwp->iused, longwidth(sfsp->f_files - 457 sfsp->f_ffree)); 458 mwp->ifree = imax(mwp->ifree, longwidth(sfsp->f_ffree)); 459 } 460 461 /* Return the width in characters of the specified long. */ 462 static int 463 longwidth(long val) 464 { 465 int len; 466 467 len = 0; 468 /* Negative or zero values require one extra digit. */ 469 if (val <= 0) { 470 val = -val; 471 len++; 472 } 473 while (val > 0) { 474 len++; 475 val /= 10; 476 } 477 478 return (len); 479 } 480 481 static void 482 usage(void) 483 { 484 485 (void)fprintf(stderr, 486 "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-t type] [file | filesystem ...]\n"); 487 exit(EX_USAGE); 488 } 489 490 static char * 491 makenetvfslist(void) 492 { 493 char *str, *strptr, **listptr; 494 int mib[3], maxvfsconf, cnt=0, i; 495 size_t miblen; 496 struct ovfsconf *ptr; 497 498 mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM; 499 miblen=sizeof(maxvfsconf); 500 if (sysctl(mib, (unsigned int)(sizeof(mib) / sizeof(mib[0])), 501 &maxvfsconf, &miblen, NULL, 0)) { 502 warnx("sysctl failed"); 503 return (NULL); 504 } 505 506 if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) { 507 warnx("malloc failed"); 508 return (NULL); 509 } 510 511 for (ptr = getvfsent(); ptr; ptr = getvfsent()) 512 if (ptr->vfc_flags & VFCF_NETWORK) { 513 listptr[cnt++] = strdup(ptr->vfc_name); 514 if (listptr[cnt-1] == NULL) { 515 warnx("malloc failed"); 516 return (NULL); 517 } 518 } 519 520 if (cnt == 0 || 521 (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { 522 if (cnt > 0) 523 warnx("malloc failed"); 524 free(listptr); 525 return (NULL); 526 } 527 528 *str = 'n'; *(str + 1) = 'o'; 529 for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { 530 strncpy(strptr, listptr[i], 32); 531 strptr += strlen(listptr[i]); 532 *strptr = ','; 533 free(listptr[i]); 534 } 535 *(--strptr) = NULL; 536 537 free(listptr); 538 return (str); 539 } 540