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