2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.6 2008/10/07 22:28:41 thomas Exp $
37 * Clean up a specific HAMMER filesystem or all HAMMER filesystems.
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.
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
48 * snapshots 1d 60d (0d 0d for /tmp, /var/tmp, /usr/obj)
54 * All hammer commands create and maintain cycle files in the snapshots
65 static void do_cleanup(const char *path);
66 static void config_init(const char *path, struct hammer_ioc_config *config);
67 static void migrate_config(FILE *fp, struct hammer_ioc_config *config);
68 static void migrate_snapshots(int fd, const char *snapshots_path);
69 static void migrate_one_snapshot(int fd, const char *fpath,
70 struct hammer_ioc_snapshot *snapshot);
71 static int strtosecs(char *ptr);
72 static const char *dividing_slash(const char *path);
73 static int check_period(const char *snapshots_path, const char *cmd, int arg1,
75 static void save_period(const char *snapshots_path, const char *cmd,
77 static int check_softlinks(int fd, int new_config, const char *snapshots_path);
78 static void cleanup_softlinks(int fd, int new_config,
79 const char *snapshots_path, int arg2, char *arg3);
80 static int check_expired(const char *fpath, int arg2);
82 static int create_snapshot(int new_config, const char *path,
83 const char *snapshots_path);
84 static int cleanup_rebalance(const char *path, const char *snapshots_path,
86 static int cleanup_prune(const char *path, const char *snapshots_path,
87 int arg1, int arg2, int snapshots_disabled);
88 static int cleanup_reblock(const char *path, const char *snapshots_path,
90 static int cleanup_recopy(const char *path, const char *snapshots_path,
93 static void runcmd(int *resp, const char *ctl, ...);
97 struct didpfs *FirstPFS;
100 hammer_cmd_cleanup(char **av, int ac)
102 char *fstype, *fs, *path;
103 struct statfs *stfsbuf;
108 mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
110 for (i=0; i < mntsize; i++) {
112 * We will cleanup in the case fstype is hammer.
113 * If we have null-mounted PFS, we check the
114 * mount source. If it looks like a PFS, we
115 * proceed to cleanup also.
117 fstype = stfsbuf[i].f_fstypename;
118 fs = stfsbuf[i].f_mntfromname;
119 if ((strcmp(fstype, "hammer") == 0) ||
120 ((strcmp(fstype, "null") == 0) &&
121 (strstr(fs, "/@@0x") != NULL ||
122 strstr(fs, "/@@-1") != NULL))) {
123 path = stfsbuf[i].f_mntonname;
140 do_cleanup(const char *path)
142 struct hammer_ioc_pseudofs_rw pfs;
143 struct hammer_ioc_config config;
144 struct hammer_ioc_version version;
145 union hammer_ioc_mrecord_any mrec_tmp;
146 char *snapshots_path;
159 struct didpfs *didpfs;
160 int snapshots_disabled = 0;
161 int prune_warning = 0;
167 bzero(&pfs, sizeof(pfs));
168 bzero(&mrec_tmp, sizeof(mrec_tmp));
169 pfs.ondisk = &mrec_tmp.pfs.pfsd;
170 pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
173 printf("cleanup %-20s -", path);
174 fd = open(path, O_RDONLY);
176 printf(" unable to access directory: %s\n", strerror(errno));
179 if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
180 printf(" not a HAMMER filesystem: %s\n", strerror(errno));
184 if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
185 printf(" unrecognized HAMMER version\n");
189 bzero(&version, sizeof(version));
190 if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) {
191 printf(" HAMMER filesystem but couldn't retrieve version!\n");
196 bzero(&config, sizeof(config));
197 if (version.cur_version >= 3) {
198 if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 &&
199 config.head.error == 0) {
205 * Make sure we have not already handled this PFS. Several nullfs
206 * mounts might alias the same PFS.
208 for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
209 if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
210 printf(" PFS #%d already handled\n", pfs.pfs_id);
215 didpfs = malloc(sizeof(*didpfs));
216 didpfs->next = FirstPFS;
218 didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
221 * Figure out where the snapshot directory is.
223 if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
224 asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
225 } else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
226 printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
229 } else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
230 printf(" WARNING: must configure snapshot dir for PFS slave\n");
231 printf("\tWe suggest <fs>/var/slaves/<name> where "
232 "<fs> is the base HAMMER fs\n");
233 printf("\tcontaining the slave\n");
237 asprintf(&snapshots_path,
238 "%s%ssnapshots", path, dividing_slash(path));
242 * Create a snapshot directory if necessary, and a config file if
245 * If the filesystem is running version >= 3 migrate the config
248 if (stat(snapshots_path, &st) < 0) {
249 if (mkdir(snapshots_path, 0755) != 0) {
250 free(snapshots_path);
251 printf(" unable to create snapshot dir \"%s\": %s\n",
252 snapshots_path, strerror(errno));
257 asprintf(&config_path, "%s/config", snapshots_path);
258 fp = fopen(config_path, "r");
261 * Handle upgrades to hammer version 3, move the config
262 * file into meta-data.
264 * For the old config read the file into the config structure,
265 * we will parse it out of the config structure regardless.
267 if (version.cur_version >= 3) {
269 printf("(migrating) ");
271 migrate_config(fp, &config);
272 migrate_snapshots(fd, snapshots_path);
274 if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
275 printf(" cannot init meta-data config!\n");
280 } else if (new_config == 0) {
281 config_init(path, &config);
282 if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
283 printf(" cannot init meta-data config!\n");
291 config_init(path, &config);
292 fp = fopen(config_path, "w");
294 fwrite(config.config.text, 1,
295 strlen(config.config.text), fp);
299 migrate_config(fp, &config);
304 if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
305 if (errno == EWOULDBLOCK)
306 printf(" PFS #%d locked by other process\n", pfs.pfs_id);
308 printf(" can not lock %s: %s\n", config_path, strerror(errno));
313 printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
316 * Process the config file
318 cbase = config.config.text;
320 while ((cptr = strchr(cbase, '\n')) != NULL) {
321 bcopy(cbase, buf, cptr - cbase);
322 buf[cptr - cbase] = 0;
325 cmd = strtok(buf, WS);
329 if ((ptr = strtok(NULL, WS)) != NULL) {
330 arg1 = strtosecs(ptr);
331 if ((ptr = strtok(NULL, WS)) != NULL) {
332 arg2 = strtosecs(ptr);
333 arg3 = strtok(NULL, WS);
337 printf("%20s - ", cmd);
341 if (strcmp(cmd, "snapshots") == 0) {
344 check_softlinks(fd, new_config,
346 printf("only removing old snapshots\n");
348 cleanup_softlinks(fd, new_config,
352 printf("disabled\n");
353 snapshots_disabled = 1;
356 if (check_period(snapshots_path, cmd, arg1, &savet)) {
358 cleanup_softlinks(fd, new_config,
361 r = create_snapshot(new_config,
362 path, snapshots_path);
366 } else if (arg1 == 0) {
368 * The commands following this check can't handle
369 * a period of 0, so call the feature disabled and
370 * ignore the directive.
372 printf("disabled\n");
373 } else if (strcmp(cmd, "prune") == 0) {
374 if (check_period(snapshots_path, cmd, arg1, &savet)) {
376 printf("run - WARNING snapshot "
378 "but snapshots disabled\n");
382 r = cleanup_prune(path, snapshots_path,
383 arg1, arg2, snapshots_disabled);
387 } else if (strcmp(cmd, "rebalance") == 0) {
389 if (check_period(snapshots_path, cmd, arg1, &savet)) {
394 r = cleanup_rebalance(path, snapshots_path,
399 } else if (strcmp(cmd, "reblock") == 0) {
400 if (check_period(snapshots_path, cmd, arg1, &savet)) {
405 r = cleanup_reblock(path, snapshots_path,
410 } else if (strcmp(cmd, "recopy") == 0) {
411 if (check_period(snapshots_path, cmd, arg1, &savet)) {
416 r = cleanup_recopy(path, snapshots_path,
422 printf("unknown directive\n");
426 save_period(snapshots_path, cmd, savet);
430 * Add new rebalance feature if the config doesn't have it.
431 * (old style config only)
433 if (new_config == 0 && found_rebal == 0) {
434 if ((fp = fopen(config_path, "r+")) != NULL) {
436 fprintf(fp, "rebalance 1d 5m\n");
442 * Cleanup, and delay a little
449 * Initialize new config data (new or old style)
452 config_init(const char *path, struct hammer_ioc_config *config)
454 const char *snapshots;
456 if (strcmp(path, "/tmp") == 0 ||
457 strcmp(path, "/var/tmp") == 0 ||
458 strcmp(path, "/usr/obj") == 0) {
459 snapshots = "snapshots 0d 0d\n";
461 snapshots = "snapshots 1d 60d\n";
463 bzero(config->config.text, sizeof(config->config.text));
464 snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s",
470 "rebalance 1d 5m\n");
474 * Migrate configuration data from the old snapshots/config
475 * file to the new mata-data format.
478 migrate_config(FILE *fp, struct hammer_ioc_config *config)
482 n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp);
484 bzero(config->config.text + n, sizeof(config->config.text) - n);
488 * Migrate snapshot softlinks in the snapshots directory to the
489 * new meta-data format. The softlinks are left intact, but
490 * this way the pruning code won't lose track of them if you
491 * happen to blow away the snapshots directory.
494 migrate_snapshots(int fd, const char *snapshots_path)
496 struct hammer_ioc_snapshot snapshot;
502 bzero(&snapshot, sizeof(snapshot));
504 if ((dir = opendir(snapshots_path)) != NULL) {
505 while ((den = readdir(dir)) != NULL) {
506 if (den->d_name[0] == '.')
508 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
509 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) {
510 migrate_one_snapshot(fd, fpath, &snapshot);
516 migrate_one_snapshot(fd, NULL, &snapshot);
521 * Migrate a single snapshot. If fpath is NULL the ioctl is flushed,
522 * otherwise it is flushed when it fills up.
525 migrate_one_snapshot(int fd, const char *fpath,
526 struct hammer_ioc_snapshot *snapshot)
529 struct hammer_snapshot_data *snap;
543 tid = (hammer_tid_t)(int64_t)-1;
546 while (*ptr && *ptr != '-' && *ptr != '.')
550 r = sscanf(ptr, "%4d%2d%2d-%2d%2d",
551 &year, &month, &day, &hour, &minute);
554 bzero(&tm, sizeof(tm));
559 tm.tm_mon = month - 1;
560 tm.tm_year = year - 1900;
563 bzero(linkbuf, sizeof(linkbuf));
564 if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 &&
565 (ptr = strrchr(linkbuf, '@')) != NULL &&
566 ptr > linkbuf && ptr[-1] == '@') {
567 tid = strtoull(ptr + 1, NULL, 16);
569 if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) {
570 snap = &snapshot->snaps[snapshot->count];
571 bzero(snap, sizeof(*snap));
573 snap->ts = (u_int64_t)t * 1000000ULL;
574 snprintf(snap->label, sizeof(snap->label),
580 if ((fpath == NULL && snapshot->count) ||
581 snapshot->count == HAMMER_SNAPS_PER_IOCTL) {
582 printf(" (%d snapshots)", snapshot->count);
584 if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) {
585 printf(" Ioctl to migrate snapshots failed: %s\n",
587 } else if (snapshot->head.error == EALREADY) {
590 } else if (snapshot->head.error) {
591 printf(" Ioctl to delete snapshots failed: %s\n",
592 strerror(snapshot->head.error));
594 printf("index %d\n", snapshot->index);
597 snapshot->head.error = 0;
607 val = strtol(ptr, &ptr, 0);
621 errx(1, "illegal suffix converting %s\n", ptr);
628 dividing_slash(const char *path)
630 int len = strlen(path);
631 if (len && path[len-1] == '/')
638 * Check whether the desired period has elapsed since the last successful
639 * run. The run may take a while and cross a boundary so we remember the
640 * current time_t so we can save it later on.
642 * Periods in minutes, hours, or days are assumed to have been crossed
643 * if the local time crosses a minute, hour, or day boundary regardless
644 * of how close the last operation actually was.
647 check_period(const char *snapshots_path, const char *cmd, int arg1,
658 localtime_r(savep, &tp1);
661 * Retrieve the start time of the last successful operation.
663 asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
664 fp = fopen(check_path, "r");
668 if (fgets(buf, sizeof(buf), fp) == NULL) {
674 lastt = strtol(buf, NULL, 0);
675 localtime_r(&lastt, &tp2);
678 * Normalize the times. e.g. if asked to do something on a 1-day
679 * interval the operation will be performed as soon as the day
680 * turns over relative to the previous operation, even if the previous
681 * operation ran a few seconds ago just before midnight.
683 if (arg1 % 60 == 0) {
687 if (arg1 % (60 * 60) == 0) {
691 if (arg1 % (24 * 60 * 60) == 0) {
696 baset = mktime(&tp1);
697 lastt = mktime(&tp2);
700 printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
703 if ((int)(baset - lastt) >= arg1)
709 * Store the start time of the last successful operation.
712 save_period(const char *snapshots_path, const char *cmd,
719 asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
720 asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
721 fp = fopen(ncheck_path, "w");
723 fprintf(fp, "0x%08llx\n", (long long)savet);
725 rename(ncheck_path, ocheck_path);
728 fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
729 ncheck_path, strerror(errno));
734 * Simply count the number of softlinks in the snapshots dir
737 check_softlinks(int fd, int new_config, const char *snapshots_path)
746 * Old-style softlink-based snapshots
748 if ((dir = opendir(snapshots_path)) != NULL) {
749 while ((den = readdir(dir)) != NULL) {
750 if (den->d_name[0] == '.')
752 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
753 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
761 * New-style snapshots are stored as filesystem meta-data,
765 struct hammer_ioc_snapshot snapshot;
767 bzero(&snapshot, sizeof(snapshot));
769 if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
770 err(2, "hammer cleanup: check_softlink "
774 res += snapshot.count;
775 } while (snapshot.head.error == 0 && snapshot.count);
781 * Clean up expired softlinks in the snapshots dir
784 cleanup_softlinks(int fd, int new_config,
785 const char *snapshots_path, int arg2, char *arg3)
793 if (arg3 != NULL && strstr(arg3, "any") != NULL)
796 if ((dir = opendir(snapshots_path)) != NULL) {
797 while ((den = readdir(dir)) != NULL) {
798 if (den->d_name[0] == '.')
800 asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
801 if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
802 (anylink || strncmp(den->d_name, "snap-", 5) == 0)
804 if (check_expired(den->d_name, arg2)) {
806 printf(" expire %s\n",
818 * New-style snapshots are stored as filesystem meta-data,
822 struct hammer_ioc_snapshot snapshot;
823 struct hammer_ioc_snapshot dsnapshot;
824 struct hammer_snapshot_data *snap;
830 bzero(&snapshot, sizeof(snapshot));
831 bzero(&dsnapshot, sizeof(dsnapshot));
833 if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
834 err(2, "hammer cleanup: check_softlink "
838 for (i = 0; i < snapshot.count; ++i) {
839 snap = &snapshot.snaps[i];
840 t = time(NULL) - snap->ts / 1000000ULL;
842 dsnapshot.snaps[dsnapshot.count++] =
845 if ((int)t > arg2 && VerboseOpt) {
847 strftime(snapts, sizeof(snapts),
848 "%Y-%m-%d %H:%M:%S %Z", tp);
849 printf(" expire 0x%016jx %s %s\n",
850 (uintmax_t)snap->tid,
854 if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL) {
855 if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) {
856 printf(" Ioctl to delete snapshots failed: %s\n", strerror(errno));
857 } else if (dsnapshot.head.error) {
858 printf(" Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error));
861 dsnapshot.head.error = 0;
864 } while (snapshot.head.error == 0 && snapshot.count);
866 if (dsnapshot.count) {
867 if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) {
868 printf(" Ioctl to delete snapshots failed: %s\n", strerror(errno));
869 } else if (dsnapshot.head.error) {
870 printf(" Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error));
873 dsnapshot.head.error = 0;
879 * Take a softlink path in the form snap-yyyymmdd-hhmm and the
880 * expiration in seconds (arg2) and return non-zero if the softlink
884 check_expired(const char *fpath, int arg2)
895 while (*fpath && *fpath != '-' && *fpath != '.')
900 r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
901 &year, &month, &day, &hour, &minute);
904 bzero(&tm, sizeof(tm));
909 tm.tm_mon = month - 1;
910 tm.tm_year = year - 1900;
925 create_snapshot(int new_config, const char *path, const char *snapshots_path)
931 * New-style snapshot >= version 3.
933 runcmd(&r, "hammer snapfs %s cleanup", snapshots_path);
936 * Old-style snapshot prior to version 3
938 runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
944 cleanup_prune(const char *path __unused, const char *snapshots_path,
945 int arg1 __unused, int arg2, int snapshots_disabled)
948 * If snapshots have been disabled run prune-everything instead
951 if (snapshots_disabled && arg2) {
952 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
953 snapshots_path, arg2, path);
954 } else if (snapshots_disabled) {
955 runcmd(NULL, "hammer prune-everything %s", path);
957 runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
958 snapshots_path, arg2, snapshots_path);
960 runcmd(NULL, "hammer prune %s", snapshots_path);
966 cleanup_rebalance(const char *path, const char *snapshots_path,
967 int arg1 __unused, int arg2)
969 if (VerboseOpt == 0) {
975 "hammer -c %s/.rebalance.cycle -t %d rebalance %s",
976 snapshots_path, arg2, path);
977 if (VerboseOpt == 0) {
987 cleanup_reblock(const char *path, const char *snapshots_path,
988 int arg1 __unused, int arg2)
990 if (VerboseOpt == 0) {
996 * When reblocking the B-Tree always reblock everything in normal
1000 "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
1001 snapshots_path, arg2, path);
1002 if (VerboseOpt == 0) {
1008 * When reblocking the inodes always reblock everything in normal
1012 "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
1013 snapshots_path, arg2, path);
1014 if (VerboseOpt == 0) {
1020 * When reblocking the directories always reblock everything in normal
1024 "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
1025 snapshots_path, arg2, path);
1026 if (VerboseOpt == 0) {
1032 * Do not reblock all the data in normal mode.
1035 "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
1036 snapshots_path, arg2, path);
1037 if (VerboseOpt == 0)
1043 cleanup_recopy(const char *path, const char *snapshots_path,
1044 int arg1 __unused, int arg2)
1046 if (VerboseOpt == 0) {
1051 "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
1052 snapshots_path, arg2, path);
1053 if (VerboseOpt == 0) {
1058 "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
1059 snapshots_path, arg2, path);
1060 if (VerboseOpt == 0) {
1065 "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
1066 snapshots_path, arg2, path);
1067 if (VerboseOpt == 0) {
1072 "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
1073 snapshots_path, arg2, path);
1074 if (VerboseOpt == 0)
1081 runcmd(int *resp, const char *ctl, ...)
1093 * Generate the command
1096 vasprintf(&cmd, ctl, va);
1099 printf(" %s\n", cmd);
1102 * Break us down into arguments. We do not just use system() here
1103 * because it blocks SIGINT and friends.
1107 av = malloc(sizeof(char *) * nmax);
1109 for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
1110 if (n == nmax - 1) {
1112 av = realloc(av, sizeof(char *) * nmax);
1122 if ((pid = fork()) == 0) {
1123 if (VerboseOpt < 2) {
1124 int fd = open("/dev/null", O_RDWR);
1130 } else if (pid < 0) {
1135 while (waitpid(pid, &status, 0) != pid)
1137 res = WEXITSTATUS(status);