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 if (fp) { 471 fprintf(fp, "0x%08llx\n", (long long)savet); 472 if (fclose(fp) == 0) 473 rename(ncheck_path, ocheck_path); 474 remove(ncheck_path); 475 } else { 476 fprintf(stderr, "hammer: Unable to create period-file %s: %s\n", 477 ncheck_path, strerror(errno)); 478 } 479 } 480 481 /* 482 * Simply count the number of softlinks in the snapshots dir 483 */ 484 static int 485 check_softlinks(const char *snapshots_path) 486 { 487 struct dirent *den; 488 struct stat st; 489 DIR *dir; 490 char *fpath; 491 int res = 0; 492 493 if ((dir = opendir(snapshots_path)) != NULL) { 494 while ((den = readdir(dir)) != NULL) { 495 if (den->d_name[0] == '.') 496 continue; 497 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 498 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) 499 ++res; 500 free(fpath); 501 } 502 closedir(dir); 503 } 504 return(res); 505 } 506 507 /* 508 * Clean up expired softlinks in the snapshots dir 509 */ 510 static void 511 cleanup_softlinks(const char *path __unused, const char *snapshots_path, 512 int arg2, char *arg3) 513 { 514 struct dirent *den; 515 struct stat st; 516 DIR *dir; 517 char *fpath; 518 int anylink = 0; 519 520 if (arg3 != NULL && strstr(arg3, "any") != NULL) 521 anylink = 1; 522 523 if ((dir = opendir(snapshots_path)) != NULL) { 524 while ((den = readdir(dir)) != NULL) { 525 if (den->d_name[0] == '.') 526 continue; 527 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name); 528 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) && 529 (anylink || strncmp(den->d_name, "snap-", 5) == 0) 530 ) { 531 if (check_expired(den->d_name, arg2)) { 532 if (VerboseOpt) { 533 printf(" expire %s\n", 534 fpath); 535 } 536 remove(fpath); 537 } 538 } 539 free(fpath); 540 } 541 closedir(dir); 542 } 543 } 544 545 /* 546 * Take a softlink path in the form snap-yyyymmdd-hhmm and the 547 * expiration in seconds (arg2) and return non-zero if the softlink 548 * has expired. 549 */ 550 static int 551 check_expired(const char *fpath, int arg2) 552 { 553 struct tm tm; 554 time_t t; 555 int year; 556 int month; 557 int day = 0; 558 int hour = 0; 559 int minute = 0; 560 int r; 561 562 while (*fpath && *fpath != '-' && *fpath != '.') 563 ++fpath; 564 if (*fpath) 565 ++fpath; 566 567 r = sscanf(fpath, "%4d%2d%2d-%2d%2d", 568 &year, &month, &day, &hour, &minute); 569 570 if (r >= 3) { 571 bzero(&tm, sizeof(tm)); 572 tm.tm_isdst = -1; 573 tm.tm_min = minute; 574 tm.tm_hour = hour; 575 tm.tm_mday = day; 576 tm.tm_mon = month - 1; 577 tm.tm_year = year - 1900; 578 t = mktime(&tm); 579 if (t == (time_t)-1) 580 return(0); 581 t = time(NULL) - t; 582 if ((int)t > arg2) 583 return(1); 584 } 585 return(0); 586 } 587 588 /* 589 * Issue a snapshot. 590 */ 591 static int 592 cleanup_snapshots(const char *path __unused, const char *snapshots_path, 593 int arg1 __unused, int arg2 __unused) 594 { 595 int r; 596 597 runcmd(&r, "hammer snapshot %s %s", path, snapshots_path); 598 return(r); 599 } 600 601 static int 602 cleanup_prune(const char *path __unused, const char *snapshots_path, 603 int arg1 __unused, int arg2, int snapshots_disabled) 604 { 605 /* 606 * If snapshots have been disabled run prune-everything instead 607 * of prune. 608 */ 609 if (snapshots_disabled && arg2) { 610 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s", 611 snapshots_path, arg2, path); 612 } else if (snapshots_disabled) { 613 runcmd(NULL, "hammer prune-everything %s", path); 614 } else if (arg2) { 615 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s", 616 snapshots_path, arg2, snapshots_path); 617 } else { 618 runcmd(NULL, "hammer prune %s", snapshots_path); 619 } 620 return(0); 621 } 622 623 static int 624 cleanup_reblock(const char *path, const char *snapshots_path, 625 int arg1 __unused, int arg2) 626 { 627 if (VerboseOpt == 0) { 628 printf("."); 629 fflush(stdout); 630 } 631 632 /* 633 * When reblocking the B-Tree always reblock everything in normal 634 * mode. 635 */ 636 runcmd(NULL, 637 "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s", 638 snapshots_path, arg2, path); 639 if (VerboseOpt == 0) { 640 printf("."); 641 fflush(stdout); 642 } 643 644 /* 645 * When reblocking the inodes always reblock everything in normal 646 * mode. 647 */ 648 runcmd(NULL, 649 "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s", 650 snapshots_path, arg2, path); 651 if (VerboseOpt == 0) { 652 printf("."); 653 fflush(stdout); 654 } 655 656 /* 657 * When reblocking the directories always reblock everything in normal 658 * mode. 659 */ 660 runcmd(NULL, 661 "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s", 662 snapshots_path, arg2, path); 663 if (VerboseOpt == 0) { 664 printf("."); 665 fflush(stdout); 666 } 667 668 /* 669 * Do not reblock all the data in normal mode. 670 */ 671 runcmd(NULL, 672 "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95", 673 snapshots_path, arg2, path); 674 if (VerboseOpt == 0) 675 printf("\n"); 676 return(0); 677 } 678 679 static int 680 cleanup_recopy(const char *path, const char *snapshots_path, 681 int arg1 __unused, int arg2) 682 { 683 if (VerboseOpt == 0) { 684 printf("."); 685 fflush(stdout); 686 } 687 runcmd(NULL, 688 "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s", 689 snapshots_path, arg2, path); 690 if (VerboseOpt == 0) { 691 printf("."); 692 fflush(stdout); 693 } 694 runcmd(NULL, 695 "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s", 696 snapshots_path, arg2, path); 697 if (VerboseOpt == 0) { 698 printf("."); 699 fflush(stdout); 700 } 701 runcmd(NULL, 702 "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s", 703 snapshots_path, arg2, path); 704 if (VerboseOpt == 0) { 705 printf("."); 706 fflush(stdout); 707 } 708 runcmd(NULL, 709 "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s", 710 snapshots_path, arg2, path); 711 if (VerboseOpt == 0) 712 printf("\n"); 713 return(0); 714 } 715 716 static 717 void 718 runcmd(int *resp, const char *ctl, ...) 719 { 720 va_list va; 721 char *cmd; 722 char *arg; 723 char **av; 724 int n; 725 int nmax; 726 int res; 727 pid_t pid; 728 729 /* 730 * Generate the command 731 */ 732 va_start(va, ctl); 733 vasprintf(&cmd, ctl, va); 734 va_end(va); 735 if (VerboseOpt) 736 printf(" %s\n", cmd); 737 738 /* 739 * Break us down into arguments. We do not just use system() here 740 * because it blocks SIGINT and friends. 741 */ 742 n = 0; 743 nmax = 16; 744 av = malloc(sizeof(char *) * nmax); 745 746 for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) { 747 if (n == nmax - 1) { 748 nmax += 16; 749 av = realloc(av, sizeof(char *) * nmax); 750 } 751 av[n++] = arg; 752 } 753 av[n++] = NULL; 754 755 /* 756 * Run the command. 757 */ 758 RunningIoctl = 1; 759 if ((pid = fork()) == 0) { 760 if (VerboseOpt < 2) { 761 int fd = open("/dev/null", O_RDWR); 762 dup2(fd, 1); 763 close(fd); 764 } 765 execvp(av[0], av); 766 _exit(127); 767 } else if (pid < 0) { 768 res = 127; 769 } else { 770 int status; 771 772 while (waitpid(pid, &status, 0) != pid) 773 ; 774 res = WEXITSTATUS(status); 775 } 776 RunningIoctl = 0; 777 if (DidInterrupt) 778 _exit(1); 779 780 free(cmd); 781 free(av); 782 if (resp) 783 *resp = res; 784 } 785 786 787