1 /*- 2 * Copyright (c) 1980, 1989, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by the University of 16 * California, Berkeley and its contributors. 17 * 4. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#) Copyright (c) 1980, 1989, 1993, 1994 The Regents of the University of California. All rights reserved. 34 * @(#)mount.c 8.25 (Berkeley) 5/8/95 35 * $FreeBSD: src/sbin/mount/mount.c,v 1.39.2.3 2001/08/01 08:26:23 obrien Exp $ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/mount.h> 40 #include <sys/mountctl.h> 41 #include <sys/stat.h> 42 #include <sys/wait.h> 43 44 #include <err.h> 45 #include <errno.h> 46 #include <fstab.h> 47 #include <pwd.h> 48 #include <signal.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 54 #include "extern.h" 55 #include "mntopts.h" 56 #include "pathnames.h" 57 58 /* `meta' options */ 59 #define MOUNT_META_OPTION_FSTAB "fstab" 60 #define MOUNT_META_OPTION_CURRENT "current" 61 62 int debug, fstab_style, verbose; 63 64 static char *catopt(char *, const char *); 65 static struct statfs 66 *getmntpt(const char *); 67 static int hasopt(const char *, const char *); 68 static int ismounted(struct fstab *, struct statfs *, int); 69 static int isremountable(const char *); 70 static void mangle(char *, int *, const char **); 71 static char *update_options(char *, char *, int); 72 static int mountfs(const char *, const char *, const char *, 73 int, const char *, const char *); 74 static void remopt(char *, const char *); 75 static void printdefvals(const struct statfs *); 76 static void prmount(struct statfs *); 77 static void putfsent(const struct statfs *); 78 static void usage(void); 79 static char *flags2opts(int); 80 static char *xstrdup(const char *str); 81 82 /* mount_ufs.c */ 83 int mount_ufs(int, const char **); 84 85 /* 86 * List of VFS types that can be remounted without becoming mounted on top 87 * of each other. 88 * XXX Is this list correct? 89 */ 90 static const char * 91 remountable_fs_names[] = { 92 "ufs", "ffs", "ext2fs", 93 0 94 }; 95 96 int 97 main(int argc, char **argv) 98 { 99 const char *mntfromname, **vfslist, *vfstype; 100 struct fstab *fs; 101 struct statfs *mntbuf; 102 FILE *mountdfp; 103 pid_t pid; 104 int all, ch, i, init_flags, mntsize, rval, have_fstab; 105 char *options; 106 107 all = init_flags = 0; 108 options = NULL; 109 vfslist = NULL; 110 vfstype = "ufs"; 111 while ((ch = getopt(argc, argv, "adF:fo:prwt:uv")) != -1) 112 switch (ch) { 113 case 'a': 114 all = 1; 115 break; 116 case 'd': 117 debug = 1; 118 break; 119 case 'F': 120 setfstab(optarg); 121 break; 122 case 'f': 123 init_flags |= MNT_FORCE; 124 break; 125 case 'o': 126 if (*optarg) 127 options = catopt(options, optarg); 128 break; 129 case 'p': 130 fstab_style = 1; 131 verbose = 1; 132 break; 133 case 'r': 134 options = catopt(options, "ro"); 135 break; 136 case 't': 137 if (vfslist != NULL) 138 errx(1, "only one -t option may be specified"); 139 vfslist = makevfslist(optarg); 140 vfstype = optarg; 141 break; 142 case 'u': 143 init_flags |= MNT_UPDATE; 144 break; 145 case 'v': 146 verbose = 1; 147 break; 148 case 'w': 149 options = catopt(options, "noro"); 150 break; 151 case '?': 152 default: 153 usage(); 154 /* NOTREACHED */ 155 } 156 argc -= optind; 157 argv += optind; 158 159 #define BADTYPE(type) \ 160 (strcmp(type, FSTAB_RO) && \ 161 strcmp(type, FSTAB_RW) && strcmp(type, FSTAB_RQ)) 162 163 rval = 0; 164 switch (argc) { 165 case 0: 166 if ((mntsize = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0) 167 err(1, "getmntinfo"); 168 if (all) { 169 while ((fs = getfsent()) != NULL) { 170 if (BADTYPE(fs->fs_type)) 171 continue; 172 if (checkvfsname(fs->fs_vfstype, vfslist)) 173 continue; 174 if (hasopt(fs->fs_mntops, "noauto")) 175 continue; 176 if (!(init_flags & MNT_UPDATE) && 177 ismounted(fs, mntbuf, mntsize)) 178 continue; 179 options = update_options(options, 180 fs->fs_mntops, mntbuf->f_flags); 181 if (mountfs(fs->fs_vfstype, fs->fs_spec, 182 fs->fs_file, init_flags, options, 183 fs->fs_mntops)) 184 rval = 1; 185 } 186 } else if (fstab_style) { 187 for (i = 0; i < mntsize; i++) { 188 if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) 189 continue; 190 putfsent(&mntbuf[i]); 191 } 192 } else { 193 for (i = 0; i < mntsize; i++) { 194 if (checkvfsname(mntbuf[i].f_fstypename, 195 vfslist)) 196 continue; 197 prmount(&mntbuf[i]); 198 } 199 } 200 exit(rval); 201 case 1: 202 if (vfslist != NULL) 203 usage(); 204 205 rmslashes(*argv, *argv); 206 207 if (init_flags & MNT_UPDATE) { 208 mntfromname = NULL; 209 have_fstab = 0; 210 if ((mntbuf = getmntpt(*argv)) == NULL) 211 errx(1, "not currently mounted %s", *argv); 212 /* 213 * Only get the mntflags from fstab if both mntpoint 214 * and mntspec are identical. Also handle the special 215 * case where just '/' is mounted and 'spec' is not 216 * identical with the one from fstab ('/dev' is missing 217 * in the spec-string at boot-time). 218 */ 219 if ((fs = getfsfile(mntbuf->f_mntonname)) != NULL) { 220 if (strcmp(fs->fs_spec, 221 mntbuf->f_mntfromname) == 0 && 222 strcmp(fs->fs_file, 223 mntbuf->f_mntonname) == 0) { 224 have_fstab = 1; 225 mntfromname = mntbuf->f_mntfromname; 226 } else if (argv[0][0] == '/' && 227 argv[0][1] == '\0') { 228 fs = getfsfile("/"); 229 have_fstab = 1; 230 mntfromname = fs->fs_spec; 231 } 232 } 233 if (have_fstab) { 234 options = update_options(options, fs->fs_mntops, 235 mntbuf->f_flags); 236 } else { 237 mntfromname = mntbuf->f_mntfromname; 238 options = update_options(options, NULL, 239 mntbuf->f_flags); 240 } 241 rval = mountfs(mntbuf->f_fstypename, mntfromname, 242 mntbuf->f_mntonname, init_flags, options, 0); 243 break; 244 } 245 if ((fs = getfsfile(*argv)) == NULL && 246 (fs = getfsspec(*argv)) == NULL) 247 errx(1, "%s: unknown special file or file system", 248 *argv); 249 if (BADTYPE(fs->fs_type)) 250 errx(1, "%s has unknown file system type", 251 *argv); 252 rval = mountfs(fs->fs_vfstype, fs->fs_spec, fs->fs_file, 253 init_flags, options, fs->fs_mntops); 254 break; 255 case 2: 256 /* 257 * If -t flag has not been specified, the path cannot be 258 * found, spec contains either a ':' or a '@', and the 259 * spec is not a file with those characters, then assume 260 * that an NFS filesystem is being specified ala Sun. 261 */ 262 if (vfslist == NULL && strpbrk(argv[0], ":@") != NULL && 263 access(argv[0], 0) == -1) 264 vfstype = "nfs"; 265 rval = mountfs(vfstype, getdevpath(argv[0], 0), argv[1], 266 init_flags, options, NULL); 267 break; 268 default: 269 usage(); 270 /* NOTREACHED */ 271 } 272 273 /* 274 * If the mount was successfully, and done by root, tell mountd the 275 * good news. Pid checks are probably unnecessary, but don't hurt. 276 */ 277 if (rval == 0 && getuid() == 0 && 278 (mountdfp = fopen(_PATH_MOUNTDPID, "r")) != NULL) { 279 if (fscanf(mountdfp, "%d", &pid) == 1 && 280 pid > 0 && kill(pid, SIGHUP) == -1 && errno != ESRCH) 281 err(1, "signal mountd"); 282 fclose(mountdfp); 283 } 284 285 exit(rval); 286 } 287 288 static int 289 ismounted(struct fstab *fs, struct statfs *mntbuf, int mntsize) 290 { 291 int i; 292 293 if (fs->fs_file[0] == '/' && fs->fs_file[1] == '\0') 294 /* the root file system can always be remounted */ 295 return (0); 296 297 for (i = mntsize - 1; i >= 0; --i) 298 if (strcmp(fs->fs_file, mntbuf[i].f_mntonname) == 0 && 299 (!isremountable(fs->fs_vfstype) || 300 strcmp(fs->fs_spec, mntbuf[i].f_mntfromname) == 0)) 301 return (1); 302 return (0); 303 } 304 305 static int 306 isremountable(const char *vfsname) 307 { 308 const char **cp; 309 310 for (cp = remountable_fs_names; *cp; cp++) 311 if (strcmp(*cp, vfsname) == 0) 312 return (1); 313 return (0); 314 } 315 316 static int 317 hasopt(const char *mntopts, const char *option) 318 { 319 int negative, found; 320 char *opt, *optbuf; 321 322 if (option[0] == 'n' && option[1] == 'o') { 323 negative = 1; 324 option += 2; 325 } else 326 negative = 0; 327 optbuf = xstrdup(mntopts); 328 found = 0; 329 for (opt = optbuf; (opt = strtok(opt, ",")) != NULL; opt = NULL) { 330 if (opt[0] == 'n' && opt[1] == 'o') { 331 if (!strcasecmp(opt + 2, option)) 332 found = negative; 333 } else if (!strcasecmp(opt, option)) 334 found = !negative; 335 } 336 free(optbuf); 337 return (found); 338 } 339 340 static int 341 mountfs(const char *vfstype, const char *spec, const char *name, int flags, 342 const char *options, const char *mntopts) 343 { 344 /* List of directories containing mount_xxx subcommands. */ 345 static const char *edirs[] = { 346 _PATH_SBIN, 347 _PATH_USRSBIN, 348 NULL 349 }; 350 const char *argv[100], **edir; 351 struct statfs sf; 352 pid_t pid; 353 int argc, i, status; 354 char *optbuf, execname[MAXPATHLEN + 1], mntpath[MAXPATHLEN]; 355 356 #if __GNUC__ 357 (void)&optbuf; 358 (void)&name; 359 #endif 360 361 /* resolve the mountpoint with realpath(3) */ 362 checkpath(name, mntpath); 363 name = mntpath; 364 365 if (mntopts == NULL) 366 mntopts = ""; 367 if (options == NULL) { 368 if (*mntopts == '\0') { 369 options = "rw"; 370 } else { 371 options = mntopts; 372 mntopts = ""; 373 } 374 } 375 optbuf = catopt(xstrdup(mntopts), options); 376 377 if (strcmp(name, "/") == 0) 378 flags |= MNT_UPDATE; 379 if (flags & MNT_FORCE) 380 optbuf = catopt(optbuf, "force"); 381 if (flags & MNT_RDONLY) 382 optbuf = catopt(optbuf, "ro"); 383 /* 384 * XXX 385 * The mount_mfs (newfs) command uses -o to select the 386 * optimization mode. We don't pass the default "-o rw" 387 * for that reason. 388 */ 389 if (flags & MNT_UPDATE) 390 optbuf = catopt(optbuf, "update"); 391 392 argc = 0; 393 argv[argc++] = vfstype; 394 mangle(optbuf, &argc, argv); 395 argv[argc++] = spec; 396 argv[argc++] = name; 397 argv[argc] = NULL; 398 399 if (debug) { 400 printf("exec: mount_%s", vfstype); 401 for (i = 1; i < argc; i++) 402 printf(" %s", argv[i]); 403 printf("\n"); 404 return (0); 405 } 406 407 switch (pid = fork()) { 408 case -1: /* Error. */ 409 warn("fork"); 410 free(optbuf); 411 return (1); 412 case 0: /* Child. */ 413 if (strcmp(vfstype, "ufs") == 0) 414 exit(mount_ufs(argc, argv)); 415 416 /* Go find an executable. */ 417 for (edir = edirs; *edir; edir++) { 418 snprintf(execname, 419 sizeof(execname), "%s/mount_%s", *edir, vfstype); 420 execv(execname, __DECONST(char * const *, argv)); 421 } 422 if (errno == ENOENT) { 423 int len = 0; 424 char *cp; 425 for (edir = edirs; *edir; edir++) 426 len += strlen(*edir) + 2; /* ", " */ 427 if ((cp = malloc(len)) == NULL) 428 errx(1, "malloc failed"); 429 cp[0] = '\0'; 430 for (edir = edirs; *edir; edir++) { 431 strcat(cp, *edir); 432 if (edir[1] != NULL) 433 strcat(cp, ", "); 434 } 435 warn("exec mount_%s not found in %s", vfstype, cp); 436 } 437 exit(1); 438 /* NOTREACHED */ 439 default: /* Parent. */ 440 free(optbuf); 441 442 if (waitpid(pid, &status, 0) < 0) { 443 warn("waitpid"); 444 return (1); 445 } 446 447 if (WIFEXITED(status)) { 448 if (WEXITSTATUS(status) != 0) 449 return (WEXITSTATUS(status)); 450 } else if (WIFSIGNALED(status)) { 451 warnx("%s: %s", name, sys_siglist[WTERMSIG(status)]); 452 return (1); 453 } 454 455 if (verbose) { 456 if (statfs(name, &sf) < 0) { 457 warn("statfs %s", name); 458 return (1); 459 } 460 if (fstab_style) 461 putfsent(&sf); 462 else 463 prmount(&sf); 464 } 465 break; 466 } 467 468 return (0); 469 } 470 471 static void 472 prmount(struct statfs *sfp) 473 { 474 struct passwd *pw; 475 char *buf; 476 int error; 477 int len; 478 479 error = 0; 480 len = 256; 481 482 if ((buf = malloc(len)) == NULL) 483 errx(1, "malloc failed"); 484 485 printf("%s on %s (%s", sfp->f_mntfromname, sfp->f_mntonname, 486 sfp->f_fstypename); 487 488 /* Get a string buffer with all the used flags names */ 489 error = mountctl(sfp->f_mntonname, MOUNTCTL_MOUNTFLAGS, 490 -1, NULL, 0, buf, len); 491 492 if (sfp->f_owner) { 493 printf(", mounted by "); 494 if ((pw = getpwuid(sfp->f_owner)) != NULL) 495 printf("%s", pw->pw_name); 496 else 497 printf("%d", sfp->f_owner); 498 } 499 500 if (error != -1 && strlen(buf)) 501 printf(", %s", buf); 502 503 if (verbose) { 504 if (sfp->f_syncwrites != 0 || sfp->f_asyncwrites != 0) 505 printf(", writes: sync %ld async %ld", 506 sfp->f_syncwrites, sfp->f_asyncwrites); 507 if (sfp->f_syncreads != 0 || sfp->f_asyncreads != 0) 508 printf(", reads: sync %ld async %ld", 509 sfp->f_syncreads, sfp->f_asyncreads); 510 } 511 printf(")\n"); 512 } 513 514 static struct statfs * 515 getmntpt(const char *name) 516 { 517 struct statfs *mntbuf; 518 int i, mntsize; 519 520 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 521 for (i = mntsize - 1; i >= 0; i--) { 522 if (strcmp(mntbuf[i].f_mntfromname, name) == 0 || 523 strcmp(mntbuf[i].f_mntonname, name) == 0) 524 return (&mntbuf[i]); 525 } 526 return (NULL); 527 } 528 529 static char * 530 catopt(char *s0, const char *s1) 531 { 532 size_t i; 533 char *cp; 534 535 if (s1 == NULL || *s1 == '\0') 536 return s0; 537 538 if (s0 && *s0) { 539 i = strlen(s0) + strlen(s1) + 1 + 1; 540 if ((cp = malloc(i)) == NULL) 541 errx(1, "malloc failed"); 542 snprintf(cp, i, "%s,%s", s0, s1); 543 } else 544 cp = xstrdup(s1); 545 546 if (s0) 547 free(s0); 548 return (cp); 549 } 550 551 static void 552 mangle(char *options, int *argcp, const char **argv) 553 { 554 char *p, *s; 555 int argc; 556 557 argc = *argcp; 558 for (s = options; (p = strsep(&s, ",")) != NULL;) 559 if (*p != '\0') { 560 if (*p == '-') { 561 argv[argc++] = p; 562 p = strchr(p, '='); 563 if (p) { 564 *p = '\0'; 565 argv[argc++] = p+1; 566 } 567 } else if (strcmp(p, "rw") != 0) { 568 argv[argc++] = "-o"; 569 argv[argc++] = p; 570 } 571 } 572 573 *argcp = argc; 574 } 575 576 577 static char * 578 update_options(char *opts, char *fstab, int curflags) 579 { 580 char *o, *p; 581 char *cur; 582 char *expopt, *newopt, *tmpopt; 583 584 if (opts == NULL) 585 return xstrdup(""); 586 587 /* remove meta options from list */ 588 remopt(fstab, MOUNT_META_OPTION_FSTAB); 589 remopt(fstab, MOUNT_META_OPTION_CURRENT); 590 cur = flags2opts(curflags); 591 592 /* 593 * Expand all meta-options passed to us first. 594 */ 595 expopt = NULL; 596 for (p = opts; (o = strsep(&p, ",")) != NULL;) { 597 if (strcmp(MOUNT_META_OPTION_FSTAB, o) == 0) 598 expopt = catopt(expopt, fstab); 599 else if (strcmp(MOUNT_META_OPTION_CURRENT, o) == 0) 600 expopt = catopt(expopt, cur); 601 else 602 expopt = catopt(expopt, o); 603 } 604 free(cur); 605 free(opts); 606 607 /* 608 * Remove previous contradictory arguments. Given option "foo" we 609 * remove all the "nofoo" options. Given "nofoo" we remove "nonofoo" 610 * and "foo" - so we can deal with possible options like "notice". 611 */ 612 newopt = NULL; 613 for (p = expopt; (o = strsep(&p, ",")) != NULL;) { 614 if ((tmpopt = malloc( strlen(o) + 2 + 1 )) == NULL) 615 errx(1, "malloc failed"); 616 617 strcpy(tmpopt, "no"); 618 strcat(tmpopt, o); 619 remopt(newopt, tmpopt); 620 free(tmpopt); 621 622 if (strncmp("no", o, 2) == 0) 623 remopt(newopt, o+2); 624 625 newopt = catopt(newopt, o); 626 } 627 free(expopt); 628 629 return newopt; 630 } 631 632 static void 633 remopt(char *string, const char *opt) 634 { 635 char *o, *p, *r; 636 637 if (string == NULL || *string == '\0' || opt == NULL || *opt == '\0') 638 return; 639 640 r = string; 641 642 for (p = string; (o = strsep(&p, ",")) != NULL;) { 643 if (strcmp(opt, o) != 0) { 644 if (*r == ',' && *o != '\0') 645 r++; 646 while ((*r++ = *o++) != '\0') 647 ; 648 *--r = ','; 649 } 650 } 651 *r = '\0'; 652 } 653 654 static void 655 usage(void) 656 { 657 658 fprintf(stderr, "%s\n%s\n%s\n", 659 "usage: mount [-adfpruvw] [-F fstab] [-o options] [-t type]", 660 " mount [-dfpruvw] {special | node}", 661 " mount [-dfpruvw] [-o options] [-t type] special node"); 662 exit(1); 663 } 664 665 /* 666 * Prints the default values for dump frequency and pass number of fsck. 667 * This happens when mount(8) is called with -p and there is no fstab file 668 * or there is but the values aren't specified. 669 */ 670 static void 671 printdefvals(const struct statfs *ent) 672 { 673 if (strcmp(ent->f_fstypename, "ufs") == 0) { 674 if (strcmp(ent->f_mntonname, "/") == 0) 675 printf("\t1 1\n"); 676 else 677 printf("\t2 2\n"); 678 } else { 679 printf("\t0 0\n"); 680 } 681 } 682 683 static void 684 putfsent(const struct statfs *ent) 685 { 686 struct stat sb; 687 struct fstab *fst; 688 char *opts; 689 690 opts = flags2opts(ent->f_flags); 691 printf("%s\t%s\t%s %s", ent->f_mntfromname, ent->f_mntonname, 692 ent->f_fstypename, opts); 693 free(opts); 694 695 /* 696 * If fstab file is missing don't call getfsspec() or getfsfile() 697 * at all, because the same warning will be printed twice for every 698 * mounted filesystem. 699 */ 700 if (stat(_PATH_FSTAB, &sb) != -1) { 701 if ((fst = getfsspec(ent->f_mntfromname))) 702 printf("\t%u %u\n", fst->fs_freq, fst->fs_passno); 703 else if ((fst = getfsfile(ent->f_mntonname))) 704 printf("\t%u %u\n", fst->fs_freq, fst->fs_passno); 705 else 706 printdefvals(ent); 707 } else { 708 printdefvals(ent); 709 } 710 } 711 712 713 static char * 714 flags2opts(int flags) 715 { 716 char *res; 717 718 res = NULL; 719 720 res = catopt(res, (flags & MNT_RDONLY) ? "ro" : "rw"); 721 722 if (flags & MNT_SYNCHRONOUS) res = catopt(res, "sync"); 723 if (flags & MNT_NOEXEC) res = catopt(res, "noexec"); 724 if (flags & MNT_NOSUID) res = catopt(res, "nosuid"); 725 if (flags & MNT_NODEV) res = catopt(res, "nodev"); 726 if (flags & MNT_UNION) res = catopt(res, "union"); 727 if (flags & MNT_ASYNC) res = catopt(res, "async"); 728 if (flags & MNT_NOATIME) res = catopt(res, "noatime"); 729 if (flags & MNT_NOCLUSTERR) res = catopt(res, "noclusterr"); 730 if (flags & MNT_NOCLUSTERW) res = catopt(res, "noclusterw"); 731 if (flags & MNT_NOSYMFOLLOW) res = catopt(res, "nosymfollow"); 732 if (flags & MNT_SUIDDIR) res = catopt(res, "suiddir"); 733 if (flags & MNT_IGNORE) res = catopt(res, "ignore"); 734 735 return res; 736 } 737 738 static char* 739 xstrdup(const char *str) 740 { 741 char* ret = strdup(str); 742 if(ret == NULL) { 743 errx(1, "strdup failed (could not allocate memory)"); 744 } 745 return ret; 746 } 747