xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision f13fea8b)
16a6e350fSMatthew Dillon /*
26a6e350fSMatthew Dillon  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
36a6e350fSMatthew Dillon  *
46a6e350fSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
56a6e350fSMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
66a6e350fSMatthew Dillon  *
76a6e350fSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
86a6e350fSMatthew Dillon  * modification, are permitted provided that the following conditions
96a6e350fSMatthew Dillon  * are met:
106a6e350fSMatthew Dillon  *
116a6e350fSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
126a6e350fSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
136a6e350fSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
146a6e350fSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
156a6e350fSMatthew Dillon  *    the documentation and/or other materials provided with the
166a6e350fSMatthew Dillon  *    distribution.
176a6e350fSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
186a6e350fSMatthew Dillon  *    contributors may be used to endorse or promote products derived
196a6e350fSMatthew Dillon  *    from this software without specific, prior written permission.
206a6e350fSMatthew Dillon  *
216a6e350fSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
226a6e350fSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
236a6e350fSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
246a6e350fSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
256a6e350fSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
266a6e350fSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
276a6e350fSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
286a6e350fSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
296a6e350fSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
306a6e350fSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
316a6e350fSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
326a6e350fSMatthew Dillon  * SUCH DAMAGE.
336a6e350fSMatthew Dillon  *
34bb8e52c0SThomas Nikolajsen  * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.6 2008/10/07 22:28:41 thomas Exp $
356a6e350fSMatthew Dillon  */
366a6e350fSMatthew Dillon /*
376a6e350fSMatthew Dillon  * Clean up a specific HAMMER filesystem or all HAMMER filesystems.
386a6e350fSMatthew Dillon  *
396a6e350fSMatthew Dillon  * Each filesystem is expected to have a <mount>/snapshots directory.
406a6e350fSMatthew Dillon  * No cleanup will be performed on any filesystem that does not.  If
416a6e350fSMatthew Dillon  * no filesystems are specified the 'df' program is run and any HAMMER
426a6e350fSMatthew Dillon  * or null-mounted hammer PFS's are extracted.
436a6e350fSMatthew Dillon  *
446a6e350fSMatthew Dillon  * The snapshots directory may contain a config file called 'config'.  If
456a6e350fSMatthew Dillon  * no config file is present one will be created with the following
466a6e350fSMatthew Dillon  * defaults:
476a6e350fSMatthew Dillon  *
485e435c92SMatthew Dillon  *	snapshots 1d 60d	(0d 0d for /tmp, /var/tmp, /usr/obj)
496a6e350fSMatthew Dillon  *	prune     1d 5m
50*f13fea8bSSascha Wildner  *	rebalance 1d 5m
516a6e350fSMatthew Dillon  *	reblock   1d 5m
526a6e350fSMatthew Dillon  *	recopy    30d 5m
536a6e350fSMatthew Dillon  *
546a6e350fSMatthew Dillon  * All hammer commands create and maintain cycle files in the snapshots
556a6e350fSMatthew Dillon  * directory.
566a6e350fSMatthew Dillon  */
576a6e350fSMatthew Dillon 
586a6e350fSMatthew Dillon #include "hammer.h"
596a6e350fSMatthew Dillon 
60c453712aSMatthew Dillon struct didpfs {
61c453712aSMatthew Dillon 	struct didpfs *next;
62c453712aSMatthew Dillon 	uuid_t		uuid;
63c453712aSMatthew Dillon };
64c453712aSMatthew Dillon 
656a6e350fSMatthew Dillon static void do_cleanup(const char *path);
666a6e350fSMatthew Dillon static int strtosecs(char *ptr);
676a6e350fSMatthew Dillon static const char *dividing_slash(const char *path);
686a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1,
696a6e350fSMatthew Dillon 			time_t *savep);
706a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd,
716a6e350fSMatthew Dillon 			time_t savet);
72c6c298a7SMatthew Dillon static int check_softlinks(const char *snapshots_path);
73ff1c9800SMatthew Dillon static void cleanup_softlinks(const char *path, const char *snapshots_path,
745e435c92SMatthew Dillon 			int arg2, char *arg3);
75ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2);
766a6e350fSMatthew Dillon 
77cbbb4f37SSimon Schubert static int create_snapshot(const char *path, const char *snapshots_path,
786a6e350fSMatthew Dillon 			      int arg1, int arg2);
790b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path,
800b8bd7daSMatthew Dillon 			      int arg1, int arg2);
816a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path,
82c6c298a7SMatthew Dillon 			      int arg1, int arg2, int snapshots_disabled);
836a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path,
846a6e350fSMatthew Dillon 			      int arg1, int arg2);
856a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path,
866a6e350fSMatthew Dillon 			      int arg1, int arg2);
876a6e350fSMatthew Dillon 
886a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...);
896a6e350fSMatthew Dillon 
906a6e350fSMatthew Dillon #define WS	" \t\r\n"
916a6e350fSMatthew Dillon 
92c453712aSMatthew Dillon struct didpfs *FirstPFS;
936a6e350fSMatthew Dillon 
946a6e350fSMatthew Dillon void
956a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
966a6e350fSMatthew Dillon {
97eac446c5SMatthew Dillon 	char *fstype, *fs, *path;
98eac446c5SMatthew Dillon 	struct statfs *stfsbuf;
99eac446c5SMatthew Dillon 	int mntsize, i;
1006a6e350fSMatthew Dillon 
1016a6e350fSMatthew Dillon 	tzset();
1026a6e350fSMatthew Dillon 	if (ac == 0) {
103eac446c5SMatthew Dillon 		mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
104eac446c5SMatthew Dillon 		if (mntsize > 0) {
105eac446c5SMatthew Dillon 			for (i=0; i < mntsize; i++) {
106eac446c5SMatthew Dillon 				/*
107eac446c5SMatthew Dillon 				 * We will cleanup in the case fstype is hammer.
108eac446c5SMatthew Dillon 				 * If we have null-mounted PFS, we check the
109eac446c5SMatthew Dillon 				 * mount source. If it looks like a PFS, we
110eac446c5SMatthew Dillon 				 * proceed to cleanup also.
111eac446c5SMatthew Dillon 				 */
112eac446c5SMatthew Dillon 				fstype = stfsbuf[i].f_fstypename;
113eac446c5SMatthew Dillon 				fs = stfsbuf[i].f_mntfromname;
114eac446c5SMatthew Dillon 				if ((strcmp(fstype, "hammer") == 0) ||
115eac446c5SMatthew Dillon 				    ((strcmp(fstype, "null") == 0) &&
116cb3c760cSMatthew Dillon 				     (strstr(fs, "/@@0x") != NULL ||
117cb3c760cSMatthew Dillon 				      strstr(fs, "/@@-1") != NULL))) {
118eac446c5SMatthew Dillon 					path = stfsbuf[i].f_mntonname;
1196a6e350fSMatthew Dillon 					do_cleanup(path);
1206a6e350fSMatthew Dillon 				}
121574066d3SMatthew Dillon 			}
122eac446c5SMatthew Dillon 		}
123eac446c5SMatthew Dillon 
1246a6e350fSMatthew Dillon 	} else {
1256a6e350fSMatthew Dillon 		while (ac) {
1266a6e350fSMatthew Dillon 			do_cleanup(*av);
1276a6e350fSMatthew Dillon 			--ac;
1286a6e350fSMatthew Dillon 			++av;
1296a6e350fSMatthew Dillon 		}
1306a6e350fSMatthew Dillon 	}
1316a6e350fSMatthew Dillon }
1326a6e350fSMatthew Dillon 
1336a6e350fSMatthew Dillon static
1346a6e350fSMatthew Dillon void
1356a6e350fSMatthew Dillon do_cleanup(const char *path)
1366a6e350fSMatthew Dillon {
1376a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1386a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
1396a6e350fSMatthew Dillon 	char *snapshots_path;
1406a6e350fSMatthew Dillon 	char *config_path;
1416a6e350fSMatthew Dillon 	struct stat st;
1426a6e350fSMatthew Dillon 	char *cmd;
1436a6e350fSMatthew Dillon 	char *ptr;
1446a6e350fSMatthew Dillon 	int arg1;
1456a6e350fSMatthew Dillon 	int arg2;
1465e435c92SMatthew Dillon 	char *arg3;
1476a6e350fSMatthew Dillon 	time_t savet;
1486a6e350fSMatthew Dillon 	char buf[256];
1496a6e350fSMatthew Dillon 	FILE *fp;
150c453712aSMatthew Dillon 	struct didpfs *didpfs;
151c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
152c6c298a7SMatthew Dillon 	int prune_warning = 0;
1536a6e350fSMatthew Dillon 	int fd;
1546a6e350fSMatthew Dillon 	int r;
1550b8bd7daSMatthew Dillon 	int found_rebal = 0;
1566a6e350fSMatthew Dillon 
1576a6e350fSMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1586a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1596a6e350fSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
1606a6e350fSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
1616a6e350fSMatthew Dillon 	pfs.pfs_id = -1;
1626a6e350fSMatthew Dillon 
1636a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1646a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1656a6e350fSMatthew Dillon 	if (fd < 0) {
1666a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1676a6e350fSMatthew Dillon 		return;
1686a6e350fSMatthew Dillon 	}
1696a6e350fSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1706a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
1716a6e350fSMatthew Dillon 		return;
1726a6e350fSMatthew Dillon 	}
1736a6e350fSMatthew Dillon 	close(fd);
1746a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1756a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
1766a6e350fSMatthew Dillon 		return;
1776a6e350fSMatthew Dillon 	}
1786a6e350fSMatthew Dillon 
1796a6e350fSMatthew Dillon 	/*
1806a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
1816a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
1826a6e350fSMatthew Dillon 	 */
183c453712aSMatthew Dillon 	for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
184c453712aSMatthew Dillon 		if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
185bb8e52c0SThomas Nikolajsen 			printf(" PFS #%d already handled\n", pfs.pfs_id);
1866a6e350fSMatthew Dillon 			return;
1876a6e350fSMatthew Dillon 		}
188c453712aSMatthew Dillon 	}
189c453712aSMatthew Dillon 	didpfs = malloc(sizeof(*didpfs));
190c453712aSMatthew Dillon 	didpfs->next = FirstPFS;
191c453712aSMatthew Dillon 	FirstPFS = didpfs;
192c453712aSMatthew Dillon 	didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
1936a6e350fSMatthew Dillon 
1946a6e350fSMatthew Dillon 	/*
195ff1c9800SMatthew Dillon 	 * Figure out where the snapshot directory is.
196ff1c9800SMatthew Dillon 	 */
197ff1c9800SMatthew Dillon 	if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
198ff1c9800SMatthew Dillon 		asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
199ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
200ff1c9800SMatthew Dillon 		printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
201ff1c9800SMatthew Dillon 		return;
202ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
203ff1c9800SMatthew Dillon 		printf(" WARNING: must configure snapshot dir for PFS slave\n");
204ff1c9800SMatthew Dillon 		printf("\tWe suggest <fs>/var/slaves/<name> where "
205ff1c9800SMatthew Dillon 		       "<fs> is the base HAMMER fs\n");
206bb8e52c0SThomas Nikolajsen 		printf("\tcontaining the slave\n");
207ff1c9800SMatthew Dillon 		return;
208ff1c9800SMatthew Dillon 	} else {
209ff1c9800SMatthew Dillon 		asprintf(&snapshots_path,
210ff1c9800SMatthew Dillon 			 "%s%ssnapshots", path, dividing_slash(path));
211ff1c9800SMatthew Dillon 	}
212ff1c9800SMatthew Dillon 
213ff1c9800SMatthew Dillon 	/*
2146a6e350fSMatthew Dillon 	 * Create a snapshot directory if necessary, and a config file if
2156a6e350fSMatthew Dillon 	 * necessary.
2166a6e350fSMatthew Dillon 	 */
2176a6e350fSMatthew Dillon 	if (stat(snapshots_path, &st) < 0) {
2186a6e350fSMatthew Dillon 		if (mkdir(snapshots_path, 0755) != 0) {
2196a6e350fSMatthew Dillon 			free(snapshots_path);
220ff1c9800SMatthew Dillon 			printf(" unable to create snapshot dir \"%s\": %s\n",
221ff1c9800SMatthew Dillon 				snapshots_path, strerror(errno));
2226a6e350fSMatthew Dillon 			return;
2236a6e350fSMatthew Dillon 		}
2246a6e350fSMatthew Dillon 	}
2256a6e350fSMatthew Dillon 	asprintf(&config_path, "%s/config", snapshots_path);
2266a6e350fSMatthew Dillon 	if ((fp = fopen(config_path, "r")) == NULL) {
2276a6e350fSMatthew Dillon 		fp = fopen(config_path, "w");
2286a6e350fSMatthew Dillon 		if (fp == NULL) {
2296a6e350fSMatthew Dillon 			printf(" cannot create %s: %s\n",
2306a6e350fSMatthew Dillon 				config_path, strerror(errno));
2316a6e350fSMatthew Dillon 			return;
2326a6e350fSMatthew Dillon 		}
2336a6e350fSMatthew Dillon 		if (strcmp(path, "/tmp") == 0 ||
2346a6e350fSMatthew Dillon 		    strcmp(path, "/var/tmp") == 0 ||
2356a6e350fSMatthew Dillon 		    strcmp(path, "/usr/obj") == 0) {
2365e435c92SMatthew Dillon 			fprintf(fp, "snapshots 0d 0d\n");
2376a6e350fSMatthew Dillon 		} else {
2386a6e350fSMatthew Dillon 			fprintf(fp, "snapshots 1d 60d\n");
2396a6e350fSMatthew Dillon 		}
2406a6e350fSMatthew Dillon 		fprintf(fp,
2416a6e350fSMatthew Dillon 			"prune     1d 5m\n"
2420b8bd7daSMatthew Dillon 			"rebalance 1d 5m\n"
2436a6e350fSMatthew Dillon 			"reblock   1d 5m\n"
2446a6e350fSMatthew Dillon 			"recopy    30d 10m\n");
2456a6e350fSMatthew Dillon 		fclose(fp);
2466a6e350fSMatthew Dillon 		fp = fopen(config_path, "r");
2476a6e350fSMatthew Dillon 	}
2486a6e350fSMatthew Dillon 	if (fp == NULL) {
2496a6e350fSMatthew Dillon 		printf(" cannot access %s: %s\n",
2506a6e350fSMatthew Dillon 		       config_path, strerror(errno));
2516a6e350fSMatthew Dillon 		return;
2526a6e350fSMatthew Dillon 	}
2536a6e350fSMatthew Dillon 
2545bd5f172SSimon Schubert 	if (flock(fileno(fp), LOCK_EX|LOCK_NB) == -1) {
2555bd5f172SSimon Schubert 		if (errno == EWOULDBLOCK)
2565bd5f172SSimon Schubert 			printf(" PFS #%d locked by other process\n", pfs.pfs_id);
2575bd5f172SSimon Schubert 		else
2585bd5f172SSimon Schubert 			printf(" can not lock %s: %s\n", config_path, strerror(errno));
2595bd5f172SSimon Schubert 		fclose(fp);
2605bd5f172SSimon Schubert 		return;
2615bd5f172SSimon Schubert 	}
2625bd5f172SSimon Schubert 
263ff1c9800SMatthew Dillon 	printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
2646a6e350fSMatthew Dillon 
2656a6e350fSMatthew Dillon 	/*
2666a6e350fSMatthew Dillon 	 * Process the config file
2676a6e350fSMatthew Dillon 	 */
2686a6e350fSMatthew Dillon 	while (fgets(buf, sizeof(buf), fp) != NULL) {
2696a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
2706a6e350fSMatthew Dillon 		arg1 = 0;
2716a6e350fSMatthew Dillon 		arg2 = 0;
2725e435c92SMatthew Dillon 		arg3 = NULL;
2736a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
2746a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
2755e435c92SMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL) {
2766a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
2775e435c92SMatthew Dillon 				arg3 = strtok(NULL, WS);
2785e435c92SMatthew Dillon 			}
2796a6e350fSMatthew Dillon 		}
2806a6e350fSMatthew Dillon 
2816a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
2826a6e350fSMatthew Dillon 		fflush(stdout);
2836a6e350fSMatthew Dillon 
2846a6e350fSMatthew Dillon 		r = 1;
2856a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
2865e435c92SMatthew Dillon 			if (arg1 == 0) {
2875e435c92SMatthew Dillon 				if (arg2 && check_softlinks(snapshots_path)) {
2885e435c92SMatthew Dillon 					printf("only removing old snapshots\n");
2895e435c92SMatthew Dillon 					prune_warning = 1;
2905e435c92SMatthew Dillon 					cleanup_softlinks(path, snapshots_path,
2915e435c92SMatthew Dillon 							  arg2, arg3);
2925e435c92SMatthew Dillon 				} else {
2935e435c92SMatthew Dillon 					printf("disabled\n");
2945e435c92SMatthew Dillon 					snapshots_disabled = 1;
2955e435c92SMatthew Dillon 				}
2965e435c92SMatthew Dillon 			} else
2976a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
2986a6e350fSMatthew Dillon 				printf("run\n");
2995e435c92SMatthew Dillon 				cleanup_softlinks(path, snapshots_path,
3005e435c92SMatthew Dillon 						  arg2, arg3);
301cbbb4f37SSimon Schubert 				r = create_snapshot(path, snapshots_path,
3026a6e350fSMatthew Dillon 						  arg1, arg2);
3036a6e350fSMatthew Dillon 			} else {
3046a6e350fSMatthew Dillon 				printf("skip\n");
3056a6e350fSMatthew Dillon 			}
3065e435c92SMatthew Dillon 		} else if (arg1 == 0) {
3075e435c92SMatthew Dillon 			/*
3085e435c92SMatthew Dillon 			 * The commands following this check can't handle
3095e435c92SMatthew Dillon 			 * a period of 0, so call the feature disabled and
3105e435c92SMatthew Dillon 			 * ignore the directive.
3115e435c92SMatthew Dillon 			 */
3125e435c92SMatthew Dillon 			printf("disabled\n");
3136a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
3146a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
3155e435c92SMatthew Dillon 				if (prune_warning) {
3165e435c92SMatthew Dillon 					printf("run - WARNING snapshot "
3175e435c92SMatthew Dillon 					       "softlinks present "
318226f3799SThomas Nikolajsen 					       "but snapshots disabled\n");
3195e435c92SMatthew Dillon 				} else {
320c6c298a7SMatthew Dillon 					printf("run\n");
3215e435c92SMatthew Dillon 				}
3226a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
323c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
3246a6e350fSMatthew Dillon 			} else {
3256a6e350fSMatthew Dillon 				printf("skip\n");
3266a6e350fSMatthew Dillon 			}
3270b8bd7daSMatthew Dillon 		} else if (strcmp(cmd, "rebalance") == 0) {
3280b8bd7daSMatthew Dillon 			found_rebal = 1;
3290b8bd7daSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
3300b8bd7daSMatthew Dillon 				printf("run");
3310b8bd7daSMatthew Dillon 				fflush(stdout);
3320b8bd7daSMatthew Dillon 				if (VerboseOpt)
3330b8bd7daSMatthew Dillon 					printf("\n");
3340b8bd7daSMatthew Dillon 				r = cleanup_rebalance(path, snapshots_path,
3350b8bd7daSMatthew Dillon 						arg1, arg2);
3360b8bd7daSMatthew Dillon 			} else {
3370b8bd7daSMatthew Dillon 				printf("skip\n");
3380b8bd7daSMatthew Dillon 			}
3396a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
3406a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
3416a6e350fSMatthew Dillon 				printf("run");
3426a6e350fSMatthew Dillon 				fflush(stdout);
3436a6e350fSMatthew Dillon 				if (VerboseOpt)
3446a6e350fSMatthew Dillon 					printf("\n");
3456a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
3466a6e350fSMatthew Dillon 						arg1, arg2);
3476a6e350fSMatthew Dillon 			} else {
3486a6e350fSMatthew Dillon 				printf("skip\n");
3496a6e350fSMatthew Dillon 			}
3506a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
3516a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
3526a6e350fSMatthew Dillon 				printf("run");
3536a6e350fSMatthew Dillon 				fflush(stdout);
3546a6e350fSMatthew Dillon 				if (VerboseOpt)
3556a6e350fSMatthew Dillon 					printf("\n");
3566a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
3576a6e350fSMatthew Dillon 					       arg1, arg2);
3586a6e350fSMatthew Dillon 			} else {
3596a6e350fSMatthew Dillon 				printf("skip\n");
3606a6e350fSMatthew Dillon 			}
3616a6e350fSMatthew Dillon 		} else {
3626a6e350fSMatthew Dillon 			printf("unknown directive\n");
3636a6e350fSMatthew Dillon 			r = 1;
3646a6e350fSMatthew Dillon 		}
3656a6e350fSMatthew Dillon 		if (r == 0)
3666a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
3676a6e350fSMatthew Dillon 	}
3686a6e350fSMatthew Dillon 	fclose(fp);
3690b8bd7daSMatthew Dillon 
3700b8bd7daSMatthew Dillon 	/*
3710b8bd7daSMatthew Dillon 	 * Add new rebalance feature if the config doesn't have it
3720b8bd7daSMatthew Dillon 	 */
3730b8bd7daSMatthew Dillon 	if (found_rebal == 0) {
3740b8bd7daSMatthew Dillon 		if ((fp = fopen(config_path, "r+")) != NULL) {
3750b8bd7daSMatthew Dillon 			fseek(fp, 0L, 2);
3760b8bd7daSMatthew Dillon 			fprintf(fp, "rebalance 1d 5m\n");
3770b8bd7daSMatthew Dillon 			fclose(fp);
3780b8bd7daSMatthew Dillon 		}
3790b8bd7daSMatthew Dillon 	}
3806a6e350fSMatthew Dillon 	usleep(1000);
3816a6e350fSMatthew Dillon }
3826a6e350fSMatthew Dillon 
3836a6e350fSMatthew Dillon static
3846a6e350fSMatthew Dillon int
3856a6e350fSMatthew Dillon strtosecs(char *ptr)
3866a6e350fSMatthew Dillon {
3876a6e350fSMatthew Dillon 	int val;
3886a6e350fSMatthew Dillon 
3896a6e350fSMatthew Dillon 	val = strtol(ptr, &ptr, 0);
3906a6e350fSMatthew Dillon 	switch(*ptr) {
3916a6e350fSMatthew Dillon 	case 'd':
3926a6e350fSMatthew Dillon 		val *= 24;
3936a6e350fSMatthew Dillon 		/* fall through */
3946a6e350fSMatthew Dillon 	case 'h':
3956a6e350fSMatthew Dillon 		val *= 60;
3966a6e350fSMatthew Dillon 		/* fall through */
3976a6e350fSMatthew Dillon 	case 'm':
3986a6e350fSMatthew Dillon 		val *= 60;
3996a6e350fSMatthew Dillon 		/* fall through */
4006a6e350fSMatthew Dillon 	case 's':
4016a6e350fSMatthew Dillon 		break;
4026a6e350fSMatthew Dillon 	default:
4036a6e350fSMatthew Dillon 		errx(1, "illegal suffix converting %s\n", ptr);
4046a6e350fSMatthew Dillon 		break;
4056a6e350fSMatthew Dillon 	}
4066a6e350fSMatthew Dillon 	return(val);
4076a6e350fSMatthew Dillon }
4086a6e350fSMatthew Dillon 
4096a6e350fSMatthew Dillon static const char *
4106a6e350fSMatthew Dillon dividing_slash(const char *path)
4116a6e350fSMatthew Dillon {
4126a6e350fSMatthew Dillon 	int len = strlen(path);
4136a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
4146a6e350fSMatthew Dillon 		return("");
4156a6e350fSMatthew Dillon 	else
4166a6e350fSMatthew Dillon 		return("/");
4176a6e350fSMatthew Dillon }
4186a6e350fSMatthew Dillon 
4196a6e350fSMatthew Dillon /*
4206a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
4216a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
4226a6e350fSMatthew Dillon  * current time_t so we can save it later on.
4236a6e350fSMatthew Dillon  *
4246a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
4256a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
4266a6e350fSMatthew Dillon  * of how close the last operation actually was.
4276a6e350fSMatthew Dillon  */
4286a6e350fSMatthew Dillon static int
4296a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
4306a6e350fSMatthew Dillon 	time_t *savep)
4316a6e350fSMatthew Dillon {
4326a6e350fSMatthew Dillon 	char *check_path;
4336a6e350fSMatthew Dillon 	struct tm tp1;
4346a6e350fSMatthew Dillon 	struct tm tp2;
4356a6e350fSMatthew Dillon 	FILE *fp;
4366a6e350fSMatthew Dillon 	time_t baset, lastt;
4376a6e350fSMatthew Dillon 	char buf[256];
4386a6e350fSMatthew Dillon 
4396a6e350fSMatthew Dillon 	time(savep);
4406a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
4416a6e350fSMatthew Dillon 
4426a6e350fSMatthew Dillon 	/*
4436a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
4446a6e350fSMatthew Dillon 	 */
4456a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
4466a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
4476a6e350fSMatthew Dillon 	free(check_path);
4486a6e350fSMatthew Dillon 	if (fp == NULL)
4496a6e350fSMatthew Dillon 		return(1);
4506a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
4516a6e350fSMatthew Dillon 		fclose(fp);
4526a6e350fSMatthew Dillon 		return(1);
4536a6e350fSMatthew Dillon 	}
4546a6e350fSMatthew Dillon 	fclose(fp);
4556a6e350fSMatthew Dillon 
4566a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
4576a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
4586a6e350fSMatthew Dillon 
4596a6e350fSMatthew Dillon 	/*
4606a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
4616a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
4626a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
4636a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
4646a6e350fSMatthew Dillon 	 */
4656a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
4666a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
4676a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
4686a6e350fSMatthew Dillon 	}
4696a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
4706a6e350fSMatthew Dillon 		tp1.tm_min = 0;
4716a6e350fSMatthew Dillon 		tp2.tm_min = 0;
4726a6e350fSMatthew Dillon 	}
4736a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
4746a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
4756a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
4766a6e350fSMatthew Dillon 	}
4776a6e350fSMatthew Dillon 
4786a6e350fSMatthew Dillon 	baset = mktime(&tp1);
4796a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
4806a6e350fSMatthew Dillon 
4816a6e350fSMatthew Dillon #if 0
4826a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
4836a6e350fSMatthew Dillon #endif
4846a6e350fSMatthew Dillon 
4856a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
4866a6e350fSMatthew Dillon 		return(1);
4876a6e350fSMatthew Dillon 	return(0);
4886a6e350fSMatthew Dillon }
4896a6e350fSMatthew Dillon 
4906a6e350fSMatthew Dillon /*
4916a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
4926a6e350fSMatthew Dillon  */
4936a6e350fSMatthew Dillon static void
4946a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
4956a6e350fSMatthew Dillon 			time_t savet)
4966a6e350fSMatthew Dillon {
4976a6e350fSMatthew Dillon 	char *ocheck_path;
4986a6e350fSMatthew Dillon 	char *ncheck_path;
4996a6e350fSMatthew Dillon 	FILE *fp;
5006a6e350fSMatthew Dillon 
5016a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
5026a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
5036a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
5043b9fbdeaSMatthew Dillon 	if (fp) {
5056a6e350fSMatthew Dillon 		fprintf(fp, "0x%08llx\n", (long long)savet);
5066a6e350fSMatthew Dillon 		if (fclose(fp) == 0)
5076a6e350fSMatthew Dillon 			rename(ncheck_path, ocheck_path);
5086a6e350fSMatthew Dillon 		remove(ncheck_path);
5093b9fbdeaSMatthew Dillon 	} else {
5103b9fbdeaSMatthew Dillon 		fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
5113b9fbdeaSMatthew Dillon 			ncheck_path, strerror(errno));
5123b9fbdeaSMatthew Dillon 	}
5136a6e350fSMatthew Dillon }
5146a6e350fSMatthew Dillon 
515ff1c9800SMatthew Dillon /*
516ff1c9800SMatthew Dillon  * Simply count the number of softlinks in the snapshots dir
517ff1c9800SMatthew Dillon  */
518c6c298a7SMatthew Dillon static int
519c6c298a7SMatthew Dillon check_softlinks(const char *snapshots_path)
520c6c298a7SMatthew Dillon {
521c6c298a7SMatthew Dillon 	struct dirent *den;
522c6c298a7SMatthew Dillon 	struct stat st;
523c6c298a7SMatthew Dillon 	DIR *dir;
524c6c298a7SMatthew Dillon 	char *fpath;
525c6c298a7SMatthew Dillon 	int res = 0;
526c6c298a7SMatthew Dillon 
527c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
528c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
529c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
530c6c298a7SMatthew Dillon 				continue;
531c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
532c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
533c6c298a7SMatthew Dillon 				++res;
534c6c298a7SMatthew Dillon 			free(fpath);
535c6c298a7SMatthew Dillon 		}
536c6c298a7SMatthew Dillon 		closedir(dir);
537c6c298a7SMatthew Dillon 	}
538c6c298a7SMatthew Dillon 	return(res);
539c6c298a7SMatthew Dillon }
540c6c298a7SMatthew Dillon 
5416a6e350fSMatthew Dillon /*
542ff1c9800SMatthew Dillon  * Clean up expired softlinks in the snapshots dir
543ff1c9800SMatthew Dillon  */
544ff1c9800SMatthew Dillon static void
5455e435c92SMatthew Dillon cleanup_softlinks(const char *path __unused, const char *snapshots_path,
5465e435c92SMatthew Dillon 		  int arg2, char *arg3)
547ff1c9800SMatthew Dillon {
548ff1c9800SMatthew Dillon 	struct dirent *den;
549ff1c9800SMatthew Dillon 	struct stat st;
550ff1c9800SMatthew Dillon 	DIR *dir;
551ff1c9800SMatthew Dillon 	char *fpath;
5525e435c92SMatthew Dillon 	int anylink = 0;
5535e435c92SMatthew Dillon 
55409e1b0d6SMatthew Dillon 	if (arg3 != NULL && strstr(arg3, "any") != NULL)
5555e435c92SMatthew Dillon 		anylink = 1;
556ff1c9800SMatthew Dillon 
557ff1c9800SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
558ff1c9800SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
559ff1c9800SMatthew Dillon 			if (den->d_name[0] == '.')
560ff1c9800SMatthew Dillon 				continue;
561ff1c9800SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
562ff1c9800SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
5635e435c92SMatthew Dillon 			    (anylink || strncmp(den->d_name, "snap-", 5) == 0)
5645e435c92SMatthew Dillon 			) {
565ff1c9800SMatthew Dillon 				if (check_expired(den->d_name, arg2)) {
566ff1c9800SMatthew Dillon 					if (VerboseOpt) {
567ff1c9800SMatthew Dillon 						printf("    expire %s\n",
568ff1c9800SMatthew Dillon 							fpath);
569ff1c9800SMatthew Dillon 					}
570ff1c9800SMatthew Dillon 					remove(fpath);
571ff1c9800SMatthew Dillon 				}
572ff1c9800SMatthew Dillon 			}
573ff1c9800SMatthew Dillon 			free(fpath);
574ff1c9800SMatthew Dillon 		}
575ff1c9800SMatthew Dillon 		closedir(dir);
576ff1c9800SMatthew Dillon 	}
577ff1c9800SMatthew Dillon }
578ff1c9800SMatthew Dillon 
579ff1c9800SMatthew Dillon /*
580ff1c9800SMatthew Dillon  * Take a softlink path in the form snap-yyyymmdd-hhmm and the
581ff1c9800SMatthew Dillon  * expiration in seconds (arg2) and return non-zero if the softlink
582ff1c9800SMatthew Dillon  * has expired.
583ff1c9800SMatthew Dillon  */
584ff1c9800SMatthew Dillon static int
585ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2)
586ff1c9800SMatthew Dillon {
587ff1c9800SMatthew Dillon 	struct tm tm;
588ff1c9800SMatthew Dillon 	time_t t;
589ff1c9800SMatthew Dillon 	int year;
590ff1c9800SMatthew Dillon 	int month;
5915e435c92SMatthew Dillon 	int day = 0;
5925e435c92SMatthew Dillon 	int hour = 0;
5935e435c92SMatthew Dillon 	int minute = 0;
594ff1c9800SMatthew Dillon 	int r;
595ff1c9800SMatthew Dillon 
5965e435c92SMatthew Dillon 	while (*fpath && *fpath != '-' && *fpath != '.')
5975e435c92SMatthew Dillon 		++fpath;
5985e435c92SMatthew Dillon 	if (*fpath)
5995e435c92SMatthew Dillon 		++fpath;
6005e435c92SMatthew Dillon 
6015e435c92SMatthew Dillon 	r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
602ff1c9800SMatthew Dillon 		   &year, &month, &day, &hour, &minute);
6035e435c92SMatthew Dillon 
6045e435c92SMatthew Dillon 	if (r >= 3) {
605ff1c9800SMatthew Dillon 		bzero(&tm, sizeof(tm));
606ff1c9800SMatthew Dillon 		tm.tm_isdst = -1;
607ff1c9800SMatthew Dillon 		tm.tm_min = minute;
608ff1c9800SMatthew Dillon 		tm.tm_hour = hour;
609ff1c9800SMatthew Dillon 		tm.tm_mday = day;
610ff1c9800SMatthew Dillon 		tm.tm_mon = month - 1;
611ff1c9800SMatthew Dillon 		tm.tm_year = year - 1900;
6125e435c92SMatthew Dillon 		t = mktime(&tm);
6135e435c92SMatthew Dillon 		if (t == (time_t)-1)
6145e435c92SMatthew Dillon 			return(0);
6155e435c92SMatthew Dillon 		t = time(NULL) - t;
616ff1c9800SMatthew Dillon 		if ((int)t > arg2)
617ff1c9800SMatthew Dillon 			return(1);
618ff1c9800SMatthew Dillon 	}
619ff1c9800SMatthew Dillon 	return(0);
620ff1c9800SMatthew Dillon }
621ff1c9800SMatthew Dillon 
622ff1c9800SMatthew Dillon /*
6236a6e350fSMatthew Dillon  * Issue a snapshot.
6246a6e350fSMatthew Dillon  */
6256a6e350fSMatthew Dillon static int
626cbbb4f37SSimon Schubert create_snapshot(const char *path __unused, const char *snapshots_path,
6276a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2 __unused)
6286a6e350fSMatthew Dillon {
6296a6e350fSMatthew Dillon 	int r;
6306a6e350fSMatthew Dillon 
631ff1c9800SMatthew Dillon 	runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
6326a6e350fSMatthew Dillon 	return(r);
6336a6e350fSMatthew Dillon }
6346a6e350fSMatthew Dillon 
6356a6e350fSMatthew Dillon static int
6366a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path,
637c6c298a7SMatthew Dillon 		  int arg1 __unused, int arg2, int snapshots_disabled)
6386a6e350fSMatthew Dillon {
639c6c298a7SMatthew Dillon 	/*
640c6c298a7SMatthew Dillon 	 * If snapshots have been disabled run prune-everything instead
641c6c298a7SMatthew Dillon 	 * of prune.
642c6c298a7SMatthew Dillon 	 */
643c6c298a7SMatthew Dillon 	if (snapshots_disabled && arg2) {
644c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
645c6c298a7SMatthew Dillon 			snapshots_path, arg2, path);
646c6c298a7SMatthew Dillon 	} else if (snapshots_disabled) {
647c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer prune-everything %s", path);
648c6c298a7SMatthew Dillon 	} else if (arg2) {
6496a6e350fSMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
6506a6e350fSMatthew Dillon 			snapshots_path, arg2, snapshots_path);
6516a6e350fSMatthew Dillon 	} else {
6526a6e350fSMatthew Dillon 		runcmd(NULL, "hammer prune %s", snapshots_path);
6536a6e350fSMatthew Dillon 	}
6546a6e350fSMatthew Dillon 	return(0);
6556a6e350fSMatthew Dillon }
6566a6e350fSMatthew Dillon 
6576a6e350fSMatthew Dillon static int
6580b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path,
6590b8bd7daSMatthew Dillon 		  int arg1 __unused, int arg2)
6600b8bd7daSMatthew Dillon {
6610b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
6620b8bd7daSMatthew Dillon 		printf(".");
6630b8bd7daSMatthew Dillon 		fflush(stdout);
6640b8bd7daSMatthew Dillon 	}
6650b8bd7daSMatthew Dillon 
6660b8bd7daSMatthew Dillon 	runcmd(NULL,
6670b8bd7daSMatthew Dillon 	       "hammer -c %s/.rebalance.cycle -t %d rebalance %s",
6680b8bd7daSMatthew Dillon 	       snapshots_path, arg2, path);
6690b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
6700b8bd7daSMatthew Dillon 		printf(".");
6710b8bd7daSMatthew Dillon 		fflush(stdout);
6720b8bd7daSMatthew Dillon 	}
6730b8bd7daSMatthew Dillon 	if (VerboseOpt == 0)
6740b8bd7daSMatthew Dillon 		printf("\n");
6750b8bd7daSMatthew Dillon 	return(0);
6760b8bd7daSMatthew Dillon }
6770b8bd7daSMatthew Dillon 
6780b8bd7daSMatthew Dillon static int
6796a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path,
6806a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
6816a6e350fSMatthew Dillon {
6826a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
6836a6e350fSMatthew Dillon 		printf(".");
6846a6e350fSMatthew Dillon 		fflush(stdout);
6856a6e350fSMatthew Dillon 	}
686797a0b63SMatthew Dillon 
687797a0b63SMatthew Dillon 	/*
688797a0b63SMatthew Dillon 	 * When reblocking the B-Tree always reblock everything in normal
689797a0b63SMatthew Dillon 	 * mode.
690797a0b63SMatthew Dillon 	 */
6916a6e350fSMatthew Dillon 	runcmd(NULL,
692797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
6936a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
6946a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
6956a6e350fSMatthew Dillon 		printf(".");
6966a6e350fSMatthew Dillon 		fflush(stdout);
6976a6e350fSMatthew Dillon 	}
698797a0b63SMatthew Dillon 
699797a0b63SMatthew Dillon 	/*
700797a0b63SMatthew Dillon 	 * When reblocking the inodes always reblock everything in normal
701797a0b63SMatthew Dillon 	 * mode.
702797a0b63SMatthew Dillon 	 */
7036a6e350fSMatthew Dillon 	runcmd(NULL,
704797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
7056a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
7066a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
7076a6e350fSMatthew Dillon 		printf(".");
7086a6e350fSMatthew Dillon 		fflush(stdout);
7096a6e350fSMatthew Dillon 	}
710797a0b63SMatthew Dillon 
711797a0b63SMatthew Dillon 	/*
712797a0b63SMatthew Dillon 	 * When reblocking the directories always reblock everything in normal
713797a0b63SMatthew Dillon 	 * mode.
714797a0b63SMatthew Dillon 	 */
715797a0b63SMatthew Dillon 	runcmd(NULL,
716797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
717797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
718797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
719797a0b63SMatthew Dillon 		printf(".");
720797a0b63SMatthew Dillon 		fflush(stdout);
721797a0b63SMatthew Dillon 	}
722797a0b63SMatthew Dillon 
723797a0b63SMatthew Dillon 	/*
724797a0b63SMatthew Dillon 	 * Do not reblock all the data in normal mode.
725797a0b63SMatthew Dillon 	 */
7266a6e350fSMatthew Dillon 	runcmd(NULL,
7276a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
7286a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
7296a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
7306a6e350fSMatthew Dillon 		printf("\n");
7316a6e350fSMatthew Dillon 	return(0);
7326a6e350fSMatthew Dillon }
7336a6e350fSMatthew Dillon 
7346a6e350fSMatthew Dillon static int
7356a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path,
7366a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
7376a6e350fSMatthew Dillon {
7386a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
7396a6e350fSMatthew Dillon 		printf(".");
7406a6e350fSMatthew Dillon 		fflush(stdout);
7416a6e350fSMatthew Dillon 	}
7426a6e350fSMatthew Dillon 	runcmd(NULL,
7436a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
7446a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
7456a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
7466a6e350fSMatthew Dillon 		printf(".");
7476a6e350fSMatthew Dillon 		fflush(stdout);
7486a6e350fSMatthew Dillon 	}
7496a6e350fSMatthew Dillon 	runcmd(NULL,
7506a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
7516a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
7526a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
7536a6e350fSMatthew Dillon 		printf(".");
7546a6e350fSMatthew Dillon 		fflush(stdout);
7556a6e350fSMatthew Dillon 	}
7566a6e350fSMatthew Dillon 	runcmd(NULL,
757797a0b63SMatthew Dillon 	       "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
758797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
759797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
760797a0b63SMatthew Dillon 		printf(".");
761797a0b63SMatthew Dillon 		fflush(stdout);
762797a0b63SMatthew Dillon 	}
763797a0b63SMatthew Dillon 	runcmd(NULL,
7646a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
7656a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
7666a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
7676a6e350fSMatthew Dillon 		printf("\n");
7686a6e350fSMatthew Dillon 	return(0);
7696a6e350fSMatthew Dillon }
7706a6e350fSMatthew Dillon 
7716a6e350fSMatthew Dillon static
7726a6e350fSMatthew Dillon void
7736a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...)
7746a6e350fSMatthew Dillon {
7756a6e350fSMatthew Dillon 	va_list va;
7766a6e350fSMatthew Dillon 	char *cmd;
7776a6e350fSMatthew Dillon 	char *arg;
7786a6e350fSMatthew Dillon 	char **av;
7796a6e350fSMatthew Dillon 	int n;
7806a6e350fSMatthew Dillon 	int nmax;
7816a6e350fSMatthew Dillon 	int res;
7826a6e350fSMatthew Dillon 	pid_t pid;
7836a6e350fSMatthew Dillon 
7846a6e350fSMatthew Dillon 	/*
7856a6e350fSMatthew Dillon 	 * Generate the command
7866a6e350fSMatthew Dillon 	 */
7876a6e350fSMatthew Dillon 	va_start(va, ctl);
7886a6e350fSMatthew Dillon 	vasprintf(&cmd, ctl, va);
7896a6e350fSMatthew Dillon 	va_end(va);
7906a6e350fSMatthew Dillon 	if (VerboseOpt)
7916a6e350fSMatthew Dillon 		printf("    %s\n", cmd);
7926a6e350fSMatthew Dillon 
7936a6e350fSMatthew Dillon 	/*
7946a6e350fSMatthew Dillon 	 * Break us down into arguments.  We do not just use system() here
7956a6e350fSMatthew Dillon 	 * because it blocks SIGINT and friends.
7966a6e350fSMatthew Dillon 	 */
7976a6e350fSMatthew Dillon 	n = 0;
7986a6e350fSMatthew Dillon 	nmax = 16;
7996a6e350fSMatthew Dillon 	av = malloc(sizeof(char *) * nmax);
8006a6e350fSMatthew Dillon 
8016a6e350fSMatthew Dillon 	for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
802c453712aSMatthew Dillon 		if (n == nmax - 1) {
8036a6e350fSMatthew Dillon 			nmax += 16;
8046a6e350fSMatthew Dillon 			av = realloc(av, sizeof(char *) * nmax);
8056a6e350fSMatthew Dillon 		}
8066a6e350fSMatthew Dillon 		av[n++] = arg;
8076a6e350fSMatthew Dillon 	}
808c453712aSMatthew Dillon 	av[n++] = NULL;
8096a6e350fSMatthew Dillon 
8106a6e350fSMatthew Dillon 	/*
8116a6e350fSMatthew Dillon 	 * Run the command.
8126a6e350fSMatthew Dillon 	 */
813445faa69SMatthew Dillon 	RunningIoctl = 1;
8146a6e350fSMatthew Dillon 	if ((pid = fork()) == 0) {
8156a6e350fSMatthew Dillon 		if (VerboseOpt < 2) {
8166a6e350fSMatthew Dillon 			int fd = open("/dev/null", O_RDWR);
8176a6e350fSMatthew Dillon 			dup2(fd, 1);
8186a6e350fSMatthew Dillon 			close(fd);
8196a6e350fSMatthew Dillon 		}
8206a6e350fSMatthew Dillon 		execvp(av[0], av);
8216a6e350fSMatthew Dillon 		_exit(127);
8226a6e350fSMatthew Dillon 	} else if (pid < 0) {
8236a6e350fSMatthew Dillon 		res = 127;
8246a6e350fSMatthew Dillon 	} else {
8256a6e350fSMatthew Dillon 		int status;
826445faa69SMatthew Dillon 
8276a6e350fSMatthew Dillon 		while (waitpid(pid, &status, 0) != pid)
8286a6e350fSMatthew Dillon 			;
8296a6e350fSMatthew Dillon 		res = WEXITSTATUS(status);
8306a6e350fSMatthew Dillon 	}
831445faa69SMatthew Dillon 	RunningIoctl = 0;
832445faa69SMatthew Dillon 	if (DidInterrupt)
833445faa69SMatthew Dillon 		_exit(1);
8346a6e350fSMatthew Dillon 
8356a6e350fSMatthew Dillon 	free(cmd);
8366a6e350fSMatthew Dillon 	free(av);
8376a6e350fSMatthew Dillon 	if (resp)
8386a6e350fSMatthew Dillon 		*resp = res;
8396a6e350fSMatthew Dillon }
840