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