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