xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision 743332ab)
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 /*
3716265794SThomas Nikolajsen  * Clean up specific HAMMER filesystems or all HAMMER filesystems.
386a6e350fSMatthew Dillon  *
39f6532f03SThomas Nikolajsen  * If no filesystems are specified any HAMMER- or null-mounted hammer PFS's
4016265794SThomas Nikolajsen  * are cleaned.
416a6e350fSMatthew Dillon  *
4216265794SThomas Nikolajsen  * Each HAMMER filesystem may contain a configuration file.  If no
4316265794SThomas Nikolajsen  * configuration file is present one will be created with the following
446a6e350fSMatthew Dillon  * defaults:
456a6e350fSMatthew Dillon  *
465e435c92SMatthew Dillon  *	snapshots 1d 60d	(0d 0d for /tmp, /var/tmp, /usr/obj)
476a6e350fSMatthew Dillon  *	prune     1d 5m
48f13fea8bSSascha Wildner  *	rebalance 1d 5m
496a6e350fSMatthew Dillon  *	reblock   1d 5m
50f6532f03SThomas Nikolajsen  *	recopy    30d 10m
516a6e350fSMatthew Dillon  *
526a6e350fSMatthew Dillon  * All hammer commands create and maintain cycle files in the snapshots
536a6e350fSMatthew Dillon  * directory.
5416265794SThomas Nikolajsen  *
5516265794SThomas Nikolajsen  * For HAMMER version 2- the configuration file is a named 'config' in
5616265794SThomas Nikolajsen  * the snapshots directory, which defaults to <pfs>/snapshots.
5716265794SThomas Nikolajsen  * For HAMMER version 3+ the configuration file is saved in filesystem
5816265794SThomas Nikolajsen  * meta-data. The snapshots directory defaults to /var/hammer/<pfs>
5916265794SThomas Nikolajsen  * (/var/hammer/root for root mount).
606a6e350fSMatthew Dillon  */
616a6e350fSMatthew Dillon 
626a6e350fSMatthew Dillon #include "hammer.h"
636a6e350fSMatthew Dillon 
64c453712aSMatthew Dillon struct didpfs {
65c453712aSMatthew Dillon 	struct didpfs *next;
66c453712aSMatthew Dillon 	uuid_t		uuid;
67c453712aSMatthew Dillon };
68c453712aSMatthew Dillon 
696a6e350fSMatthew Dillon static void do_cleanup(const char *path);
7083f2a3aaSMatthew Dillon static void config_init(const char *path, struct hammer_ioc_config *config);
7183f2a3aaSMatthew Dillon static void migrate_config(FILE *fp, struct hammer_ioc_config *config);
7283f2a3aaSMatthew Dillon static void migrate_snapshots(int fd, const char *snapshots_path);
7383f2a3aaSMatthew Dillon static void migrate_one_snapshot(int fd, const char *fpath,
7483f2a3aaSMatthew Dillon 			struct hammer_ioc_snapshot *snapshot);
756a6e350fSMatthew Dillon static int strtosecs(char *ptr);
766a6e350fSMatthew Dillon static const char *dividing_slash(const char *path);
776a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1,
786a6e350fSMatthew Dillon 			time_t *savep);
796a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd,
806a6e350fSMatthew Dillon 			time_t savet);
8183f2a3aaSMatthew Dillon static int check_softlinks(int fd, int new_config, const char *snapshots_path);
8283f2a3aaSMatthew Dillon static void cleanup_softlinks(int fd, int new_config,
8383f2a3aaSMatthew Dillon 			const char *snapshots_path, int arg2, char *arg3);
84ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2);
856a6e350fSMatthew Dillon 
86b5ec5ad4SMatthew Dillon static int create_snapshot(const char *path, const char *snapshots_path);
870b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path,
880b8bd7daSMatthew Dillon 			int arg1, int arg2);
896a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path,
90c6c298a7SMatthew Dillon 			int arg1, int arg2, int snapshots_disabled);
916a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path,
926a6e350fSMatthew Dillon 			int arg1, int arg2);
936a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path,
946a6e350fSMatthew Dillon 			int arg1, int arg2);
956a6e350fSMatthew Dillon 
966a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...);
976a6e350fSMatthew Dillon 
98b5ec5ad4SMatthew Dillon /*
99b5ec5ad4SMatthew Dillon  * WARNING: Do not make the SNAPSHOTS_BASE "/var/snapshots" because
100b5ec5ad4SMatthew Dillon  * it will interfere with the older HAMMER VERS < 3 snapshots directory
101b5ec5ad4SMatthew Dillon  * for the /var PFS.
102b5ec5ad4SMatthew Dillon  */
103b5ec5ad4SMatthew Dillon #define SNAPSHOTS_BASE	"/var/hammer"	/* HAMMER VERS >= 3 */
1046a6e350fSMatthew Dillon #define WS	" \t\r\n"
1056a6e350fSMatthew Dillon 
106c453712aSMatthew Dillon struct didpfs *FirstPFS;
1076a6e350fSMatthew Dillon 
1086a6e350fSMatthew Dillon void
1096a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
1106a6e350fSMatthew Dillon {
111eac446c5SMatthew Dillon 	char *fstype, *fs, *path;
112eac446c5SMatthew Dillon 	struct statfs *stfsbuf;
113eac446c5SMatthew Dillon 	int mntsize, i;
1146a6e350fSMatthew Dillon 
1156a6e350fSMatthew Dillon 	tzset();
1166a6e350fSMatthew Dillon 	if (ac == 0) {
117eac446c5SMatthew Dillon 		mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
118eac446c5SMatthew Dillon 		if (mntsize > 0) {
119eac446c5SMatthew Dillon 			for (i=0; i < mntsize; i++) {
120eac446c5SMatthew Dillon 				/*
121eac446c5SMatthew Dillon 				 * We will cleanup in the case fstype is hammer.
122eac446c5SMatthew Dillon 				 * If we have null-mounted PFS, we check the
123eac446c5SMatthew Dillon 				 * mount source. If it looks like a PFS, we
124eac446c5SMatthew Dillon 				 * proceed to cleanup also.
125eac446c5SMatthew Dillon 				 */
126eac446c5SMatthew Dillon 				fstype = stfsbuf[i].f_fstypename;
127eac446c5SMatthew Dillon 				fs = stfsbuf[i].f_mntfromname;
128eac446c5SMatthew Dillon 				if ((strcmp(fstype, "hammer") == 0) ||
129eac446c5SMatthew Dillon 				    ((strcmp(fstype, "null") == 0) &&
130cb3c760cSMatthew Dillon 				     (strstr(fs, "/@@0x") != NULL ||
131cb3c760cSMatthew Dillon 				      strstr(fs, "/@@-1") != NULL))) {
132eac446c5SMatthew Dillon 					path = stfsbuf[i].f_mntonname;
1336a6e350fSMatthew Dillon 					do_cleanup(path);
1346a6e350fSMatthew Dillon 				}
135574066d3SMatthew Dillon 			}
136eac446c5SMatthew Dillon 		}
137eac446c5SMatthew Dillon 
1386a6e350fSMatthew Dillon 	} else {
1396a6e350fSMatthew Dillon 		while (ac) {
1406a6e350fSMatthew Dillon 			do_cleanup(*av);
1416a6e350fSMatthew Dillon 			--ac;
1426a6e350fSMatthew Dillon 			++av;
1436a6e350fSMatthew Dillon 		}
1446a6e350fSMatthew Dillon 	}
1456a6e350fSMatthew Dillon }
1466a6e350fSMatthew Dillon 
1476a6e350fSMatthew Dillon static
1486a6e350fSMatthew Dillon void
1496a6e350fSMatthew Dillon do_cleanup(const char *path)
1506a6e350fSMatthew Dillon {
1516a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
15283f2a3aaSMatthew Dillon 	struct hammer_ioc_config config;
15383f2a3aaSMatthew Dillon 	struct hammer_ioc_version version;
1546a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
155*82893617SThomas Nikolajsen 	char *snapshots_path = NULL;
1566a6e350fSMatthew Dillon 	char *config_path;
1576a6e350fSMatthew Dillon 	struct stat st;
1586a6e350fSMatthew Dillon 	char *cmd;
1596a6e350fSMatthew Dillon 	char *ptr;
1606a6e350fSMatthew Dillon 	int arg1;
1616a6e350fSMatthew Dillon 	int arg2;
1625e435c92SMatthew Dillon 	char *arg3;
1636a6e350fSMatthew Dillon 	time_t savet;
1646a6e350fSMatthew Dillon 	char buf[256];
16583f2a3aaSMatthew Dillon 	char *cbase;
16683f2a3aaSMatthew Dillon 	char *cptr;
167*82893617SThomas Nikolajsen 	FILE *fp = NULL;
168c453712aSMatthew Dillon 	struct didpfs *didpfs;
169c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
170c6c298a7SMatthew Dillon 	int prune_warning = 0;
17183f2a3aaSMatthew Dillon 	int new_config = 0;
172b5ec5ad4SMatthew Dillon 	int snapshots_from_pfs = 0;
1736a6e350fSMatthew Dillon 	int fd;
1746a6e350fSMatthew Dillon 	int r;
1750b8bd7daSMatthew Dillon 	int found_rebal = 0;
1766a6e350fSMatthew Dillon 
1776a6e350fSMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1786a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1796a6e350fSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
1806a6e350fSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
1816a6e350fSMatthew Dillon 	pfs.pfs_id = -1;
1826a6e350fSMatthew Dillon 
1836a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1846a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1856a6e350fSMatthew Dillon 	if (fd < 0) {
1866a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1876a6e350fSMatthew Dillon 		return;
1886a6e350fSMatthew Dillon 	}
18983f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
1906a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
19183f2a3aaSMatthew Dillon 		close(fd);
1926a6e350fSMatthew Dillon 		return;
1936a6e350fSMatthew Dillon 	}
1946a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1956a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
19683f2a3aaSMatthew Dillon 		close(fd);
1976a6e350fSMatthew Dillon 		return;
1986a6e350fSMatthew Dillon 	}
19983f2a3aaSMatthew Dillon 	bzero(&version, sizeof(version));
20083f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) {
20183f2a3aaSMatthew Dillon 		printf(" HAMMER filesystem but couldn't retrieve version!\n");
20283f2a3aaSMatthew Dillon 		close(fd);
20383f2a3aaSMatthew Dillon 		return;
20483f2a3aaSMatthew Dillon 	}
20583f2a3aaSMatthew Dillon 
20683f2a3aaSMatthew Dillon 	bzero(&config, sizeof(config));
20783f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
20883f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 &&
20983f2a3aaSMatthew Dillon 		    config.head.error == 0) {
21083f2a3aaSMatthew Dillon 			new_config = 1;
21183f2a3aaSMatthew Dillon 		}
21283f2a3aaSMatthew Dillon 	}
2136a6e350fSMatthew Dillon 
2146a6e350fSMatthew Dillon 	/*
2156a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
2166a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
2176a6e350fSMatthew Dillon 	 */
218c453712aSMatthew Dillon 	for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
219c453712aSMatthew Dillon 		if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
220bb8e52c0SThomas Nikolajsen 			printf(" PFS #%d already handled\n", pfs.pfs_id);
22183f2a3aaSMatthew Dillon 			close(fd);
2226a6e350fSMatthew Dillon 			return;
2236a6e350fSMatthew Dillon 		}
224c453712aSMatthew Dillon 	}
225c453712aSMatthew Dillon 	didpfs = malloc(sizeof(*didpfs));
226c453712aSMatthew Dillon 	didpfs->next = FirstPFS;
227c453712aSMatthew Dillon 	FirstPFS = didpfs;
228c453712aSMatthew Dillon 	didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
2296a6e350fSMatthew Dillon 
2306a6e350fSMatthew Dillon 	/*
231b5ec5ad4SMatthew Dillon 	 * Calculate the old snapshots directory for HAMMER VERSION < 3
232b5ec5ad4SMatthew Dillon 	 *
233b5ec5ad4SMatthew Dillon 	 * If the directory is explicitly specified in the PFS config
234b5ec5ad4SMatthew Dillon 	 * we flag it and will not migrate it later.
235ff1c9800SMatthew Dillon 	 */
236ff1c9800SMatthew Dillon 	if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
237ff1c9800SMatthew Dillon 		asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
238b5ec5ad4SMatthew Dillon 		snapshots_from_pfs = 1;
239ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
240ff1c9800SMatthew Dillon 		printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
24183f2a3aaSMatthew Dillon 		close(fd);
242ff1c9800SMatthew Dillon 		return;
243ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
244*82893617SThomas Nikolajsen 		if (version.cur_version < 3) {
245ff1c9800SMatthew Dillon 			printf(" WARNING: must configure snapshot dir for PFS slave\n");
246ff1c9800SMatthew Dillon 			printf("\tWe suggest <fs>/var/slaves/<name> where "
247ff1c9800SMatthew Dillon 			       "<fs> is the base HAMMER fs\n");
248bb8e52c0SThomas Nikolajsen 			printf("\tcontaining the slave\n");
24983f2a3aaSMatthew Dillon 			close(fd);
250ff1c9800SMatthew Dillon 			return;
251*82893617SThomas Nikolajsen 		}
252ff1c9800SMatthew Dillon 	} else {
253ff1c9800SMatthew Dillon 		asprintf(&snapshots_path,
254ff1c9800SMatthew Dillon 			 "%s%ssnapshots", path, dividing_slash(path));
255ff1c9800SMatthew Dillon 	}
256ff1c9800SMatthew Dillon 
257ff1c9800SMatthew Dillon 	/*
258b5ec5ad4SMatthew Dillon 	 * Check for old-style config file
2596a6e350fSMatthew Dillon 	 */
260*82893617SThomas Nikolajsen 	if (snapshots_path) {
2616a6e350fSMatthew Dillon 		asprintf(&config_path, "%s/config", snapshots_path);
2626a6e350fSMatthew Dillon 		fp = fopen(config_path, "r");
263*82893617SThomas Nikolajsen 	}
26483f2a3aaSMatthew Dillon 
26583f2a3aaSMatthew Dillon 	/*
26683f2a3aaSMatthew Dillon 	 * Handle upgrades to hammer version 3, move the config
26783f2a3aaSMatthew Dillon 	 * file into meta-data.
26883f2a3aaSMatthew Dillon 	 *
26983f2a3aaSMatthew Dillon 	 * For the old config read the file into the config structure,
27083f2a3aaSMatthew Dillon 	 * we will parse it out of the config structure regardless.
27183f2a3aaSMatthew Dillon 	 */
27283f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
27383f2a3aaSMatthew Dillon 		if (fp) {
27483f2a3aaSMatthew Dillon 			printf("(migrating) ");
27583f2a3aaSMatthew Dillon 			fflush(stdout);
27683f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
27783f2a3aaSMatthew Dillon 			migrate_snapshots(fd, snapshots_path);
27883f2a3aaSMatthew Dillon 			fclose(fp);
27983f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
28083f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
28183f2a3aaSMatthew Dillon 				close(fd);
2826a6e350fSMatthew Dillon 				return;
2836a6e350fSMatthew Dillon 			}
28483f2a3aaSMatthew Dillon 			remove(config_path);
28583f2a3aaSMatthew Dillon 		} else if (new_config == 0) {
28683f2a3aaSMatthew Dillon 			config_init(path, &config);
28783f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
28883f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
28983f2a3aaSMatthew Dillon 				close(fd);
29083f2a3aaSMatthew Dillon 				return;
29183f2a3aaSMatthew Dillon 			}
29283f2a3aaSMatthew Dillon 		}
29383f2a3aaSMatthew Dillon 		new_config = 1;
29483f2a3aaSMatthew Dillon 	} else {
295b5ec5ad4SMatthew Dillon 		/*
296b5ec5ad4SMatthew Dillon 		 * Create missing snapshots directory for HAMMER VERSION < 3
297b5ec5ad4SMatthew Dillon 		 */
298b5ec5ad4SMatthew Dillon 		if (stat(snapshots_path, &st) < 0) {
299b5ec5ad4SMatthew Dillon 			if (mkdir(snapshots_path, 0755) != 0) {
300b5ec5ad4SMatthew Dillon 				free(snapshots_path);
301b5ec5ad4SMatthew Dillon 				printf(" unable to create snapshot dir \"%s\": %s\n",
302b5ec5ad4SMatthew Dillon 					snapshots_path, strerror(errno));
303b5ec5ad4SMatthew Dillon 				close(fd);
304b5ec5ad4SMatthew Dillon 				return;
305b5ec5ad4SMatthew Dillon 			}
306b5ec5ad4SMatthew Dillon 		}
307b5ec5ad4SMatthew Dillon 
308b5ec5ad4SMatthew Dillon 		/*
309b5ec5ad4SMatthew Dillon 		 *  Create missing config file for HAMMER VERSION < 3
310b5ec5ad4SMatthew Dillon 		 */
31183f2a3aaSMatthew Dillon 		if (fp == NULL) {
31283f2a3aaSMatthew Dillon 			config_init(path, &config);
31383f2a3aaSMatthew Dillon 			fp = fopen(config_path, "w");
31483f2a3aaSMatthew Dillon 			if (fp) {
31583f2a3aaSMatthew Dillon 				fwrite(config.config.text, 1,
31683f2a3aaSMatthew Dillon 					strlen(config.config.text), fp);
31783f2a3aaSMatthew Dillon 				fclose(fp);
31883f2a3aaSMatthew Dillon 			}
31983f2a3aaSMatthew Dillon 		} else {
32083f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
32183f2a3aaSMatthew Dillon 			fclose(fp);
32283f2a3aaSMatthew Dillon 		}
32383f2a3aaSMatthew Dillon 	}
3246a6e350fSMatthew Dillon 
325b5ec5ad4SMatthew Dillon 	/*
326b5ec5ad4SMatthew Dillon 	 * If snapshots_from_pfs is not set we calculate the new snapshots
327b5ec5ad4SMatthew Dillon 	 * directory default (in /var) for HAMMER VERSION >= 3 and migrate
328b5ec5ad4SMatthew Dillon 	 * the old snapshots directory over.
329b5ec5ad4SMatthew Dillon 	 *
330b5ec5ad4SMatthew Dillon 	 * People who have set an explicit snapshots directory will have
331b5ec5ad4SMatthew Dillon 	 * to migrate the data manually into /var/hammer, or not bother at
332b5ec5ad4SMatthew Dillon 	 * all.  People running slaves may wish to migrate it and then
333b5ec5ad4SMatthew Dillon 	 * clear the snapshots specification in the PFS config for the
334b5ec5ad4SMatthew Dillon 	 * slave.
335b5ec5ad4SMatthew Dillon 	 */
336b5ec5ad4SMatthew Dillon 	if (new_config && snapshots_from_pfs == 0) {
337b5ec5ad4SMatthew Dillon 		char *npath;
338b5ec5ad4SMatthew Dillon 
339b5ec5ad4SMatthew Dillon 		assert(path[0] == '/');
340b5ec5ad4SMatthew Dillon 		if (strcmp(path, "/") == 0)
341b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/root", SNAPSHOTS_BASE);
342b5ec5ad4SMatthew Dillon 		else
343b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1);
344*82893617SThomas Nikolajsen 		if (snapshots_path) {
345b5ec5ad4SMatthew Dillon 			if (stat(npath, &st) < 0 && errno == ENOENT) {
346b5ec5ad4SMatthew Dillon 				if (stat(snapshots_path, &st) < 0 && errno == ENOENT) {
347b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Creating snapshots\n"
348b5ec5ad4SMatthew Dillon 					       "\tCreating snapshots in %s\n",
349b5ec5ad4SMatthew Dillon 					       npath);
350b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
351b5ec5ad4SMatthew Dillon 				} else {
352b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Moving snapshots\n"
353b5ec5ad4SMatthew Dillon 					       "\tMoving snapshots from %s to %s\n",
354b5ec5ad4SMatthew Dillon 					       snapshots_path, npath);
355b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
356b5ec5ad4SMatthew Dillon 					runcmd(&r, "cpdup %s %s", snapshots_path, npath);
357b5ec5ad4SMatthew Dillon 					if (r != 0) {
358b5ec5ad4SMatthew Dillon 				    printf("Unable to move snapshots directory!\n");
359b5ec5ad4SMatthew Dillon 				    printf("Please fix this critical error.\n");
360b5ec5ad4SMatthew Dillon 				    printf("Aborting cleanup of %s\n", path);
361b5ec5ad4SMatthew Dillon 						close(fd);
362b5ec5ad4SMatthew Dillon 						return;
363b5ec5ad4SMatthew Dillon 					}
364b5ec5ad4SMatthew Dillon 					runcmd(&r, "rm -rf %s", snapshots_path);
365b5ec5ad4SMatthew Dillon 				}
366b5ec5ad4SMatthew Dillon 			}
367b5ec5ad4SMatthew Dillon 			free(snapshots_path);
368*82893617SThomas Nikolajsen 		} else if (stat(npath, &st) < 0 && errno == ENOENT) {
369*82893617SThomas Nikolajsen 			runcmd(&r, "mkdir -p %s", npath);
370*82893617SThomas Nikolajsen 		}
371b5ec5ad4SMatthew Dillon 		snapshots_path = npath;
372b5ec5ad4SMatthew Dillon 	}
373b5ec5ad4SMatthew Dillon 
374b5ec5ad4SMatthew Dillon 	/*
375b5ec5ad4SMatthew Dillon 	 * Lock the PFS.  fd is the base directory of the mounted PFS.
376b5ec5ad4SMatthew Dillon 	 */
37783f2a3aaSMatthew Dillon 	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
3785bd5f172SSimon Schubert 		if (errno == EWOULDBLOCK)
3795bd5f172SSimon Schubert 			printf(" PFS #%d locked by other process\n", pfs.pfs_id);
3805bd5f172SSimon Schubert 		else
3815bd5f172SSimon Schubert 			printf(" can not lock %s: %s\n", config_path, strerror(errno));
38283f2a3aaSMatthew Dillon 		close(fd);
3835bd5f172SSimon Schubert 		return;
3845bd5f172SSimon Schubert 	}
3855bd5f172SSimon Schubert 
386ff1c9800SMatthew Dillon 	printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
3876a6e350fSMatthew Dillon 
3886a6e350fSMatthew Dillon 	/*
3896a6e350fSMatthew Dillon 	 * Process the config file
3906a6e350fSMatthew Dillon 	 */
39183f2a3aaSMatthew Dillon 	cbase = config.config.text;
39283f2a3aaSMatthew Dillon 
39383f2a3aaSMatthew Dillon 	while ((cptr = strchr(cbase, '\n')) != NULL) {
39483f2a3aaSMatthew Dillon 		bcopy(cbase, buf, cptr - cbase);
39583f2a3aaSMatthew Dillon 		buf[cptr - cbase] = 0;
39683f2a3aaSMatthew Dillon 		cbase = cptr + 1;
39783f2a3aaSMatthew Dillon 
3986a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
399743332abSMatthew Dillon 		if (cmd == NULL || cmd[0] == '#')
400743332abSMatthew Dillon 			continue;
401743332abSMatthew Dillon 
4026a6e350fSMatthew Dillon 		arg1 = 0;
4036a6e350fSMatthew Dillon 		arg2 = 0;
4045e435c92SMatthew Dillon 		arg3 = NULL;
4056a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
4066a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
4075e435c92SMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL) {
4086a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
4095e435c92SMatthew Dillon 				arg3 = strtok(NULL, WS);
4105e435c92SMatthew Dillon 			}
4116a6e350fSMatthew Dillon 		}
4126a6e350fSMatthew Dillon 
4136a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
4146a6e350fSMatthew Dillon 		fflush(stdout);
4156a6e350fSMatthew Dillon 
4166a6e350fSMatthew Dillon 		r = 1;
4176a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
4185e435c92SMatthew Dillon 			if (arg1 == 0) {
41983f2a3aaSMatthew Dillon 				if (arg2 &&
42083f2a3aaSMatthew Dillon 				    check_softlinks(fd, new_config,
42183f2a3aaSMatthew Dillon 						    snapshots_path)) {
4225e435c92SMatthew Dillon 					printf("only removing old snapshots\n");
4235e435c92SMatthew Dillon 					prune_warning = 1;
42483f2a3aaSMatthew Dillon 					cleanup_softlinks(fd, new_config,
42583f2a3aaSMatthew Dillon 							  snapshots_path,
4265e435c92SMatthew Dillon 							  arg2, arg3);
4275e435c92SMatthew Dillon 				} else {
4285e435c92SMatthew Dillon 					printf("disabled\n");
4295e435c92SMatthew Dillon 					snapshots_disabled = 1;
4305e435c92SMatthew Dillon 				}
4315e435c92SMatthew Dillon 			} else
4326a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4336a6e350fSMatthew Dillon 				printf("run\n");
43483f2a3aaSMatthew Dillon 				cleanup_softlinks(fd, new_config,
43583f2a3aaSMatthew Dillon 						  snapshots_path,
4365e435c92SMatthew Dillon 						  arg2, arg3);
437b5ec5ad4SMatthew Dillon 				r = create_snapshot(path, snapshots_path);
4386a6e350fSMatthew Dillon 			} else {
4396a6e350fSMatthew Dillon 				printf("skip\n");
4406a6e350fSMatthew Dillon 			}
4415e435c92SMatthew Dillon 		} else if (arg1 == 0) {
4425e435c92SMatthew Dillon 			/*
4435e435c92SMatthew Dillon 			 * The commands following this check can't handle
4445e435c92SMatthew Dillon 			 * a period of 0, so call the feature disabled and
4455e435c92SMatthew Dillon 			 * ignore the directive.
4465e435c92SMatthew Dillon 			 */
4475e435c92SMatthew Dillon 			printf("disabled\n");
4486a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
4496a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4505e435c92SMatthew Dillon 				if (prune_warning) {
4515e435c92SMatthew Dillon 					printf("run - WARNING snapshot "
4525e435c92SMatthew Dillon 					       "softlinks present "
453226f3799SThomas Nikolajsen 					       "but snapshots disabled\n");
4545e435c92SMatthew Dillon 				} else {
455c6c298a7SMatthew Dillon 					printf("run\n");
4565e435c92SMatthew Dillon 				}
4576a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
458c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
4596a6e350fSMatthew Dillon 			} else {
4606a6e350fSMatthew Dillon 				printf("skip\n");
4616a6e350fSMatthew Dillon 			}
4620b8bd7daSMatthew Dillon 		} else if (strcmp(cmd, "rebalance") == 0) {
4630b8bd7daSMatthew Dillon 			found_rebal = 1;
4640b8bd7daSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4650b8bd7daSMatthew Dillon 				printf("run");
4660b8bd7daSMatthew Dillon 				fflush(stdout);
4670b8bd7daSMatthew Dillon 				if (VerboseOpt)
4680b8bd7daSMatthew Dillon 					printf("\n");
4690b8bd7daSMatthew Dillon 				r = cleanup_rebalance(path, snapshots_path,
4700b8bd7daSMatthew Dillon 						arg1, arg2);
4710b8bd7daSMatthew Dillon 			} else {
4720b8bd7daSMatthew Dillon 				printf("skip\n");
4730b8bd7daSMatthew Dillon 			}
4746a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
4756a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4766a6e350fSMatthew Dillon 				printf("run");
4776a6e350fSMatthew Dillon 				fflush(stdout);
4786a6e350fSMatthew Dillon 				if (VerboseOpt)
4796a6e350fSMatthew Dillon 					printf("\n");
4806a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
4816a6e350fSMatthew Dillon 						arg1, arg2);
4826a6e350fSMatthew Dillon 			} else {
4836a6e350fSMatthew Dillon 				printf("skip\n");
4846a6e350fSMatthew Dillon 			}
4856a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
4866a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4876a6e350fSMatthew Dillon 				printf("run");
4886a6e350fSMatthew Dillon 				fflush(stdout);
4896a6e350fSMatthew Dillon 				if (VerboseOpt)
4906a6e350fSMatthew Dillon 					printf("\n");
4916a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
4926a6e350fSMatthew Dillon 					       arg1, arg2);
4936a6e350fSMatthew Dillon 			} else {
4946a6e350fSMatthew Dillon 				printf("skip\n");
4956a6e350fSMatthew Dillon 			}
4966a6e350fSMatthew Dillon 		} else {
4976a6e350fSMatthew Dillon 			printf("unknown directive\n");
4986a6e350fSMatthew Dillon 			r = 1;
4996a6e350fSMatthew Dillon 		}
5006a6e350fSMatthew Dillon 		if (r == 0)
5016a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
5026a6e350fSMatthew Dillon 	}
5030b8bd7daSMatthew Dillon 
5040b8bd7daSMatthew Dillon 	/*
50583f2a3aaSMatthew Dillon 	 * Add new rebalance feature if the config doesn't have it.
506b5ec5ad4SMatthew Dillon 	 * (old style config only).
5070b8bd7daSMatthew Dillon 	 */
50883f2a3aaSMatthew Dillon 	if (new_config == 0 && found_rebal == 0) {
5090b8bd7daSMatthew Dillon 		if ((fp = fopen(config_path, "r+")) != NULL) {
5100b8bd7daSMatthew Dillon 			fseek(fp, 0L, 2);
5110b8bd7daSMatthew Dillon 			fprintf(fp, "rebalance 1d 5m\n");
5120b8bd7daSMatthew Dillon 			fclose(fp);
5130b8bd7daSMatthew Dillon 		}
5140b8bd7daSMatthew Dillon 	}
51583f2a3aaSMatthew Dillon 
51683f2a3aaSMatthew Dillon 	/*
51783f2a3aaSMatthew Dillon 	 * Cleanup, and delay a little
51883f2a3aaSMatthew Dillon 	 */
51983f2a3aaSMatthew Dillon 	close(fd);
5206a6e350fSMatthew Dillon 	usleep(1000);
5216a6e350fSMatthew Dillon }
5226a6e350fSMatthew Dillon 
52383f2a3aaSMatthew Dillon /*
52483f2a3aaSMatthew Dillon  * Initialize new config data (new or old style)
52583f2a3aaSMatthew Dillon  */
52683f2a3aaSMatthew Dillon static void
52783f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config)
52883f2a3aaSMatthew Dillon {
52983f2a3aaSMatthew Dillon 	const char *snapshots;
53083f2a3aaSMatthew Dillon 
53183f2a3aaSMatthew Dillon 	if (strcmp(path, "/tmp") == 0 ||
53283f2a3aaSMatthew Dillon 	    strcmp(path, "/var/tmp") == 0 ||
53383f2a3aaSMatthew Dillon 	    strcmp(path, "/usr/obj") == 0) {
53483f2a3aaSMatthew Dillon 		snapshots = "snapshots 0d 0d\n";
53583f2a3aaSMatthew Dillon 	} else {
53683f2a3aaSMatthew Dillon 		snapshots = "snapshots 1d 60d\n";
53783f2a3aaSMatthew Dillon 	}
53883f2a3aaSMatthew Dillon 	bzero(config->config.text, sizeof(config->config.text));
53983f2a3aaSMatthew Dillon 	snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s",
54083f2a3aaSMatthew Dillon 		snapshots,
54183f2a3aaSMatthew Dillon 		"prune     1d 5m\n"
54283f2a3aaSMatthew Dillon 		"rebalance 1d 5m\n"
54383f2a3aaSMatthew Dillon 		"reblock   1d 5m\n"
544f6532f03SThomas Nikolajsen 		"recopy    30d 10m\n");
54583f2a3aaSMatthew Dillon }
54683f2a3aaSMatthew Dillon 
54783f2a3aaSMatthew Dillon /*
54883f2a3aaSMatthew Dillon  * Migrate configuration data from the old snapshots/config
54916265794SThomas Nikolajsen  * file to the new meta-data format.
55083f2a3aaSMatthew Dillon  */
55183f2a3aaSMatthew Dillon static void
55283f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config)
55383f2a3aaSMatthew Dillon {
55483f2a3aaSMatthew Dillon 	int n;
55583f2a3aaSMatthew Dillon 
55683f2a3aaSMatthew Dillon 	n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp);
55783f2a3aaSMatthew Dillon 	if (n >= 0)
55883f2a3aaSMatthew Dillon 		bzero(config->config.text + n, sizeof(config->config.text) - n);
55983f2a3aaSMatthew Dillon }
56083f2a3aaSMatthew Dillon 
56183f2a3aaSMatthew Dillon /*
56283f2a3aaSMatthew Dillon  * Migrate snapshot softlinks in the snapshots directory to the
56383f2a3aaSMatthew Dillon  * new meta-data format.  The softlinks are left intact, but
56483f2a3aaSMatthew Dillon  * this way the pruning code won't lose track of them if you
56583f2a3aaSMatthew Dillon  * happen to blow away the snapshots directory.
56683f2a3aaSMatthew Dillon  */
56783f2a3aaSMatthew Dillon static void
56883f2a3aaSMatthew Dillon migrate_snapshots(int fd, const char *snapshots_path)
56983f2a3aaSMatthew Dillon {
57083f2a3aaSMatthew Dillon 	struct hammer_ioc_snapshot snapshot;
57183f2a3aaSMatthew Dillon 	struct dirent *den;
57283f2a3aaSMatthew Dillon 	struct stat st;
57383f2a3aaSMatthew Dillon 	DIR *dir;
57483f2a3aaSMatthew Dillon 	char *fpath;
57583f2a3aaSMatthew Dillon 
57683f2a3aaSMatthew Dillon 	bzero(&snapshot, sizeof(snapshot));
57783f2a3aaSMatthew Dillon 
57883f2a3aaSMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
57983f2a3aaSMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
58083f2a3aaSMatthew Dillon 			if (den->d_name[0] == '.')
58183f2a3aaSMatthew Dillon 				continue;
58283f2a3aaSMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
58383f2a3aaSMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) {
58483f2a3aaSMatthew Dillon 				migrate_one_snapshot(fd, fpath, &snapshot);
58583f2a3aaSMatthew Dillon 			}
58683f2a3aaSMatthew Dillon 			free(fpath);
58783f2a3aaSMatthew Dillon 		}
58883f2a3aaSMatthew Dillon 		closedir(dir);
58983f2a3aaSMatthew Dillon 	}
59083f2a3aaSMatthew Dillon 	migrate_one_snapshot(fd, NULL, &snapshot);
59183f2a3aaSMatthew Dillon 
59283f2a3aaSMatthew Dillon }
59383f2a3aaSMatthew Dillon 
59483f2a3aaSMatthew Dillon /*
59583f2a3aaSMatthew Dillon  * Migrate a single snapshot.  If fpath is NULL the ioctl is flushed,
59683f2a3aaSMatthew Dillon  * otherwise it is flushed when it fills up.
59783f2a3aaSMatthew Dillon  */
59883f2a3aaSMatthew Dillon static void
59983f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath,
60083f2a3aaSMatthew Dillon 		     struct hammer_ioc_snapshot *snapshot)
60183f2a3aaSMatthew Dillon {
60283f2a3aaSMatthew Dillon 	if (fpath) {
60383f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *snap;
60483f2a3aaSMatthew Dillon 		struct tm tm;
60583f2a3aaSMatthew Dillon 		time_t t;
60683f2a3aaSMatthew Dillon 		int year;
60783f2a3aaSMatthew Dillon 		int month;
60883f2a3aaSMatthew Dillon 		int day = 0;
60983f2a3aaSMatthew Dillon 		int hour = 0;
61083f2a3aaSMatthew Dillon 		int minute = 0;
61183f2a3aaSMatthew Dillon 		int r;
61283f2a3aaSMatthew Dillon 		char linkbuf[1024];
61383f2a3aaSMatthew Dillon 		const char *ptr;
61483f2a3aaSMatthew Dillon 		hammer_tid_t tid;
61583f2a3aaSMatthew Dillon 
61683f2a3aaSMatthew Dillon 		t = (time_t)-1;
61783f2a3aaSMatthew Dillon 		tid = (hammer_tid_t)(int64_t)-1;
61883f2a3aaSMatthew Dillon 
61983f2a3aaSMatthew Dillon 		ptr = fpath;
62083f2a3aaSMatthew Dillon 		while (*ptr && *ptr != '-' && *ptr != '.')
62183f2a3aaSMatthew Dillon 			++ptr;
62283f2a3aaSMatthew Dillon 		if (*ptr)
62383f2a3aaSMatthew Dillon 			++ptr;
62483f2a3aaSMatthew Dillon 		r = sscanf(ptr, "%4d%2d%2d-%2d%2d",
62583f2a3aaSMatthew Dillon 			   &year, &month, &day, &hour, &minute);
62683f2a3aaSMatthew Dillon 
62783f2a3aaSMatthew Dillon 		if (r >= 3) {
62883f2a3aaSMatthew Dillon 			bzero(&tm, sizeof(tm));
62983f2a3aaSMatthew Dillon 			tm.tm_isdst = -1;
63083f2a3aaSMatthew Dillon 			tm.tm_min = minute;
63183f2a3aaSMatthew Dillon 			tm.tm_hour = hour;
63283f2a3aaSMatthew Dillon 			tm.tm_mday = day;
63383f2a3aaSMatthew Dillon 			tm.tm_mon = month - 1;
63483f2a3aaSMatthew Dillon 			tm.tm_year = year - 1900;
63583f2a3aaSMatthew Dillon 			t = mktime(&tm);
63683f2a3aaSMatthew Dillon 		}
63783f2a3aaSMatthew Dillon 		bzero(linkbuf, sizeof(linkbuf));
63883f2a3aaSMatthew Dillon 		if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 &&
63983f2a3aaSMatthew Dillon 		    (ptr = strrchr(linkbuf, '@')) != NULL &&
64083f2a3aaSMatthew Dillon 		    ptr > linkbuf && ptr[-1] == '@') {
64183f2a3aaSMatthew Dillon 			tid = strtoull(ptr + 1, NULL, 16);
64283f2a3aaSMatthew Dillon 		}
64383f2a3aaSMatthew Dillon 		if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) {
64483f2a3aaSMatthew Dillon 			snap = &snapshot->snaps[snapshot->count];
64583f2a3aaSMatthew Dillon 			bzero(snap, sizeof(*snap));
64683f2a3aaSMatthew Dillon 			snap->tid = tid;
64783f2a3aaSMatthew Dillon 			snap->ts = (u_int64_t)t * 1000000ULL;
64883f2a3aaSMatthew Dillon 			snprintf(snap->label, sizeof(snap->label),
64983f2a3aaSMatthew Dillon 				 "migrated");
65083f2a3aaSMatthew Dillon 			++snapshot->count;
65183f2a3aaSMatthew Dillon 		}
65283f2a3aaSMatthew Dillon 	}
65383f2a3aaSMatthew Dillon 
65483f2a3aaSMatthew Dillon 	if ((fpath == NULL && snapshot->count) ||
65583f2a3aaSMatthew Dillon 	    snapshot->count == HAMMER_SNAPS_PER_IOCTL) {
65683f2a3aaSMatthew Dillon 		printf(" (%d snapshots)", snapshot->count);
65783f2a3aaSMatthew Dillon again:
65883f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) {
65983f2a3aaSMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
66083f2a3aaSMatthew Dillon 			       strerror(errno));
66183f2a3aaSMatthew Dillon 		} else if (snapshot->head.error == EALREADY) {
66283f2a3aaSMatthew Dillon 			++snapshot->index;
66383f2a3aaSMatthew Dillon 			goto again;
66483f2a3aaSMatthew Dillon 		} else if (snapshot->head.error) {
665068da2c2SMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
66683f2a3aaSMatthew Dillon 			       strerror(snapshot->head.error));
66783f2a3aaSMatthew Dillon 		}
66883f2a3aaSMatthew Dillon 		printf("index %d\n", snapshot->index);
66983f2a3aaSMatthew Dillon 		snapshot->index = 0;
67083f2a3aaSMatthew Dillon 		snapshot->count = 0;
67183f2a3aaSMatthew Dillon 		snapshot->head.error = 0;
67283f2a3aaSMatthew Dillon 	}
67383f2a3aaSMatthew Dillon }
67483f2a3aaSMatthew Dillon 
6756a6e350fSMatthew Dillon static
6766a6e350fSMatthew Dillon int
6776a6e350fSMatthew Dillon strtosecs(char *ptr)
6786a6e350fSMatthew Dillon {
6796a6e350fSMatthew Dillon 	int val;
6806a6e350fSMatthew Dillon 
6816a6e350fSMatthew Dillon 	val = strtol(ptr, &ptr, 0);
6826a6e350fSMatthew Dillon 	switch(*ptr) {
6836a6e350fSMatthew Dillon 	case 'd':
6846a6e350fSMatthew Dillon 		val *= 24;
6856a6e350fSMatthew Dillon 		/* fall through */
6866a6e350fSMatthew Dillon 	case 'h':
6876a6e350fSMatthew Dillon 		val *= 60;
6886a6e350fSMatthew Dillon 		/* fall through */
6896a6e350fSMatthew Dillon 	case 'm':
6906a6e350fSMatthew Dillon 		val *= 60;
6916a6e350fSMatthew Dillon 		/* fall through */
6926a6e350fSMatthew Dillon 	case 's':
6936a6e350fSMatthew Dillon 		break;
6946a6e350fSMatthew Dillon 	default:
6956a6e350fSMatthew Dillon 		errx(1, "illegal suffix converting %s\n", ptr);
6966a6e350fSMatthew Dillon 		break;
6976a6e350fSMatthew Dillon 	}
6986a6e350fSMatthew Dillon 	return(val);
6996a6e350fSMatthew Dillon }
7006a6e350fSMatthew Dillon 
7016a6e350fSMatthew Dillon static const char *
7026a6e350fSMatthew Dillon dividing_slash(const char *path)
7036a6e350fSMatthew Dillon {
7046a6e350fSMatthew Dillon 	int len = strlen(path);
7056a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
7066a6e350fSMatthew Dillon 		return("");
7076a6e350fSMatthew Dillon 	else
7086a6e350fSMatthew Dillon 		return("/");
7096a6e350fSMatthew Dillon }
7106a6e350fSMatthew Dillon 
7116a6e350fSMatthew Dillon /*
7126a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
7136a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
7146a6e350fSMatthew Dillon  * current time_t so we can save it later on.
7156a6e350fSMatthew Dillon  *
7166a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
7176a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
7186a6e350fSMatthew Dillon  * of how close the last operation actually was.
7196a6e350fSMatthew Dillon  */
7206a6e350fSMatthew Dillon static int
7216a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
7226a6e350fSMatthew Dillon 	time_t *savep)
7236a6e350fSMatthew Dillon {
7246a6e350fSMatthew Dillon 	char *check_path;
7256a6e350fSMatthew Dillon 	struct tm tp1;
7266a6e350fSMatthew Dillon 	struct tm tp2;
7276a6e350fSMatthew Dillon 	FILE *fp;
7286a6e350fSMatthew Dillon 	time_t baset, lastt;
7296a6e350fSMatthew Dillon 	char buf[256];
7306a6e350fSMatthew Dillon 
7316a6e350fSMatthew Dillon 	time(savep);
7326a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
7336a6e350fSMatthew Dillon 
7346a6e350fSMatthew Dillon 	/*
7356a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
7366a6e350fSMatthew Dillon 	 */
7376a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
7386a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
7396a6e350fSMatthew Dillon 	free(check_path);
7406a6e350fSMatthew Dillon 	if (fp == NULL)
7416a6e350fSMatthew Dillon 		return(1);
7426a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
7436a6e350fSMatthew Dillon 		fclose(fp);
7446a6e350fSMatthew Dillon 		return(1);
7456a6e350fSMatthew Dillon 	}
7466a6e350fSMatthew Dillon 	fclose(fp);
7476a6e350fSMatthew Dillon 
7486a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
7496a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
7506a6e350fSMatthew Dillon 
7516a6e350fSMatthew Dillon 	/*
7526a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
7536a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
7546a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
7556a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
7566a6e350fSMatthew Dillon 	 */
7576a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
7586a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
7596a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
7606a6e350fSMatthew Dillon 	}
7616a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
7626a6e350fSMatthew Dillon 		tp1.tm_min = 0;
7636a6e350fSMatthew Dillon 		tp2.tm_min = 0;
7646a6e350fSMatthew Dillon 	}
7656a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
7666a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
7676a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
7686a6e350fSMatthew Dillon 	}
7696a6e350fSMatthew Dillon 
7706a6e350fSMatthew Dillon 	baset = mktime(&tp1);
7716a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
7726a6e350fSMatthew Dillon 
7736a6e350fSMatthew Dillon #if 0
7746a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
7756a6e350fSMatthew Dillon #endif
7766a6e350fSMatthew Dillon 
7776a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
7786a6e350fSMatthew Dillon 		return(1);
7796a6e350fSMatthew Dillon 	return(0);
7806a6e350fSMatthew Dillon }
7816a6e350fSMatthew Dillon 
7826a6e350fSMatthew Dillon /*
7836a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
7846a6e350fSMatthew Dillon  */
7856a6e350fSMatthew Dillon static void
7866a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
7876a6e350fSMatthew Dillon 			time_t savet)
7886a6e350fSMatthew Dillon {
7896a6e350fSMatthew Dillon 	char *ocheck_path;
7906a6e350fSMatthew Dillon 	char *ncheck_path;
7916a6e350fSMatthew Dillon 	FILE *fp;
7926a6e350fSMatthew Dillon 
7936a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
7946a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
7956a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
7963b9fbdeaSMatthew Dillon 	if (fp) {
7976a6e350fSMatthew Dillon 		fprintf(fp, "0x%08llx\n", (long long)savet);
7986a6e350fSMatthew Dillon 		if (fclose(fp) == 0)
7996a6e350fSMatthew Dillon 			rename(ncheck_path, ocheck_path);
8006a6e350fSMatthew Dillon 		remove(ncheck_path);
8013b9fbdeaSMatthew Dillon 	} else {
8023b9fbdeaSMatthew Dillon 		fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
8033b9fbdeaSMatthew Dillon 			ncheck_path, strerror(errno));
8043b9fbdeaSMatthew Dillon 	}
8056a6e350fSMatthew Dillon }
8066a6e350fSMatthew Dillon 
807ff1c9800SMatthew Dillon /*
808ff1c9800SMatthew Dillon  * Simply count the number of softlinks in the snapshots dir
809ff1c9800SMatthew Dillon  */
810c6c298a7SMatthew Dillon static int
81183f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path)
812c6c298a7SMatthew Dillon {
813c6c298a7SMatthew Dillon 	struct dirent *den;
814c6c298a7SMatthew Dillon 	struct stat st;
815c6c298a7SMatthew Dillon 	DIR *dir;
816c6c298a7SMatthew Dillon 	char *fpath;
817c6c298a7SMatthew Dillon 	int res = 0;
818c6c298a7SMatthew Dillon 
81983f2a3aaSMatthew Dillon 	/*
82083f2a3aaSMatthew Dillon 	 * Old-style softlink-based snapshots
82183f2a3aaSMatthew Dillon 	 */
822c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
823c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
824c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
825c6c298a7SMatthew Dillon 				continue;
826c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
827c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
828c6c298a7SMatthew Dillon 				++res;
829c6c298a7SMatthew Dillon 			free(fpath);
830c6c298a7SMatthew Dillon 		}
831c6c298a7SMatthew Dillon 		closedir(dir);
832c6c298a7SMatthew Dillon 	}
83383f2a3aaSMatthew Dillon 
83483f2a3aaSMatthew Dillon 	/*
83583f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
83683f2a3aaSMatthew Dillon 	 * count those too.
83783f2a3aaSMatthew Dillon 	 */
83883f2a3aaSMatthew Dillon 	if (new_config) {
83983f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
84083f2a3aaSMatthew Dillon 
84183f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
84283f2a3aaSMatthew Dillon 		do {
84383f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
84483f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
84583f2a3aaSMatthew Dillon 					"snapshot error");
84683f2a3aaSMatthew Dillon 				/* not reached */
84783f2a3aaSMatthew Dillon 			}
84883f2a3aaSMatthew Dillon 			res += snapshot.count;
84983f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
85083f2a3aaSMatthew Dillon 	}
851c6c298a7SMatthew Dillon 	return (res);
852c6c298a7SMatthew Dillon }
853c6c298a7SMatthew Dillon 
8546a6e350fSMatthew Dillon /*
855ff1c9800SMatthew Dillon  * Clean up expired softlinks in the snapshots dir
856ff1c9800SMatthew Dillon  */
857ff1c9800SMatthew Dillon static void
85883f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config,
85983f2a3aaSMatthew Dillon 		  const char *snapshots_path, int arg2, char *arg3)
860ff1c9800SMatthew Dillon {
861ff1c9800SMatthew Dillon 	struct dirent *den;
862ff1c9800SMatthew Dillon 	struct stat st;
863ff1c9800SMatthew Dillon 	DIR *dir;
864ff1c9800SMatthew Dillon 	char *fpath;
8655e435c92SMatthew Dillon 	int anylink = 0;
8665e435c92SMatthew Dillon 
86709e1b0d6SMatthew Dillon 	if (arg3 != NULL && strstr(arg3, "any") != NULL)
8685e435c92SMatthew Dillon 		anylink = 1;
869ff1c9800SMatthew Dillon 
870ff1c9800SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
871ff1c9800SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
872ff1c9800SMatthew Dillon 			if (den->d_name[0] == '.')
873ff1c9800SMatthew Dillon 				continue;
874ff1c9800SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
875ff1c9800SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
8765e435c92SMatthew Dillon 			    (anylink || strncmp(den->d_name, "snap-", 5) == 0)
8775e435c92SMatthew Dillon 			) {
878ff1c9800SMatthew Dillon 				if (check_expired(den->d_name, arg2)) {
879ff1c9800SMatthew Dillon 					if (VerboseOpt) {
880ff1c9800SMatthew Dillon 						printf("    expire %s\n",
881ff1c9800SMatthew Dillon 							fpath);
882ff1c9800SMatthew Dillon 					}
883ff1c9800SMatthew Dillon 					remove(fpath);
884ff1c9800SMatthew Dillon 				}
885ff1c9800SMatthew Dillon 			}
886ff1c9800SMatthew Dillon 			free(fpath);
887ff1c9800SMatthew Dillon 		}
888ff1c9800SMatthew Dillon 		closedir(dir);
889ff1c9800SMatthew Dillon 	}
89083f2a3aaSMatthew Dillon 
89183f2a3aaSMatthew Dillon 	/*
89283f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
89383f2a3aaSMatthew Dillon 	 * count those too.
89483f2a3aaSMatthew Dillon 	 */
89583f2a3aaSMatthew Dillon 	if (new_config) {
89683f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
89783f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot dsnapshot;
89883f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *snap;
89983f2a3aaSMatthew Dillon 		struct tm *tp;
90083f2a3aaSMatthew Dillon 		time_t t;
90183f2a3aaSMatthew Dillon 		char snapts[32];
90283f2a3aaSMatthew Dillon 		u_int32_t i;
90383f2a3aaSMatthew Dillon 
90483f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
90583f2a3aaSMatthew Dillon 		bzero(&dsnapshot, sizeof(dsnapshot));
90683f2a3aaSMatthew Dillon 		do {
90783f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
90883f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
90983f2a3aaSMatthew Dillon 					"snapshot error");
91083f2a3aaSMatthew Dillon 				/* not reached */
91183f2a3aaSMatthew Dillon 			}
91283f2a3aaSMatthew Dillon 			for (i = 0; i < snapshot.count; ++i) {
91383f2a3aaSMatthew Dillon 				snap = &snapshot.snaps[i];
91483f2a3aaSMatthew Dillon 				t = time(NULL) - snap->ts / 1000000ULL;
915068da2c2SMatthew Dillon 				if ((int)t > arg2 && snap->tid != 0) {
91683f2a3aaSMatthew Dillon 					dsnapshot.snaps[dsnapshot.count++] =
91783f2a3aaSMatthew Dillon 						*snap;
91883f2a3aaSMatthew Dillon 				}
91983f2a3aaSMatthew Dillon 				if ((int)t > arg2 && VerboseOpt) {
92083f2a3aaSMatthew Dillon 					tp = localtime(&t);
92183f2a3aaSMatthew Dillon 					strftime(snapts, sizeof(snapts),
92283f2a3aaSMatthew Dillon 						 "%Y-%m-%d %H:%M:%S %Z", tp);
92383f2a3aaSMatthew Dillon 					printf("    expire 0x%016jx %s %s\n",
92483f2a3aaSMatthew Dillon 					       (uintmax_t)snap->tid,
92583f2a3aaSMatthew Dillon 					       snapts,
92683f2a3aaSMatthew Dillon 					       snap->label);
92783f2a3aaSMatthew Dillon 				}
92883f2a3aaSMatthew Dillon 				if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL) {
92983f2a3aaSMatthew Dillon 					if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) {
930068da2c2SMatthew Dillon 						printf("    Ioctl to delete snapshots failed: %s index %d\n", strerror(errno), dsnapshot.index);
93183f2a3aaSMatthew Dillon 					} else if (dsnapshot.head.error) {
93283f2a3aaSMatthew Dillon 						printf("    Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error));
933068da2c2SMatthew Dillon 						exit(1);
93483f2a3aaSMatthew Dillon 					}
935068da2c2SMatthew Dillon 					dsnapshot.index = 0;
93683f2a3aaSMatthew Dillon 					dsnapshot.count = 0;
93783f2a3aaSMatthew Dillon 					dsnapshot.head.error = 0;
93883f2a3aaSMatthew Dillon 				}
93983f2a3aaSMatthew Dillon 			}
94083f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
94183f2a3aaSMatthew Dillon 
94283f2a3aaSMatthew Dillon 		if (dsnapshot.count) {
94383f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) {
94483f2a3aaSMatthew Dillon 				printf("    Ioctl to delete snapshots failed: %s\n", strerror(errno));
94583f2a3aaSMatthew Dillon 			} else if (dsnapshot.head.error) {
94683f2a3aaSMatthew Dillon 				printf("    Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error));
94783f2a3aaSMatthew Dillon 			}
94883f2a3aaSMatthew Dillon 			dsnapshot.count = 0;
949068da2c2SMatthew Dillon 			dsnapshot.index = 0;
95083f2a3aaSMatthew Dillon 			dsnapshot.head.error = 0;
95183f2a3aaSMatthew Dillon 		}
95283f2a3aaSMatthew Dillon 	}
953ff1c9800SMatthew Dillon }
954ff1c9800SMatthew Dillon 
955ff1c9800SMatthew Dillon /*
956ff1c9800SMatthew Dillon  * Take a softlink path in the form snap-yyyymmdd-hhmm and the
957ff1c9800SMatthew Dillon  * expiration in seconds (arg2) and return non-zero if the softlink
958ff1c9800SMatthew Dillon  * has expired.
959ff1c9800SMatthew Dillon  */
960ff1c9800SMatthew Dillon static int
961ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2)
962ff1c9800SMatthew Dillon {
963ff1c9800SMatthew Dillon 	struct tm tm;
964ff1c9800SMatthew Dillon 	time_t t;
965ff1c9800SMatthew Dillon 	int year;
966ff1c9800SMatthew Dillon 	int month;
9675e435c92SMatthew Dillon 	int day = 0;
9685e435c92SMatthew Dillon 	int hour = 0;
9695e435c92SMatthew Dillon 	int minute = 0;
970ff1c9800SMatthew Dillon 	int r;
971ff1c9800SMatthew Dillon 
9725e435c92SMatthew Dillon 	while (*fpath && *fpath != '-' && *fpath != '.')
9735e435c92SMatthew Dillon 		++fpath;
9745e435c92SMatthew Dillon 	if (*fpath)
9755e435c92SMatthew Dillon 		++fpath;
9765e435c92SMatthew Dillon 
9775e435c92SMatthew Dillon 	r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
978ff1c9800SMatthew Dillon 		   &year, &month, &day, &hour, &minute);
9795e435c92SMatthew Dillon 
9805e435c92SMatthew Dillon 	if (r >= 3) {
981ff1c9800SMatthew Dillon 		bzero(&tm, sizeof(tm));
982ff1c9800SMatthew Dillon 		tm.tm_isdst = -1;
983ff1c9800SMatthew Dillon 		tm.tm_min = minute;
984ff1c9800SMatthew Dillon 		tm.tm_hour = hour;
985ff1c9800SMatthew Dillon 		tm.tm_mday = day;
986ff1c9800SMatthew Dillon 		tm.tm_mon = month - 1;
987ff1c9800SMatthew Dillon 		tm.tm_year = year - 1900;
9885e435c92SMatthew Dillon 		t = mktime(&tm);
9895e435c92SMatthew Dillon 		if (t == (time_t)-1)
9905e435c92SMatthew Dillon 			return(0);
9915e435c92SMatthew Dillon 		t = time(NULL) - t;
992ff1c9800SMatthew Dillon 		if ((int)t > arg2)
993ff1c9800SMatthew Dillon 			return(1);
994ff1c9800SMatthew Dillon 	}
995ff1c9800SMatthew Dillon 	return(0);
996ff1c9800SMatthew Dillon }
997ff1c9800SMatthew Dillon 
998ff1c9800SMatthew Dillon /*
9996a6e350fSMatthew Dillon  * Issue a snapshot.
10006a6e350fSMatthew Dillon  */
10016a6e350fSMatthew Dillon static int
1002b5ec5ad4SMatthew Dillon create_snapshot(const char *path, const char *snapshots_path)
10036a6e350fSMatthew Dillon {
10046a6e350fSMatthew Dillon 	int r;
10056a6e350fSMatthew Dillon 
1006ff1c9800SMatthew Dillon 	runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
10076a6e350fSMatthew Dillon 	return(r);
10086a6e350fSMatthew Dillon }
10096a6e350fSMatthew Dillon 
10106a6e350fSMatthew Dillon static int
10116a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path,
1012c6c298a7SMatthew Dillon 		  int arg1 __unused, int arg2, int snapshots_disabled)
10136a6e350fSMatthew Dillon {
1014c6c298a7SMatthew Dillon 	/*
1015c6c298a7SMatthew Dillon 	 * If snapshots have been disabled run prune-everything instead
1016c6c298a7SMatthew Dillon 	 * of prune.
1017c6c298a7SMatthew Dillon 	 */
1018c6c298a7SMatthew Dillon 	if (snapshots_disabled && arg2) {
1019c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
1020c6c298a7SMatthew Dillon 			snapshots_path, arg2, path);
1021c6c298a7SMatthew Dillon 	} else if (snapshots_disabled) {
1022c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer prune-everything %s", path);
1023c6c298a7SMatthew Dillon 	} else if (arg2) {
10246a6e350fSMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
10256a6e350fSMatthew Dillon 			snapshots_path, arg2, snapshots_path);
10266a6e350fSMatthew Dillon 	} else {
10276a6e350fSMatthew Dillon 		runcmd(NULL, "hammer prune %s", snapshots_path);
10286a6e350fSMatthew Dillon 	}
10296a6e350fSMatthew Dillon 	return(0);
10306a6e350fSMatthew Dillon }
10316a6e350fSMatthew Dillon 
10326a6e350fSMatthew Dillon static int
10330b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path,
10340b8bd7daSMatthew Dillon 		  int arg1 __unused, int arg2)
10350b8bd7daSMatthew Dillon {
10360b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
10370b8bd7daSMatthew Dillon 		printf(".");
10380b8bd7daSMatthew Dillon 		fflush(stdout);
10390b8bd7daSMatthew Dillon 	}
10400b8bd7daSMatthew Dillon 
10410b8bd7daSMatthew Dillon 	runcmd(NULL,
10420b8bd7daSMatthew Dillon 	       "hammer -c %s/.rebalance.cycle -t %d rebalance %s",
10430b8bd7daSMatthew Dillon 	       snapshots_path, arg2, path);
10440b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
10450b8bd7daSMatthew Dillon 		printf(".");
10460b8bd7daSMatthew Dillon 		fflush(stdout);
10470b8bd7daSMatthew Dillon 	}
10480b8bd7daSMatthew Dillon 	if (VerboseOpt == 0)
10490b8bd7daSMatthew Dillon 		printf("\n");
10500b8bd7daSMatthew Dillon 	return(0);
10510b8bd7daSMatthew Dillon }
10520b8bd7daSMatthew Dillon 
10530b8bd7daSMatthew Dillon static int
10546a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path,
10556a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
10566a6e350fSMatthew Dillon {
10576a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
10586a6e350fSMatthew Dillon 		printf(".");
10596a6e350fSMatthew Dillon 		fflush(stdout);
10606a6e350fSMatthew Dillon 	}
1061797a0b63SMatthew Dillon 
1062797a0b63SMatthew Dillon 	/*
1063797a0b63SMatthew Dillon 	 * When reblocking the B-Tree always reblock everything in normal
1064797a0b63SMatthew Dillon 	 * mode.
1065797a0b63SMatthew Dillon 	 */
10666a6e350fSMatthew Dillon 	runcmd(NULL,
1067797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
10686a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
10696a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
10706a6e350fSMatthew Dillon 		printf(".");
10716a6e350fSMatthew Dillon 		fflush(stdout);
10726a6e350fSMatthew Dillon 	}
1073797a0b63SMatthew Dillon 
1074797a0b63SMatthew Dillon 	/*
1075797a0b63SMatthew Dillon 	 * When reblocking the inodes always reblock everything in normal
1076797a0b63SMatthew Dillon 	 * mode.
1077797a0b63SMatthew Dillon 	 */
10786a6e350fSMatthew Dillon 	runcmd(NULL,
1079797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
10806a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
10816a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
10826a6e350fSMatthew Dillon 		printf(".");
10836a6e350fSMatthew Dillon 		fflush(stdout);
10846a6e350fSMatthew Dillon 	}
1085797a0b63SMatthew Dillon 
1086797a0b63SMatthew Dillon 	/*
1087797a0b63SMatthew Dillon 	 * When reblocking the directories always reblock everything in normal
1088797a0b63SMatthew Dillon 	 * mode.
1089797a0b63SMatthew Dillon 	 */
1090797a0b63SMatthew Dillon 	runcmd(NULL,
1091797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
1092797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1093797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1094797a0b63SMatthew Dillon 		printf(".");
1095797a0b63SMatthew Dillon 		fflush(stdout);
1096797a0b63SMatthew Dillon 	}
1097797a0b63SMatthew Dillon 
1098797a0b63SMatthew Dillon 	/*
1099797a0b63SMatthew Dillon 	 * Do not reblock all the data in normal mode.
1100797a0b63SMatthew Dillon 	 */
11016a6e350fSMatthew Dillon 	runcmd(NULL,
11026a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
11036a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11046a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
11056a6e350fSMatthew Dillon 		printf("\n");
11066a6e350fSMatthew Dillon 	return(0);
11076a6e350fSMatthew Dillon }
11086a6e350fSMatthew Dillon 
11096a6e350fSMatthew Dillon static int
11106a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path,
11116a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
11126a6e350fSMatthew Dillon {
11136a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11146a6e350fSMatthew Dillon 		printf(".");
11156a6e350fSMatthew Dillon 		fflush(stdout);
11166a6e350fSMatthew Dillon 	}
11176a6e350fSMatthew Dillon 	runcmd(NULL,
11186a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
11196a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11206a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11216a6e350fSMatthew Dillon 		printf(".");
11226a6e350fSMatthew Dillon 		fflush(stdout);
11236a6e350fSMatthew Dillon 	}
11246a6e350fSMatthew Dillon 	runcmd(NULL,
11256a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
11266a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11276a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11286a6e350fSMatthew Dillon 		printf(".");
11296a6e350fSMatthew Dillon 		fflush(stdout);
11306a6e350fSMatthew Dillon 	}
11316a6e350fSMatthew Dillon 	runcmd(NULL,
1132797a0b63SMatthew Dillon 	       "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
1133797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1134797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1135797a0b63SMatthew Dillon 		printf(".");
1136797a0b63SMatthew Dillon 		fflush(stdout);
1137797a0b63SMatthew Dillon 	}
1138797a0b63SMatthew Dillon 	runcmd(NULL,
11396a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
11406a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11416a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
11426a6e350fSMatthew Dillon 		printf("\n");
11436a6e350fSMatthew Dillon 	return(0);
11446a6e350fSMatthew Dillon }
11456a6e350fSMatthew Dillon 
11466a6e350fSMatthew Dillon static
11476a6e350fSMatthew Dillon void
11486a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...)
11496a6e350fSMatthew Dillon {
11506a6e350fSMatthew Dillon 	va_list va;
11516a6e350fSMatthew Dillon 	char *cmd;
11526a6e350fSMatthew Dillon 	char *arg;
11536a6e350fSMatthew Dillon 	char **av;
11546a6e350fSMatthew Dillon 	int n;
11556a6e350fSMatthew Dillon 	int nmax;
11566a6e350fSMatthew Dillon 	int res;
11576a6e350fSMatthew Dillon 	pid_t pid;
11586a6e350fSMatthew Dillon 
11596a6e350fSMatthew Dillon 	/*
11606a6e350fSMatthew Dillon 	 * Generate the command
11616a6e350fSMatthew Dillon 	 */
11626a6e350fSMatthew Dillon 	va_start(va, ctl);
11636a6e350fSMatthew Dillon 	vasprintf(&cmd, ctl, va);
11646a6e350fSMatthew Dillon 	va_end(va);
11656a6e350fSMatthew Dillon 	if (VerboseOpt)
11666a6e350fSMatthew Dillon 		printf("    %s\n", cmd);
11676a6e350fSMatthew Dillon 
11686a6e350fSMatthew Dillon 	/*
11696a6e350fSMatthew Dillon 	 * Break us down into arguments.  We do not just use system() here
11706a6e350fSMatthew Dillon 	 * because it blocks SIGINT and friends.
11716a6e350fSMatthew Dillon 	 */
11726a6e350fSMatthew Dillon 	n = 0;
11736a6e350fSMatthew Dillon 	nmax = 16;
11746a6e350fSMatthew Dillon 	av = malloc(sizeof(char *) * nmax);
11756a6e350fSMatthew Dillon 
11766a6e350fSMatthew Dillon 	for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
1177c453712aSMatthew Dillon 		if (n == nmax - 1) {
11786a6e350fSMatthew Dillon 			nmax += 16;
11796a6e350fSMatthew Dillon 			av = realloc(av, sizeof(char *) * nmax);
11806a6e350fSMatthew Dillon 		}
11816a6e350fSMatthew Dillon 		av[n++] = arg;
11826a6e350fSMatthew Dillon 	}
1183c453712aSMatthew Dillon 	av[n++] = NULL;
11846a6e350fSMatthew Dillon 
11856a6e350fSMatthew Dillon 	/*
11866a6e350fSMatthew Dillon 	 * Run the command.
11876a6e350fSMatthew Dillon 	 */
1188445faa69SMatthew Dillon 	RunningIoctl = 1;
11896a6e350fSMatthew Dillon 	if ((pid = fork()) == 0) {
11906a6e350fSMatthew Dillon 		if (VerboseOpt < 2) {
11916a6e350fSMatthew Dillon 			int fd = open("/dev/null", O_RDWR);
11926a6e350fSMatthew Dillon 			dup2(fd, 1);
11936a6e350fSMatthew Dillon 			close(fd);
11946a6e350fSMatthew Dillon 		}
11956a6e350fSMatthew Dillon 		execvp(av[0], av);
11966a6e350fSMatthew Dillon 		_exit(127);
11976a6e350fSMatthew Dillon 	} else if (pid < 0) {
11986a6e350fSMatthew Dillon 		res = 127;
11996a6e350fSMatthew Dillon 	} else {
12006a6e350fSMatthew Dillon 		int status;
1201445faa69SMatthew Dillon 
12026a6e350fSMatthew Dillon 		while (waitpid(pid, &status, 0) != pid)
12036a6e350fSMatthew Dillon 			;
12046a6e350fSMatthew Dillon 		res = WEXITSTATUS(status);
12056a6e350fSMatthew Dillon 	}
1206445faa69SMatthew Dillon 	RunningIoctl = 0;
1207445faa69SMatthew Dillon 	if (DidInterrupt)
1208445faa69SMatthew Dillon 		_exit(1);
12096a6e350fSMatthew Dillon 
12106a6e350fSMatthew Dillon 	free(cmd);
12116a6e350fSMatthew Dillon 	free(av);
12126a6e350fSMatthew Dillon 	if (resp)
12136a6e350fSMatthew Dillon 		*resp = res;
12146a6e350fSMatthew Dillon }
1215