1 /* 2 * Copyright (c) 2008 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 3. Neither the name of The DragonFly Project nor the names of its 18 * contributors may be used to endorse or promote products derived 19 * from this software without specific, prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.6 2008/10/07 22:28:41 thomas Exp $ 35 */ 36 /* 37 * Clean up a specific HAMMER filesystem or all HAMMER filesystems. 38 * 39 * Each filesystem is expected to have a <mount>/snapshots directory. 40 * No cleanup will be performed on any filesystem that does not. If 41 * no filesystems are specified the 'df' program is run and any HAMMER 42 * or null-mounted hammer PFS's are extracted. 43 * 44 * The snapshots directory may contain a config file called 'config'. If 45 * no config file is present one will be created with the following 46 * defaults: 47 * 48 * snapshots 1d 60d (0d 0d for /tmp, /var/tmp, /usr/obj) 49 * prune 1d 5m 50 * reblock 1d 5m 51 * recopy 30d 5m 52 * 53 * All hammer commands create and maintain cycle files in the snapshots 54 * directory. 55 */ 56 57 #include "hammer.h" 58 59 struct didpfs { 60 struct didpfs *next; 61 uuid_t uuid; 62 }; 63 64 static void do_cleanup(const char *path); 65 static int strtosecs(char *ptr); 66 static const char *dividing_slash(const char *path); 67 static int check_period(const char *snapshots_path, const char *cmd, int arg1, 68 time_t *savep); 69 static void save_period(const char *snapshots_path, const char *cmd, 70 time_t savet); 71 static int check_softlinks(const char *snapshots_path); 72 static void cleanup_softlinks(const char *path, const char *snapshots_path, 73 int arg2, char *arg3); 74 static int check_expired(const char *fpath, int arg2); 75 76 static int cleanup_snapshots(const char *path, const char *snapshots_path, 77 int arg1, int arg2); 78 static int cleanup_prune(const char *path, const char *snapshots_path, 79 int arg1, int arg2, int snapshots_disabled); 80 static int cleanup_reblock(const char *path, const char *snapshots_path, 81 int arg1, int arg2); 82 static int cleanup_recopy(const char *path, const char *snapshots_path, 83 int arg1, int arg2); 84 85 static void runcmd(int *resp, const char *ctl, ...); 86 87 #define WS " \t\r\n" 88 89 struct didpfs *FirstPFS; 90 91 void 92 hammer_cmd_cleanup(char **av, int ac) 93 { 94 FILE *fp; 95 char *fs, *ptr, *path; 96 char buf[256]; 97 98 tzset(); 99 if (ac == 0) { 100 fp = popen("/sbin/mount -t hammer,null", "r"); 101 if (fp == NULL) 102 errx(1, "hammer cleanup: 'mount' failed"); 103 while (fgets(buf, sizeof(buf), fp) != NULL) { 104 fs = strtok(buf, WS); 105 if (fs == NULL) 106 continue; 107 ptr = strtok(NULL, WS); 108 if (ptr == NULL) 109 continue; 110 path = strtok(NULL, WS); 111 if (path == NULL) 112 continue; 113 ptr = strtok(NULL, WS); 114 if (ptr == NULL) 115 continue; 116 if ((strncmp(ptr, "(hammer,", 8) == 0) || 117 ((strncmp(ptr, "(null,", 6) == 0) && 118 (strstr(fs, "/@@0x") != NULL || 119 strstr(fs, "/@@-1") != NULL))) { 120 do_cleanup(path); 121 } 122 } 123 fclose(fp); 124 } else { 125 while (ac) { 126 do_cleanup(*av); 127 --ac; 128 ++av; 129 } 130 } 131 } 132 133 static 134 void 135 do_cleanup(const char *path) 136 { 137 struct hammer_ioc_pseudofs_rw pfs; 138 union hammer_ioc_mrecord_any mrec_tmp; 139 char *snapshots_path; 140 char *config_path; 141 struct stat st; 142 char *cmd; 143 char *ptr; 144 int arg1; 145 int arg2; 146 char *arg3; 147 time_t savet; 148 char buf[256]; 149 FILE *fp; 150 struct didpfs *didpfs; 151 int snapshots_disabled = 0; 152 int prune_warning = 0; 153 int fd; 154 int r; 155 156 bzero(&pfs, sizeof(pfs)); 157 bzero(&mrec_tmp, sizeof(mrec_tmp)); 158 pfs.ondisk = &mrec_tmp.pfs.pfsd; 159 pfs.bytes = sizeof(mrec_tmp.pfs.pfsd); 160 pfs.pfs_id = -1; 161 162 printf("cleanup %-20s -", path); 163 fd = open(path, O_RDONLY); 164 if (fd < 0) { 165 printf(" unable to access directory: %s\n", strerror(errno)); 166 return; 167 } 168 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) { 169 printf(" not a HAMMER filesystem: %s\n", strerror(errno)); 170 return; 171 } 172 close(fd); 173 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) { 174 printf(" unrecognized HAMMER version\n"); 175 return; 176 } 177 178 /* 179 * Make sure we have not already handled this PFS. Several nullfs 180 * mounts might alias the same PFS. 181 */ 182 for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) { 183 if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) { 184 printf(" PFS #%d already handled\n", pfs.pfs_id); 185 return; 186 } 187 } 188 didpfs = malloc(sizeof(*didpfs)); 189 didpfs->next = FirstPFS; 190 FirstPFS = didpfs; 191 didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid; 192 193 /* 194 * Figure out where the snapshot directory is. 195 */ 196 if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') { 197 asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots); 198 } else if (mrec_tmp.pfs.pfsd.snapshots[0]) { 199 printf(" WARNING: pfs-slave's snapshots dir is not absolute\n"); 200 return; 201 } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) { 202 printf(" WARNING: must configure snapshot dir for PFS slave\n"); 203 printf("\tWe suggest <fs>/var/slaves/<name> where " 204 "<fs> is the base HAMMER fs\n"); 205 printf("\tcontaining the slave\n"); 206 return; 207 } else { 208 asprintf(&snapshots_path, 209 "%s%ssnapshots", path, dividing_slash(path)); 210 } 211 212 /* 213 * Create a snapshot directory if necessary, and a config file if 214 * necessary. 215 */ 216 if (stat(snapshots_path, &st) < 0) { 217 if (mkdir(snapshots_path, 0755) != 0) { 218 free(snapshots_path); 219 printf(" unable to create snapshot dir \"%s\": %s\n", 220 snapshots_path, strerror(errno)); 221 return; 222 } 223 } 224 asprintf(&config_path, "%s/config", snapshots_path); 225 if ((fp = fopen(config_path, "r")) == NULL) { 226 fp = fopen(config_path, "w"); 227 if (fp == NULL) { 228 printf(" cannot create %s: %s\n", 229 config_path, strerror(errno)); 230 return; 231 } 232 if (strcmp(path, "/tmp") == 0 || 233 strcmp(path, "/var/tmp") == 0 || 234 strcmp(path, "/usr/obj") == 0) { 235 fprintf(fp, "snapshots 0d 0d\n"); 236 } else { 237 fprintf(fp, "snapshots 1d 60d\n"); 238 } 239 fprintf(fp, 240 "prune 1d 5m\n" 241 "reblock 1d 5m\n" 242 "recopy 30d 10m\n"); 243 fclose(fp); 244 fp = fopen(config_path, "r"); 245 } 246 if (fp == NULL) { 247 printf(" cannot access %s: %s\n", 248 config_path, strerror(errno)); 249 return; 250 } 251 252 printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path); 253 254 /* 255 * Process the config file 256 */ 257 while (fgets(buf, sizeof(buf), fp) != NULL) { 258 cmd = strtok(buf, WS); 259 arg1 = 0; 260 arg2 = 0; 261 arg3 = NULL; 262 if ((ptr = strtok(NULL, WS)) != NULL) { 263 arg1 = strtosecs(ptr); 264 if ((ptr = strtok(NULL, WS)) != NULL) { 265 arg2 = strtosecs(ptr); 266 arg3 = strtok(NULL, WS); 267 } 268 } 269 270 printf("%20s - ", cmd); 271 fflush(stdout); 272 273 r = 1; 274 if (strcmp(cmd, "snapshots") == 0) { 275 if (arg1 == 0) { 276 if (arg2 && check_softlinks(snapshots_path)) { 277 printf("only removing old snapshots\n"); 278 prune_warning = 1; 279 cleanup_softlinks(path, snapshots_path, 280 arg2, arg3); 281 } else { 282 printf("disabled\n"); 283 snapshots_disabled = 1; 284 } 285 } else 286 if (check_period(snapshots_path, cmd, arg1, &savet)) { 287 printf("run\n"); 288 cleanup_softlinks(path, snapshots_path, 289 arg2, arg3); 290 r = cleanup_snapshots(path, snapshots_path, 291 arg1, arg2); 292 } else { 293 printf("skip\n"); 294 } 295 } else if (arg1 == 0) { 296 /* 297 * The commands following this check can't handle 298 * a period of 0, so call the feature disabled and 299 * ignore the directive. 300 */ 301 printf("disabled\n"); 302 } else if (strcmp(cmd, "prune") == 0) { 303 if (check_period(snapshots_path, cmd, arg1, &savet)) { 304 if (prune_warning) { 305 printf("run - WARNING snapshot " 306 "softlinks present " 307 "but snapshots disabled\n"); 308 } else { 309 printf("run\n"); 310 } 311 r = cleanup_prune(path, snapshots_path, 312 arg1, arg2, snapshots_disabled); 313 } else { 314 printf("skip\n"); 315 } 316 } else if (strcmp(cmd, "reblock") == 0) { 317 if (check_period(snapshots_path, cmd, arg1, &savet)) { 318 printf("run"); 319 fflush(stdout); 320 if (VerboseOpt) 321 printf("\n"); 322 r = cleanup_reblock(path, snapshots_path, 323 arg1, arg2); 324 } else { 325 printf("skip\n"); 326 } 327 } else if (strcmp(cmd, "recopy") == 0) { 328 if (check_period(snapshots_path, cmd, arg1, &savet)) { 329 printf("run"); 330 fflush(stdout); 331 if (VerboseOpt) 332 printf("\n"); 333 r = cleanup_recopy(path, snapshots_path, 334 arg1, arg2); 335 } else { 336 printf("skip\n"); 337 } 338 } else { 339 printf("unknown directive\n"); 340 r = 1; 341 } 342 if (r == 0) 343 save_period(snapshots_path, cmd, savet); 344 } 345 fclose(fp); 346 usleep(1000); 347 } 348 349 static 350 int 351 strtosecs(char *ptr) 352 { 353 int val; 354 355 val = strtol(ptr, &ptr, 0); 356 switch(*ptr) { 357 case 'd': 358 val *= 24; 359 /* fall through */ 360 case 'h': 361 val *= 60; 362 /* fall through */ 363 case 'm': 364 val *= 60; 365 /* fall through */ 366 case 's': 367 break; 368 default: 369 errx(1, "illegal suffix converting %s\n", ptr); 370 break; 371 } 372 return(val); 373 } 374 375 static const char * 376 dividing_slash(const char *path) 377 { 378 int len = strlen(path); 379 if (len && path[len-1] == '/') 380 return(""); 381 else 382 return("/"); 383 } 384 385 /* 386 * Check whether the desired period has elapsed since the last successful 387 * run. The run may take a while and cross a boundary so we remember the 388 * current time_t so we can save it later on. 389 * 390 * Periods in minutes, hours, or days are assumed to have been crossed 391 * if the local time crosses a minute, hour, or day boundary regardless 392 * of how close the last operation actually was. 393 */ 394 static int 395 check_period(const char *snapshots_path, const char *cmd, int arg1, 396 time_t *savep) 397 { 398 char *check_path; 399 struct tm tp1; 400 struct tm tp2; 401 FILE *fp; 402 time_t baset, lastt; 403 char buf[256]; 404 405 time(savep); 406 localtime_r(savep, &tp1); 407 408 /* 409 * Retrieve the start time of the last successful operation. 410 */ 411 asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd); 412 fp = fopen(check_path, "r"); 413 free(check_path); 414 if (fp == NULL) 415 return(1); 416 if (fgets(buf, sizeof(buf), fp) == NULL) { 417 fclose(fp); 418 return(1); 419 } 420 fclose(fp); 421 422 lastt = strtol(buf, NULL, 0); 423 localtime_r(&lastt, &tp2); 424 425 /* 426 * Normalize the times. e.g. if asked to do something on a 1-day 427 * interval the operation will be performed as soon as the day 428 * turns over relative to the previous operation, even if the previous 429 * operation ran a few seconds ago just before midnight. 430 */ 431 if (arg1 % 60 == 0) { 432 tp1.tm_sec = 0; 433 tp2.tm_sec = 0; 434 } 435 if (arg1 % (60 * 60) == 0) { 436 tp1.tm_min = 0; 437 tp2.tm_min = 0; 438 } 439 if (arg1 % (24 * 60 * 60) == 0) { 440 tp1.tm_hour = 0; 441 tp2.tm_hour = 0; 442 } 443 444 baset = mktime(&tp1); 445 lastt = mktime(&tp2); 446 447 #if 0 448 printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1); 449 #endif 450 451 if ((int)(baset - lastt) >= arg1) 452 return(1); 453 return(0); 454 } 455 456 /* 457 * Store the start time of the last successful operation. 458 */ 459 static void 460 save_period(const char *snapshots_path, const char *cmd, 461 time_t savet) 462 { 463 char *ocheck_path; 464 char *ncheck_path; 465 FILE *fp; 466 467 asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd); 468 asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd); 469 fp = fopen(ncheck_path, "w"); 470 fprintf(fp, "0x%08llx\n", (long long)savet); 471 if (fclose(fp) == 0) 472 rename(ncheck_path, ocheck_path); 473 remove(ncheck_path); 474 } 475 476 /* 477 * Simply count the number of softlinks in the snapshots dir 478 */ 479 static int 480 check_softlinks(const char *snapshots_path) 481 { 482 struct dirent *den; 483 struct stat st; 484 DIR *dir; 485 char *fpath; 486 int res = 0; 487 488 if ((dir = opendir(snapshots_path)) != NULL) { 489 while ((den = readdir(dir)) != NULL) { 490 if (den->d_name[0] == '.') 491 continue; 492 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 493 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) 494 ++res; 495 free(fpath); 496 } 497 closedir(dir); 498 } 499 return(res); 500 } 501 502 /* 503 * Clean up expired softlinks in the snapshots dir 504 */ 505 static void 506 cleanup_softlinks(const char *path __unused, const char *snapshots_path, 507 int arg2, char *arg3) 508 { 509 struct dirent *den; 510 struct stat st; 511 DIR *dir; 512 char *fpath; 513 int anylink = 0; 514 515 if (arg3 != NULL && strstr(arg3, "any") != NULL) 516 anylink = 1; 517 518 if ((dir = opendir(snapshots_path)) != NULL) { 519 while ((den = readdir(dir)) != NULL) { 520 if (den->d_name[0] == '.') 521 continue; 522 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 523 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) && 524 (anylink || strncmp(den->d_name, "snap-", 5) == 0) 525 ) { 526 if (check_expired(den->d_name, arg2)) { 527 if (VerboseOpt) { 528 printf(" expire %s\n", 529 fpath); 530 } 531 remove(fpath); 532 } 533 } 534 free(fpath); 535 } 536 closedir(dir); 537 } 538 } 539 540 /* 541 * Take a softlink path in the form snap-yyyymmdd-hhmm and the 542 * expiration in seconds (arg2) and return non-zero if the softlink 543 * has expired. 544 */ 545 static int 546 check_expired(const char *fpath, int arg2) 547 { 548 struct tm tm; 549 time_t t; 550 int year; 551 int month; 552 int day = 0; 553 int hour = 0; 554 int minute = 0; 555 int r; 556 557 while (*fpath && *fpath != '-' && *fpath != '.') 558 ++fpath; 559 if (*fpath) 560 ++fpath; 561 562 r = sscanf(fpath, "%4d%2d%2d-%2d%2d", 563 &year, &month, &day, &hour, &minute); 564 565 if (r >= 3) { 566 bzero(&tm, sizeof(tm)); 567 tm.tm_isdst = -1; 568 tm.tm_min = minute; 569 tm.tm_hour = hour; 570 tm.tm_mday = day; 571 tm.tm_mon = month - 1; 572 tm.tm_year = year - 1900; 573 t = mktime(&tm); 574 if (t == (time_t)-1) 575 return(0); 576 t = time(NULL) - t; 577 if ((int)t > arg2) 578 return(1); 579 } 580 return(0); 581 } 582 583 /* 584 * Issue a snapshot. 585 */ 586 static int 587 cleanup_snapshots(const char *path __unused, const char *snapshots_path, 588 int arg1 __unused, int arg2 __unused) 589 { 590 int r; 591 592 runcmd(&r, "hammer snapshot %s %s", path, snapshots_path); 593 return(r); 594 } 595 596 static int 597 cleanup_prune(const char *path __unused, const char *snapshots_path, 598 int arg1 __unused, int arg2, int snapshots_disabled) 599 { 600 /* 601 * If snapshots have been disabled run prune-everything instead 602 * of prune. 603 */ 604 if (snapshots_disabled && arg2) { 605 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s", 606 snapshots_path, arg2, path); 607 } else if (snapshots_disabled) { 608 runcmd(NULL, "hammer prune-everything %s", path); 609 } else if (arg2) { 610 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s", 611 snapshots_path, arg2, snapshots_path); 612 } else { 613 runcmd(NULL, "hammer prune %s", snapshots_path); 614 } 615 return(0); 616 } 617 618 static int 619 cleanup_reblock(const char *path, const char *snapshots_path, 620 int arg1 __unused, int arg2) 621 { 622 if (VerboseOpt == 0) { 623 printf("."); 624 fflush(stdout); 625 } 626 627 /* 628 * When reblocking the B-Tree always reblock everything in normal 629 * mode. 630 */ 631 runcmd(NULL, 632 "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s", 633 snapshots_path, arg2, path); 634 if (VerboseOpt == 0) { 635 printf("."); 636 fflush(stdout); 637 } 638 639 /* 640 * When reblocking the inodes always reblock everything in normal 641 * mode. 642 */ 643 runcmd(NULL, 644 "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s", 645 snapshots_path, arg2, path); 646 if (VerboseOpt == 0) { 647 printf("."); 648 fflush(stdout); 649 } 650 651 /* 652 * When reblocking the directories always reblock everything in normal 653 * mode. 654 */ 655 runcmd(NULL, 656 "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s", 657 snapshots_path, arg2, path); 658 if (VerboseOpt == 0) { 659 printf("."); 660 fflush(stdout); 661 } 662 663 /* 664 * Do not reblock all the data in normal mode. 665 */ 666 runcmd(NULL, 667 "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95", 668 snapshots_path, arg2, path); 669 if (VerboseOpt == 0) 670 printf("\n"); 671 return(0); 672 } 673 674 static int 675 cleanup_recopy(const char *path, const char *snapshots_path, 676 int arg1 __unused, int arg2) 677 { 678 if (VerboseOpt == 0) { 679 printf("."); 680 fflush(stdout); 681 } 682 runcmd(NULL, 683 "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s", 684 snapshots_path, arg2, path); 685 if (VerboseOpt == 0) { 686 printf("."); 687 fflush(stdout); 688 } 689 runcmd(NULL, 690 "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s", 691 snapshots_path, arg2, path); 692 if (VerboseOpt == 0) { 693 printf("."); 694 fflush(stdout); 695 } 696 runcmd(NULL, 697 "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s", 698 snapshots_path, arg2, path); 699 if (VerboseOpt == 0) { 700 printf("."); 701 fflush(stdout); 702 } 703 runcmd(NULL, 704 "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s", 705 snapshots_path, arg2, path); 706 if (VerboseOpt == 0) 707 printf("\n"); 708 return(0); 709 } 710 711 static 712 void 713 runcmd(int *resp, const char *ctl, ...) 714 { 715 va_list va; 716 char *cmd; 717 char *arg; 718 char **av; 719 int n; 720 int nmax; 721 int res; 722 pid_t pid; 723 724 /* 725 * Generate the command 726 */ 727 va_start(va, ctl); 728 vasprintf(&cmd, ctl, va); 729 va_end(va); 730 if (VerboseOpt) 731 printf(" %s\n", cmd); 732 733 /* 734 * Break us down into arguments. We do not just use system() here 735 * because it blocks SIGINT and friends. 736 */ 737 n = 0; 738 nmax = 16; 739 av = malloc(sizeof(char *) * nmax); 740 741 for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) { 742 if (n == nmax - 1) { 743 nmax += 16; 744 av = realloc(av, sizeof(char *) * nmax); 745 } 746 av[n++] = arg; 747 } 748 av[n++] = NULL; 749 750 /* 751 * Run the command. 752 */ 753 RunningIoctl = 1; 754 if ((pid = fork()) == 0) { 755 if (VerboseOpt < 2) { 756 int fd = open("/dev/null", O_RDWR); 757 dup2(fd, 1); 758 close(fd); 759 } 760 execvp(av[0], av); 761 _exit(127); 762 } else if (pid < 0) { 763 res = 127; 764 } else { 765 int status; 766 767 while (waitpid(pid, &status, 0) != pid) 768 ; 769 res = WEXITSTATUS(status); 770 } 771 RunningIoctl = 0; 772 if (DidInterrupt) 773 _exit(1); 774 775 free(cmd); 776 free(av); 777 if (resp) 778 *resp = res; 779 } 780 781 782