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 95 imax(int a, int b) 96 { 97 return (a > b ? a : b); 98 } 99 100 int 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 * 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 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 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 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 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 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 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 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 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 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 * 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