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