xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision 068da2c2)
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
50f13fea8bSSascha 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);
6683f2a3aaSMatthew Dillon static void config_init(const char *path, struct hammer_ioc_config *config);
6783f2a3aaSMatthew Dillon static void migrate_config(FILE *fp, struct hammer_ioc_config *config);
6883f2a3aaSMatthew Dillon static void migrate_snapshots(int fd, const char *snapshots_path);
6983f2a3aaSMatthew Dillon static void migrate_one_snapshot(int fd, const char *fpath,
7083f2a3aaSMatthew Dillon 			struct hammer_ioc_snapshot *snapshot);
716a6e350fSMatthew Dillon static int strtosecs(char *ptr);
726a6e350fSMatthew Dillon static const char *dividing_slash(const char *path);
736a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1,
746a6e350fSMatthew Dillon 			time_t *savep);
756a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd,
766a6e350fSMatthew Dillon 			time_t savet);
7783f2a3aaSMatthew Dillon static int check_softlinks(int fd, int new_config, const char *snapshots_path);
7883f2a3aaSMatthew Dillon static void cleanup_softlinks(int fd, int new_config,
7983f2a3aaSMatthew Dillon 			const char *snapshots_path, int arg2, char *arg3);
80ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2);
816a6e350fSMatthew Dillon 
82b5ec5ad4SMatthew Dillon static int create_snapshot(const char *path, const char *snapshots_path);
830b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path,
840b8bd7daSMatthew Dillon 			int arg1, int arg2);
856a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path,
86c6c298a7SMatthew Dillon 			int arg1, int arg2, int snapshots_disabled);
876a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path,
886a6e350fSMatthew Dillon 			int arg1, int arg2);
896a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path,
906a6e350fSMatthew Dillon 			int arg1, int arg2);
916a6e350fSMatthew Dillon 
926a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...);
936a6e350fSMatthew Dillon 
94b5ec5ad4SMatthew Dillon /*
95b5ec5ad4SMatthew Dillon  * WARNING: Do not make the SNAPSHOTS_BASE "/var/snapshots" because
96b5ec5ad4SMatthew Dillon  * it will interfere with the older HAMMER VERS < 3 snapshots directory
97b5ec5ad4SMatthew Dillon  * for the /var PFS.
98b5ec5ad4SMatthew Dillon  */
99b5ec5ad4SMatthew Dillon #define SNAPSHOTS_BASE	"/var/hammer"	/* HAMMER VERS >= 3 */
1006a6e350fSMatthew Dillon #define WS	" \t\r\n"
1016a6e350fSMatthew Dillon 
102c453712aSMatthew Dillon struct didpfs *FirstPFS;
1036a6e350fSMatthew Dillon 
1046a6e350fSMatthew Dillon void
1056a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
1066a6e350fSMatthew Dillon {
107eac446c5SMatthew Dillon 	char *fstype, *fs, *path;
108eac446c5SMatthew Dillon 	struct statfs *stfsbuf;
109eac446c5SMatthew Dillon 	int mntsize, i;
1106a6e350fSMatthew Dillon 
1116a6e350fSMatthew Dillon 	tzset();
1126a6e350fSMatthew Dillon 	if (ac == 0) {
113eac446c5SMatthew Dillon 		mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
114eac446c5SMatthew Dillon 		if (mntsize > 0) {
115eac446c5SMatthew Dillon 			for (i=0; i < mntsize; i++) {
116eac446c5SMatthew Dillon 				/*
117eac446c5SMatthew Dillon 				 * We will cleanup in the case fstype is hammer.
118eac446c5SMatthew Dillon 				 * If we have null-mounted PFS, we check the
119eac446c5SMatthew Dillon 				 * mount source. If it looks like a PFS, we
120eac446c5SMatthew Dillon 				 * proceed to cleanup also.
121eac446c5SMatthew Dillon 				 */
122eac446c5SMatthew Dillon 				fstype = stfsbuf[i].f_fstypename;
123eac446c5SMatthew Dillon 				fs = stfsbuf[i].f_mntfromname;
124eac446c5SMatthew Dillon 				if ((strcmp(fstype, "hammer") == 0) ||
125eac446c5SMatthew Dillon 				    ((strcmp(fstype, "null") == 0) &&
126cb3c760cSMatthew Dillon 				     (strstr(fs, "/@@0x") != NULL ||
127cb3c760cSMatthew Dillon 				      strstr(fs, "/@@-1") != NULL))) {
128eac446c5SMatthew Dillon 					path = stfsbuf[i].f_mntonname;
1296a6e350fSMatthew Dillon 					do_cleanup(path);
1306a6e350fSMatthew Dillon 				}
131574066d3SMatthew Dillon 			}
132eac446c5SMatthew Dillon 		}
133eac446c5SMatthew Dillon 
1346a6e350fSMatthew Dillon 	} else {
1356a6e350fSMatthew Dillon 		while (ac) {
1366a6e350fSMatthew Dillon 			do_cleanup(*av);
1376a6e350fSMatthew Dillon 			--ac;
1386a6e350fSMatthew Dillon 			++av;
1396a6e350fSMatthew Dillon 		}
1406a6e350fSMatthew Dillon 	}
1416a6e350fSMatthew Dillon }
1426a6e350fSMatthew Dillon 
1436a6e350fSMatthew Dillon static
1446a6e350fSMatthew Dillon void
1456a6e350fSMatthew Dillon do_cleanup(const char *path)
1466a6e350fSMatthew Dillon {
1476a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
14883f2a3aaSMatthew Dillon 	struct hammer_ioc_config config;
14983f2a3aaSMatthew Dillon 	struct hammer_ioc_version version;
1506a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
1516a6e350fSMatthew Dillon 	char *snapshots_path;
1526a6e350fSMatthew Dillon 	char *config_path;
1536a6e350fSMatthew Dillon 	struct stat st;
1546a6e350fSMatthew Dillon 	char *cmd;
1556a6e350fSMatthew Dillon 	char *ptr;
1566a6e350fSMatthew Dillon 	int arg1;
1576a6e350fSMatthew Dillon 	int arg2;
1585e435c92SMatthew Dillon 	char *arg3;
1596a6e350fSMatthew Dillon 	time_t savet;
1606a6e350fSMatthew Dillon 	char buf[256];
16183f2a3aaSMatthew Dillon 	char *cbase;
16283f2a3aaSMatthew Dillon 	char *cptr;
1636a6e350fSMatthew Dillon 	FILE *fp;
164c453712aSMatthew Dillon 	struct didpfs *didpfs;
165c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
166c6c298a7SMatthew Dillon 	int prune_warning = 0;
16783f2a3aaSMatthew Dillon 	int new_config = 0;
168b5ec5ad4SMatthew Dillon 	int snapshots_from_pfs = 0;
1696a6e350fSMatthew Dillon 	int fd;
1706a6e350fSMatthew Dillon 	int r;
1710b8bd7daSMatthew Dillon 	int found_rebal = 0;
1726a6e350fSMatthew Dillon 
1736a6e350fSMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1746a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1756a6e350fSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
1766a6e350fSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
1776a6e350fSMatthew Dillon 	pfs.pfs_id = -1;
1786a6e350fSMatthew Dillon 
1796a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1806a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1816a6e350fSMatthew Dillon 	if (fd < 0) {
1826a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1836a6e350fSMatthew Dillon 		return;
1846a6e350fSMatthew Dillon 	}
18583f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
1866a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
18783f2a3aaSMatthew Dillon 		close(fd);
1886a6e350fSMatthew Dillon 		return;
1896a6e350fSMatthew Dillon 	}
1906a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1916a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
19283f2a3aaSMatthew Dillon 		close(fd);
1936a6e350fSMatthew Dillon 		return;
1946a6e350fSMatthew Dillon 	}
19583f2a3aaSMatthew Dillon 	bzero(&version, sizeof(version));
19683f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) {
19783f2a3aaSMatthew Dillon 		printf(" HAMMER filesystem but couldn't retrieve version!\n");
19883f2a3aaSMatthew Dillon 		close(fd);
19983f2a3aaSMatthew Dillon 		return;
20083f2a3aaSMatthew Dillon 	}
20183f2a3aaSMatthew Dillon 
20283f2a3aaSMatthew Dillon 	bzero(&config, sizeof(config));
20383f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
20483f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 &&
20583f2a3aaSMatthew Dillon 		    config.head.error == 0) {
20683f2a3aaSMatthew Dillon 			new_config = 1;
20783f2a3aaSMatthew Dillon 		}
20883f2a3aaSMatthew Dillon 	}
2096a6e350fSMatthew Dillon 
2106a6e350fSMatthew Dillon 	/*
2116a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
2126a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
2136a6e350fSMatthew Dillon 	 */
214c453712aSMatthew Dillon 	for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
215c453712aSMatthew Dillon 		if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
216bb8e52c0SThomas Nikolajsen 			printf(" PFS #%d already handled\n", pfs.pfs_id);
21783f2a3aaSMatthew Dillon 			close(fd);
2186a6e350fSMatthew Dillon 			return;
2196a6e350fSMatthew Dillon 		}
220c453712aSMatthew Dillon 	}
221c453712aSMatthew Dillon 	didpfs = malloc(sizeof(*didpfs));
222c453712aSMatthew Dillon 	didpfs->next = FirstPFS;
223c453712aSMatthew Dillon 	FirstPFS = didpfs;
224c453712aSMatthew Dillon 	didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
2256a6e350fSMatthew Dillon 
2266a6e350fSMatthew Dillon 	/*
227b5ec5ad4SMatthew Dillon 	 * Calculate the old snapshots directory for HAMMER VERSION < 3
228b5ec5ad4SMatthew Dillon 	 *
229b5ec5ad4SMatthew Dillon 	 * If the directory is explicitly specified in the PFS config
230b5ec5ad4SMatthew Dillon 	 * we flag it and will not migrate it later.
231ff1c9800SMatthew Dillon 	 */
232ff1c9800SMatthew Dillon 	if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
233ff1c9800SMatthew Dillon 		asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
234b5ec5ad4SMatthew Dillon 		snapshots_from_pfs = 1;
235ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
236ff1c9800SMatthew Dillon 		printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
23783f2a3aaSMatthew Dillon 		close(fd);
238ff1c9800SMatthew Dillon 		return;
239ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
240ff1c9800SMatthew Dillon 		printf(" WARNING: must configure snapshot dir for PFS slave\n");
241ff1c9800SMatthew Dillon 		printf("\tWe suggest <fs>/var/slaves/<name> where "
242ff1c9800SMatthew Dillon 		       "<fs> is the base HAMMER fs\n");
243bb8e52c0SThomas Nikolajsen 		printf("\tcontaining the slave\n");
24483f2a3aaSMatthew Dillon 		close(fd);
245ff1c9800SMatthew Dillon 		return;
246ff1c9800SMatthew Dillon 	} else {
247ff1c9800SMatthew Dillon 		asprintf(&snapshots_path,
248ff1c9800SMatthew Dillon 			 "%s%ssnapshots", path, dividing_slash(path));
249ff1c9800SMatthew Dillon 	}
250ff1c9800SMatthew Dillon 
251ff1c9800SMatthew Dillon 	/*
252b5ec5ad4SMatthew Dillon 	 * Check for old-style config file
2536a6e350fSMatthew Dillon 	 */
2546a6e350fSMatthew Dillon 	asprintf(&config_path, "%s/config", snapshots_path);
2556a6e350fSMatthew Dillon 	fp = fopen(config_path, "r");
25683f2a3aaSMatthew Dillon 
25783f2a3aaSMatthew Dillon 	/*
25883f2a3aaSMatthew Dillon 	 * Handle upgrades to hammer version 3, move the config
25983f2a3aaSMatthew Dillon 	 * file into meta-data.
26083f2a3aaSMatthew Dillon 	 *
26183f2a3aaSMatthew Dillon 	 * For the old config read the file into the config structure,
26283f2a3aaSMatthew Dillon 	 * we will parse it out of the config structure regardless.
26383f2a3aaSMatthew Dillon 	 */
26483f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
26583f2a3aaSMatthew Dillon 		if (fp) {
26683f2a3aaSMatthew Dillon 			printf("(migrating) ");
26783f2a3aaSMatthew Dillon 			fflush(stdout);
26883f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
26983f2a3aaSMatthew Dillon 			migrate_snapshots(fd, snapshots_path);
27083f2a3aaSMatthew Dillon 			fclose(fp);
27183f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
27283f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
27383f2a3aaSMatthew Dillon 				close(fd);
2746a6e350fSMatthew Dillon 				return;
2756a6e350fSMatthew Dillon 			}
27683f2a3aaSMatthew Dillon 			remove(config_path);
27783f2a3aaSMatthew Dillon 		} else if (new_config == 0) {
27883f2a3aaSMatthew Dillon 			config_init(path, &config);
27983f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
28083f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
28183f2a3aaSMatthew Dillon 				close(fd);
28283f2a3aaSMatthew Dillon 				return;
28383f2a3aaSMatthew Dillon 			}
28483f2a3aaSMatthew Dillon 		}
28583f2a3aaSMatthew Dillon 		new_config = 1;
28683f2a3aaSMatthew Dillon 	} else {
287b5ec5ad4SMatthew Dillon 		/*
288b5ec5ad4SMatthew Dillon 		 * Create missing snapshots directory for HAMMER VERSION < 3
289b5ec5ad4SMatthew Dillon 		 */
290b5ec5ad4SMatthew Dillon 		if (stat(snapshots_path, &st) < 0) {
291b5ec5ad4SMatthew Dillon 			if (mkdir(snapshots_path, 0755) != 0) {
292b5ec5ad4SMatthew Dillon 				free(snapshots_path);
293b5ec5ad4SMatthew Dillon 				printf(" unable to create snapshot dir \"%s\": %s\n",
294b5ec5ad4SMatthew Dillon 					snapshots_path, strerror(errno));
295b5ec5ad4SMatthew Dillon 				close(fd);
296b5ec5ad4SMatthew Dillon 				return;
297b5ec5ad4SMatthew Dillon 			}
298b5ec5ad4SMatthew Dillon 		}
299b5ec5ad4SMatthew Dillon 
300b5ec5ad4SMatthew Dillon 		/*
301b5ec5ad4SMatthew Dillon 		 *  Create missing config file for HAMMER VERSION < 3
302b5ec5ad4SMatthew Dillon 		 */
30383f2a3aaSMatthew Dillon 		if (fp == NULL) {
30483f2a3aaSMatthew Dillon 			config_init(path, &config);
30583f2a3aaSMatthew Dillon 			fp = fopen(config_path, "w");
30683f2a3aaSMatthew Dillon 			if (fp) {
30783f2a3aaSMatthew Dillon 				fwrite(config.config.text, 1,
30883f2a3aaSMatthew Dillon 					strlen(config.config.text), fp);
30983f2a3aaSMatthew Dillon 				fclose(fp);
31083f2a3aaSMatthew Dillon 			}
31183f2a3aaSMatthew Dillon 		} else {
31283f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
31383f2a3aaSMatthew Dillon 			fclose(fp);
31483f2a3aaSMatthew Dillon 		}
31583f2a3aaSMatthew Dillon 	}
3166a6e350fSMatthew Dillon 
317b5ec5ad4SMatthew Dillon 	/*
318b5ec5ad4SMatthew Dillon 	 * If snapshots_from_pfs is not set we calculate the new snapshots
319b5ec5ad4SMatthew Dillon 	 * directory default (in /var) for HAMMER VERSION >= 3 and migrate
320b5ec5ad4SMatthew Dillon 	 * the old snapshots directory over.
321b5ec5ad4SMatthew Dillon 	 *
322b5ec5ad4SMatthew Dillon 	 * People who have set an explicit snapshots directory will have
323b5ec5ad4SMatthew Dillon 	 * to migrate the data manually into /var/hammer, or not bother at
324b5ec5ad4SMatthew Dillon 	 * all.  People running slaves may wish to migrate it and then
325b5ec5ad4SMatthew Dillon 	 * clear the snapshots specification in the PFS config for the
326b5ec5ad4SMatthew Dillon 	 * slave.
327b5ec5ad4SMatthew Dillon 	 */
328b5ec5ad4SMatthew Dillon 	if (new_config && snapshots_from_pfs == 0) {
329b5ec5ad4SMatthew Dillon 		char *npath;
330b5ec5ad4SMatthew Dillon 
331b5ec5ad4SMatthew Dillon 		assert(path[0] == '/');
332b5ec5ad4SMatthew Dillon 		if (strcmp(path, "/") == 0)
333b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/root", SNAPSHOTS_BASE);
334b5ec5ad4SMatthew Dillon 		else
335b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1);
336b5ec5ad4SMatthew Dillon 		if (stat(npath, &st) < 0 && errno == ENOENT) {
337b5ec5ad4SMatthew Dillon 			if (stat(snapshots_path, &st) < 0 && errno == ENOENT) {
338b5ec5ad4SMatthew Dillon 				printf(" HAMMER UPGRADE: Creating snapshots\n"
339b5ec5ad4SMatthew Dillon 				       "\tCreating snapshots in %s\n",
340b5ec5ad4SMatthew Dillon 				       npath);
341b5ec5ad4SMatthew Dillon 				runcmd(&r, "mkdir -p %s", npath);
342b5ec5ad4SMatthew Dillon 			} else {
343b5ec5ad4SMatthew Dillon 				printf(" HAMMER UPGRADE: Moving snapshots\n"
344b5ec5ad4SMatthew Dillon 				       "\tMoving snapshots from %s to %s\n",
345b5ec5ad4SMatthew Dillon 				       snapshots_path, npath);
346b5ec5ad4SMatthew Dillon 				runcmd(&r, "mkdir -p %s", npath);
347b5ec5ad4SMatthew Dillon 				runcmd(&r, "cpdup %s %s", snapshots_path, npath);
348b5ec5ad4SMatthew Dillon 				if (r != 0) {
349b5ec5ad4SMatthew Dillon 			    printf("Unable to move snapshots directory!\n");
350b5ec5ad4SMatthew Dillon 			    printf("Please fix this critical error.\n");
351b5ec5ad4SMatthew Dillon 			    printf("Aborting cleanup of %s\n", path);
352b5ec5ad4SMatthew Dillon 					close(fd);
353b5ec5ad4SMatthew Dillon 					return;
354b5ec5ad4SMatthew Dillon 				}
355b5ec5ad4SMatthew Dillon 				runcmd(&r, "rm -rf %s", snapshots_path);
356b5ec5ad4SMatthew Dillon 			}
357b5ec5ad4SMatthew Dillon 		}
358b5ec5ad4SMatthew Dillon 		free(snapshots_path);
359b5ec5ad4SMatthew Dillon 		snapshots_path = npath;
360b5ec5ad4SMatthew Dillon 	}
361b5ec5ad4SMatthew Dillon 
362b5ec5ad4SMatthew Dillon 	/*
363b5ec5ad4SMatthew Dillon 	 * Lock the PFS.  fd is the base directory of the mounted PFS.
364b5ec5ad4SMatthew Dillon 	 */
36583f2a3aaSMatthew Dillon 	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
3665bd5f172SSimon Schubert 		if (errno == EWOULDBLOCK)
3675bd5f172SSimon Schubert 			printf(" PFS #%d locked by other process\n", pfs.pfs_id);
3685bd5f172SSimon Schubert 		else
3695bd5f172SSimon Schubert 			printf(" can not lock %s: %s\n", config_path, strerror(errno));
37083f2a3aaSMatthew Dillon 		close(fd);
3715bd5f172SSimon Schubert 		return;
3725bd5f172SSimon Schubert 	}
3735bd5f172SSimon Schubert 
374ff1c9800SMatthew Dillon 	printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
3756a6e350fSMatthew Dillon 
3766a6e350fSMatthew Dillon 	/*
3776a6e350fSMatthew Dillon 	 * Process the config file
3786a6e350fSMatthew Dillon 	 */
37983f2a3aaSMatthew Dillon 	cbase = config.config.text;
38083f2a3aaSMatthew Dillon 
38183f2a3aaSMatthew Dillon 	while ((cptr = strchr(cbase, '\n')) != NULL) {
38283f2a3aaSMatthew Dillon 		bcopy(cbase, buf, cptr - cbase);
38383f2a3aaSMatthew Dillon 		buf[cptr - cbase] = 0;
38483f2a3aaSMatthew Dillon 		cbase = cptr + 1;
38583f2a3aaSMatthew Dillon 
3866a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
3876a6e350fSMatthew Dillon 		arg1 = 0;
3886a6e350fSMatthew Dillon 		arg2 = 0;
3895e435c92SMatthew Dillon 		arg3 = NULL;
3906a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
3916a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
3925e435c92SMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL) {
3936a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
3945e435c92SMatthew Dillon 				arg3 = strtok(NULL, WS);
3955e435c92SMatthew Dillon 			}
3966a6e350fSMatthew Dillon 		}
3976a6e350fSMatthew Dillon 
3986a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
3996a6e350fSMatthew Dillon 		fflush(stdout);
4006a6e350fSMatthew Dillon 
4016a6e350fSMatthew Dillon 		r = 1;
4026a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
4035e435c92SMatthew Dillon 			if (arg1 == 0) {
40483f2a3aaSMatthew Dillon 				if (arg2 &&
40583f2a3aaSMatthew Dillon 				    check_softlinks(fd, new_config,
40683f2a3aaSMatthew Dillon 						    snapshots_path)) {
4075e435c92SMatthew Dillon 					printf("only removing old snapshots\n");
4085e435c92SMatthew Dillon 					prune_warning = 1;
40983f2a3aaSMatthew Dillon 					cleanup_softlinks(fd, new_config,
41083f2a3aaSMatthew Dillon 							  snapshots_path,
4115e435c92SMatthew Dillon 							  arg2, arg3);
4125e435c92SMatthew Dillon 				} else {
4135e435c92SMatthew Dillon 					printf("disabled\n");
4145e435c92SMatthew Dillon 					snapshots_disabled = 1;
4155e435c92SMatthew Dillon 				}
4165e435c92SMatthew Dillon 			} else
4176a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4186a6e350fSMatthew Dillon 				printf("run\n");
41983f2a3aaSMatthew Dillon 				cleanup_softlinks(fd, new_config,
42083f2a3aaSMatthew Dillon 						  snapshots_path,
4215e435c92SMatthew Dillon 						  arg2, arg3);
422b5ec5ad4SMatthew Dillon 				r = create_snapshot(path, snapshots_path);
4236a6e350fSMatthew Dillon 			} else {
4246a6e350fSMatthew Dillon 				printf("skip\n");
4256a6e350fSMatthew Dillon 			}
4265e435c92SMatthew Dillon 		} else if (arg1 == 0) {
4275e435c92SMatthew Dillon 			/*
4285e435c92SMatthew Dillon 			 * The commands following this check can't handle
4295e435c92SMatthew Dillon 			 * a period of 0, so call the feature disabled and
4305e435c92SMatthew Dillon 			 * ignore the directive.
4315e435c92SMatthew Dillon 			 */
4325e435c92SMatthew Dillon 			printf("disabled\n");
4336a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
4346a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4355e435c92SMatthew Dillon 				if (prune_warning) {
4365e435c92SMatthew Dillon 					printf("run - WARNING snapshot "
4375e435c92SMatthew Dillon 					       "softlinks present "
438226f3799SThomas Nikolajsen 					       "but snapshots disabled\n");
4395e435c92SMatthew Dillon 				} else {
440c6c298a7SMatthew Dillon 					printf("run\n");
4415e435c92SMatthew Dillon 				}
4426a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
443c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
4446a6e350fSMatthew Dillon 			} else {
4456a6e350fSMatthew Dillon 				printf("skip\n");
4466a6e350fSMatthew Dillon 			}
4470b8bd7daSMatthew Dillon 		} else if (strcmp(cmd, "rebalance") == 0) {
4480b8bd7daSMatthew Dillon 			found_rebal = 1;
4490b8bd7daSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4500b8bd7daSMatthew Dillon 				printf("run");
4510b8bd7daSMatthew Dillon 				fflush(stdout);
4520b8bd7daSMatthew Dillon 				if (VerboseOpt)
4530b8bd7daSMatthew Dillon 					printf("\n");
4540b8bd7daSMatthew Dillon 				r = cleanup_rebalance(path, snapshots_path,
4550b8bd7daSMatthew Dillon 						arg1, arg2);
4560b8bd7daSMatthew Dillon 			} else {
4570b8bd7daSMatthew Dillon 				printf("skip\n");
4580b8bd7daSMatthew Dillon 			}
4596a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
4606a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4616a6e350fSMatthew Dillon 				printf("run");
4626a6e350fSMatthew Dillon 				fflush(stdout);
4636a6e350fSMatthew Dillon 				if (VerboseOpt)
4646a6e350fSMatthew Dillon 					printf("\n");
4656a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
4666a6e350fSMatthew Dillon 						arg1, arg2);
4676a6e350fSMatthew Dillon 			} else {
4686a6e350fSMatthew Dillon 				printf("skip\n");
4696a6e350fSMatthew Dillon 			}
4706a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
4716a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4726a6e350fSMatthew Dillon 				printf("run");
4736a6e350fSMatthew Dillon 				fflush(stdout);
4746a6e350fSMatthew Dillon 				if (VerboseOpt)
4756a6e350fSMatthew Dillon 					printf("\n");
4766a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
4776a6e350fSMatthew Dillon 					       arg1, arg2);
4786a6e350fSMatthew Dillon 			} else {
4796a6e350fSMatthew Dillon 				printf("skip\n");
4806a6e350fSMatthew Dillon 			}
4816a6e350fSMatthew Dillon 		} else {
4826a6e350fSMatthew Dillon 			printf("unknown directive\n");
4836a6e350fSMatthew Dillon 			r = 1;
4846a6e350fSMatthew Dillon 		}
4856a6e350fSMatthew Dillon 		if (r == 0)
4866a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
4876a6e350fSMatthew Dillon 	}
4880b8bd7daSMatthew Dillon 
4890b8bd7daSMatthew Dillon 	/*
49083f2a3aaSMatthew Dillon 	 * Add new rebalance feature if the config doesn't have it.
491b5ec5ad4SMatthew Dillon 	 * (old style config only).
4920b8bd7daSMatthew Dillon 	 */
49383f2a3aaSMatthew Dillon 	if (new_config == 0 && found_rebal == 0) {
4940b8bd7daSMatthew Dillon 		if ((fp = fopen(config_path, "r+")) != NULL) {
4950b8bd7daSMatthew Dillon 			fseek(fp, 0L, 2);
4960b8bd7daSMatthew Dillon 			fprintf(fp, "rebalance 1d 5m\n");
4970b8bd7daSMatthew Dillon 			fclose(fp);
4980b8bd7daSMatthew Dillon 		}
4990b8bd7daSMatthew Dillon 	}
50083f2a3aaSMatthew Dillon 
50183f2a3aaSMatthew Dillon 	/*
50283f2a3aaSMatthew Dillon 	 * Cleanup, and delay a little
50383f2a3aaSMatthew Dillon 	 */
50483f2a3aaSMatthew Dillon 	close(fd);
5056a6e350fSMatthew Dillon 	usleep(1000);
5066a6e350fSMatthew Dillon }
5076a6e350fSMatthew Dillon 
50883f2a3aaSMatthew Dillon /*
50983f2a3aaSMatthew Dillon  * Initialize new config data (new or old style)
51083f2a3aaSMatthew Dillon  */
51183f2a3aaSMatthew Dillon static void
51283f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config)
51383f2a3aaSMatthew Dillon {
51483f2a3aaSMatthew Dillon 	const char *snapshots;
51583f2a3aaSMatthew Dillon 
51683f2a3aaSMatthew Dillon 	if (strcmp(path, "/tmp") == 0 ||
51783f2a3aaSMatthew Dillon 	    strcmp(path, "/var/tmp") == 0 ||
51883f2a3aaSMatthew Dillon 	    strcmp(path, "/usr/obj") == 0) {
51983f2a3aaSMatthew Dillon 		snapshots = "snapshots 0d 0d\n";
52083f2a3aaSMatthew Dillon 	} else {
52183f2a3aaSMatthew Dillon 		snapshots = "snapshots 1d 60d\n";
52283f2a3aaSMatthew Dillon 	}
52383f2a3aaSMatthew Dillon 	bzero(config->config.text, sizeof(config->config.text));
52483f2a3aaSMatthew Dillon 	snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s",
52583f2a3aaSMatthew Dillon 		snapshots,
52683f2a3aaSMatthew Dillon 		"prune     1d 5m\n"
52783f2a3aaSMatthew Dillon 		"rebalance 1d 5m\n"
52883f2a3aaSMatthew Dillon 		"reblock   1d 5m\n"
52983f2a3aaSMatthew Dillon 		"recopy    30d 10m\n"
53083f2a3aaSMatthew Dillon 		"rebalance 1d 5m\n");
53183f2a3aaSMatthew Dillon }
53283f2a3aaSMatthew Dillon 
53383f2a3aaSMatthew Dillon /*
53483f2a3aaSMatthew Dillon  * Migrate configuration data from the old snapshots/config
53583f2a3aaSMatthew Dillon  * file to the new mata-data format.
53683f2a3aaSMatthew Dillon  */
53783f2a3aaSMatthew Dillon static void
53883f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config)
53983f2a3aaSMatthew Dillon {
54083f2a3aaSMatthew Dillon 	int n;
54183f2a3aaSMatthew Dillon 
54283f2a3aaSMatthew Dillon 	n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp);
54383f2a3aaSMatthew Dillon 	if (n >= 0)
54483f2a3aaSMatthew Dillon 		bzero(config->config.text + n, sizeof(config->config.text) - n);
54583f2a3aaSMatthew Dillon }
54683f2a3aaSMatthew Dillon 
54783f2a3aaSMatthew Dillon /*
54883f2a3aaSMatthew Dillon  * Migrate snapshot softlinks in the snapshots directory to the
54983f2a3aaSMatthew Dillon  * new meta-data format.  The softlinks are left intact, but
55083f2a3aaSMatthew Dillon  * this way the pruning code won't lose track of them if you
55183f2a3aaSMatthew Dillon  * happen to blow away the snapshots directory.
55283f2a3aaSMatthew Dillon  */
55383f2a3aaSMatthew Dillon static void
55483f2a3aaSMatthew Dillon migrate_snapshots(int fd, const char *snapshots_path)
55583f2a3aaSMatthew Dillon {
55683f2a3aaSMatthew Dillon 	struct hammer_ioc_snapshot snapshot;
55783f2a3aaSMatthew Dillon 	struct dirent *den;
55883f2a3aaSMatthew Dillon 	struct stat st;
55983f2a3aaSMatthew Dillon 	DIR *dir;
56083f2a3aaSMatthew Dillon 	char *fpath;
56183f2a3aaSMatthew Dillon 
56283f2a3aaSMatthew Dillon 	bzero(&snapshot, sizeof(snapshot));
56383f2a3aaSMatthew Dillon 
56483f2a3aaSMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
56583f2a3aaSMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
56683f2a3aaSMatthew Dillon 			if (den->d_name[0] == '.')
56783f2a3aaSMatthew Dillon 				continue;
56883f2a3aaSMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
56983f2a3aaSMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) {
57083f2a3aaSMatthew Dillon 				migrate_one_snapshot(fd, fpath, &snapshot);
57183f2a3aaSMatthew Dillon 			}
57283f2a3aaSMatthew Dillon 			free(fpath);
57383f2a3aaSMatthew Dillon 		}
57483f2a3aaSMatthew Dillon 		closedir(dir);
57583f2a3aaSMatthew Dillon 	}
57683f2a3aaSMatthew Dillon 	migrate_one_snapshot(fd, NULL, &snapshot);
57783f2a3aaSMatthew Dillon 
57883f2a3aaSMatthew Dillon }
57983f2a3aaSMatthew Dillon 
58083f2a3aaSMatthew Dillon /*
58183f2a3aaSMatthew Dillon  * Migrate a single snapshot.  If fpath is NULL the ioctl is flushed,
58283f2a3aaSMatthew Dillon  * otherwise it is flushed when it fills up.
58383f2a3aaSMatthew Dillon  */
58483f2a3aaSMatthew Dillon static void
58583f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath,
58683f2a3aaSMatthew Dillon 		     struct hammer_ioc_snapshot *snapshot)
58783f2a3aaSMatthew Dillon {
58883f2a3aaSMatthew Dillon 	if (fpath) {
58983f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *snap;
59083f2a3aaSMatthew Dillon 		struct tm tm;
59183f2a3aaSMatthew Dillon 		time_t t;
59283f2a3aaSMatthew Dillon 		int year;
59383f2a3aaSMatthew Dillon 		int month;
59483f2a3aaSMatthew Dillon 		int day = 0;
59583f2a3aaSMatthew Dillon 		int hour = 0;
59683f2a3aaSMatthew Dillon 		int minute = 0;
59783f2a3aaSMatthew Dillon 		int r;
59883f2a3aaSMatthew Dillon 		char linkbuf[1024];
59983f2a3aaSMatthew Dillon 		const char *ptr;
60083f2a3aaSMatthew Dillon 		hammer_tid_t tid;
60183f2a3aaSMatthew Dillon 
60283f2a3aaSMatthew Dillon 		t = (time_t)-1;
60383f2a3aaSMatthew Dillon 		tid = (hammer_tid_t)(int64_t)-1;
60483f2a3aaSMatthew Dillon 
60583f2a3aaSMatthew Dillon 		ptr = fpath;
60683f2a3aaSMatthew Dillon 		while (*ptr && *ptr != '-' && *ptr != '.')
60783f2a3aaSMatthew Dillon 			++ptr;
60883f2a3aaSMatthew Dillon 		if (*ptr)
60983f2a3aaSMatthew Dillon 			++ptr;
61083f2a3aaSMatthew Dillon 		r = sscanf(ptr, "%4d%2d%2d-%2d%2d",
61183f2a3aaSMatthew Dillon 			   &year, &month, &day, &hour, &minute);
61283f2a3aaSMatthew Dillon 
61383f2a3aaSMatthew Dillon 		if (r >= 3) {
61483f2a3aaSMatthew Dillon 			bzero(&tm, sizeof(tm));
61583f2a3aaSMatthew Dillon 			tm.tm_isdst = -1;
61683f2a3aaSMatthew Dillon 			tm.tm_min = minute;
61783f2a3aaSMatthew Dillon 			tm.tm_hour = hour;
61883f2a3aaSMatthew Dillon 			tm.tm_mday = day;
61983f2a3aaSMatthew Dillon 			tm.tm_mon = month - 1;
62083f2a3aaSMatthew Dillon 			tm.tm_year = year - 1900;
62183f2a3aaSMatthew Dillon 			t = mktime(&tm);
62283f2a3aaSMatthew Dillon 		}
62383f2a3aaSMatthew Dillon 		bzero(linkbuf, sizeof(linkbuf));
62483f2a3aaSMatthew Dillon 		if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 &&
62583f2a3aaSMatthew Dillon 		    (ptr = strrchr(linkbuf, '@')) != NULL &&
62683f2a3aaSMatthew Dillon 		    ptr > linkbuf && ptr[-1] == '@') {
62783f2a3aaSMatthew Dillon 			tid = strtoull(ptr + 1, NULL, 16);
62883f2a3aaSMatthew Dillon 		}
62983f2a3aaSMatthew Dillon 		if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) {
63083f2a3aaSMatthew Dillon 			snap = &snapshot->snaps[snapshot->count];
63183f2a3aaSMatthew Dillon 			bzero(snap, sizeof(*snap));
63283f2a3aaSMatthew Dillon 			snap->tid = tid;
63383f2a3aaSMatthew Dillon 			snap->ts = (u_int64_t)t * 1000000ULL;
63483f2a3aaSMatthew Dillon 			snprintf(snap->label, sizeof(snap->label),
63583f2a3aaSMatthew Dillon 				 "migrated");
63683f2a3aaSMatthew Dillon 			++snapshot->count;
63783f2a3aaSMatthew Dillon 		}
63883f2a3aaSMatthew Dillon 	}
63983f2a3aaSMatthew Dillon 
64083f2a3aaSMatthew Dillon 	if ((fpath == NULL && snapshot->count) ||
64183f2a3aaSMatthew Dillon 	    snapshot->count == HAMMER_SNAPS_PER_IOCTL) {
64283f2a3aaSMatthew Dillon 		printf(" (%d snapshots)", snapshot->count);
64383f2a3aaSMatthew Dillon again:
64483f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) {
64583f2a3aaSMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
64683f2a3aaSMatthew Dillon 			       strerror(errno));
64783f2a3aaSMatthew Dillon 		} else if (snapshot->head.error == EALREADY) {
64883f2a3aaSMatthew Dillon 			++snapshot->index;
64983f2a3aaSMatthew Dillon 			goto again;
65083f2a3aaSMatthew Dillon 		} else if (snapshot->head.error) {
651*068da2c2SMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
65283f2a3aaSMatthew Dillon 			       strerror(snapshot->head.error));
65383f2a3aaSMatthew Dillon 		}
65483f2a3aaSMatthew Dillon 		printf("index %d\n", snapshot->index);
65583f2a3aaSMatthew Dillon 		snapshot->index = 0;
65683f2a3aaSMatthew Dillon 		snapshot->count = 0;
65783f2a3aaSMatthew Dillon 		snapshot->head.error = 0;
65883f2a3aaSMatthew Dillon 	}
65983f2a3aaSMatthew Dillon }
66083f2a3aaSMatthew Dillon 
6616a6e350fSMatthew Dillon static
6626a6e350fSMatthew Dillon int
6636a6e350fSMatthew Dillon strtosecs(char *ptr)
6646a6e350fSMatthew Dillon {
6656a6e350fSMatthew Dillon 	int val;
6666a6e350fSMatthew Dillon 
6676a6e350fSMatthew Dillon 	val = strtol(ptr, &ptr, 0);
6686a6e350fSMatthew Dillon 	switch(*ptr) {
6696a6e350fSMatthew Dillon 	case 'd':
6706a6e350fSMatthew Dillon 		val *= 24;
6716a6e350fSMatthew Dillon 		/* fall through */
6726a6e350fSMatthew Dillon 	case 'h':
6736a6e350fSMatthew Dillon 		val *= 60;
6746a6e350fSMatthew Dillon 		/* fall through */
6756a6e350fSMatthew Dillon 	case 'm':
6766a6e350fSMatthew Dillon 		val *= 60;
6776a6e350fSMatthew Dillon 		/* fall through */
6786a6e350fSMatthew Dillon 	case 's':
6796a6e350fSMatthew Dillon 		break;
6806a6e350fSMatthew Dillon 	default:
6816a6e350fSMatthew Dillon 		errx(1, "illegal suffix converting %s\n", ptr);
6826a6e350fSMatthew Dillon 		break;
6836a6e350fSMatthew Dillon 	}
6846a6e350fSMatthew Dillon 	return(val);
6856a6e350fSMatthew Dillon }
6866a6e350fSMatthew Dillon 
6876a6e350fSMatthew Dillon static const char *
6886a6e350fSMatthew Dillon dividing_slash(const char *path)
6896a6e350fSMatthew Dillon {
6906a6e350fSMatthew Dillon 	int len = strlen(path);
6916a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
6926a6e350fSMatthew Dillon 		return("");
6936a6e350fSMatthew Dillon 	else
6946a6e350fSMatthew Dillon 		return("/");
6956a6e350fSMatthew Dillon }
6966a6e350fSMatthew Dillon 
6976a6e350fSMatthew Dillon /*
6986a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
6996a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
7006a6e350fSMatthew Dillon  * current time_t so we can save it later on.
7016a6e350fSMatthew Dillon  *
7026a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
7036a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
7046a6e350fSMatthew Dillon  * of how close the last operation actually was.
7056a6e350fSMatthew Dillon  */
7066a6e350fSMatthew Dillon static int
7076a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
7086a6e350fSMatthew Dillon 	time_t *savep)
7096a6e350fSMatthew Dillon {
7106a6e350fSMatthew Dillon 	char *check_path;
7116a6e350fSMatthew Dillon 	struct tm tp1;
7126a6e350fSMatthew Dillon 	struct tm tp2;
7136a6e350fSMatthew Dillon 	FILE *fp;
7146a6e350fSMatthew Dillon 	time_t baset, lastt;
7156a6e350fSMatthew Dillon 	char buf[256];
7166a6e350fSMatthew Dillon 
7176a6e350fSMatthew Dillon 	time(savep);
7186a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
7196a6e350fSMatthew Dillon 
7206a6e350fSMatthew Dillon 	/*
7216a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
7226a6e350fSMatthew Dillon 	 */
7236a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
7246a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
7256a6e350fSMatthew Dillon 	free(check_path);
7266a6e350fSMatthew Dillon 	if (fp == NULL)
7276a6e350fSMatthew Dillon 		return(1);
7286a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
7296a6e350fSMatthew Dillon 		fclose(fp);
7306a6e350fSMatthew Dillon 		return(1);
7316a6e350fSMatthew Dillon 	}
7326a6e350fSMatthew Dillon 	fclose(fp);
7336a6e350fSMatthew Dillon 
7346a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
7356a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
7366a6e350fSMatthew Dillon 
7376a6e350fSMatthew Dillon 	/*
7386a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
7396a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
7406a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
7416a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
7426a6e350fSMatthew Dillon 	 */
7436a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
7446a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
7456a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
7466a6e350fSMatthew Dillon 	}
7476a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
7486a6e350fSMatthew Dillon 		tp1.tm_min = 0;
7496a6e350fSMatthew Dillon 		tp2.tm_min = 0;
7506a6e350fSMatthew Dillon 	}
7516a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
7526a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
7536a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
7546a6e350fSMatthew Dillon 	}
7556a6e350fSMatthew Dillon 
7566a6e350fSMatthew Dillon 	baset = mktime(&tp1);
7576a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
7586a6e350fSMatthew Dillon 
7596a6e350fSMatthew Dillon #if 0
7606a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
7616a6e350fSMatthew Dillon #endif
7626a6e350fSMatthew Dillon 
7636a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
7646a6e350fSMatthew Dillon 		return(1);
7656a6e350fSMatthew Dillon 	return(0);
7666a6e350fSMatthew Dillon }
7676a6e350fSMatthew Dillon 
7686a6e350fSMatthew Dillon /*
7696a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
7706a6e350fSMatthew Dillon  */
7716a6e350fSMatthew Dillon static void
7726a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
7736a6e350fSMatthew Dillon 			time_t savet)
7746a6e350fSMatthew Dillon {
7756a6e350fSMatthew Dillon 	char *ocheck_path;
7766a6e350fSMatthew Dillon 	char *ncheck_path;
7776a6e350fSMatthew Dillon 	FILE *fp;
7786a6e350fSMatthew Dillon 
7796a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
7806a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
7816a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
7823b9fbdeaSMatthew Dillon 	if (fp) {
7836a6e350fSMatthew Dillon 		fprintf(fp, "0x%08llx\n", (long long)savet);
7846a6e350fSMatthew Dillon 		if (fclose(fp) == 0)
7856a6e350fSMatthew Dillon 			rename(ncheck_path, ocheck_path);
7866a6e350fSMatthew Dillon 		remove(ncheck_path);
7873b9fbdeaSMatthew Dillon 	} else {
7883b9fbdeaSMatthew Dillon 		fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
7893b9fbdeaSMatthew Dillon 			ncheck_path, strerror(errno));
7903b9fbdeaSMatthew Dillon 	}
7916a6e350fSMatthew Dillon }
7926a6e350fSMatthew Dillon 
793ff1c9800SMatthew Dillon /*
794ff1c9800SMatthew Dillon  * Simply count the number of softlinks in the snapshots dir
795ff1c9800SMatthew Dillon  */
796c6c298a7SMatthew Dillon static int
79783f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path)
798c6c298a7SMatthew Dillon {
799c6c298a7SMatthew Dillon 	struct dirent *den;
800c6c298a7SMatthew Dillon 	struct stat st;
801c6c298a7SMatthew Dillon 	DIR *dir;
802c6c298a7SMatthew Dillon 	char *fpath;
803c6c298a7SMatthew Dillon 	int res = 0;
804c6c298a7SMatthew Dillon 
80583f2a3aaSMatthew Dillon 	/*
80683f2a3aaSMatthew Dillon 	 * Old-style softlink-based snapshots
80783f2a3aaSMatthew Dillon 	 */
808c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
809c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
810c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
811c6c298a7SMatthew Dillon 				continue;
812c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
813c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
814c6c298a7SMatthew Dillon 				++res;
815c6c298a7SMatthew Dillon 			free(fpath);
816c6c298a7SMatthew Dillon 		}
817c6c298a7SMatthew Dillon 		closedir(dir);
818c6c298a7SMatthew Dillon 	}
81983f2a3aaSMatthew Dillon 
82083f2a3aaSMatthew Dillon 	/*
82183f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
82283f2a3aaSMatthew Dillon 	 * count those too.
82383f2a3aaSMatthew Dillon 	 */
82483f2a3aaSMatthew Dillon 	if (new_config) {
82583f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
82683f2a3aaSMatthew Dillon 
82783f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
82883f2a3aaSMatthew Dillon 		do {
82983f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
83083f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
83183f2a3aaSMatthew Dillon 					"snapshot error");
83283f2a3aaSMatthew Dillon 				/* not reached */
83383f2a3aaSMatthew Dillon 			}
83483f2a3aaSMatthew Dillon 			res += snapshot.count;
83583f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
83683f2a3aaSMatthew Dillon 	}
837c6c298a7SMatthew Dillon 	return (res);
838c6c298a7SMatthew Dillon }
839c6c298a7SMatthew Dillon 
8406a6e350fSMatthew Dillon /*
841ff1c9800SMatthew Dillon  * Clean up expired softlinks in the snapshots dir
842ff1c9800SMatthew Dillon  */
843ff1c9800SMatthew Dillon static void
84483f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config,
84583f2a3aaSMatthew Dillon 		  const char *snapshots_path, int arg2, char *arg3)
846ff1c9800SMatthew Dillon {
847ff1c9800SMatthew Dillon 	struct dirent *den;
848ff1c9800SMatthew Dillon 	struct stat st;
849ff1c9800SMatthew Dillon 	DIR *dir;
850ff1c9800SMatthew Dillon 	char *fpath;
8515e435c92SMatthew Dillon 	int anylink = 0;
8525e435c92SMatthew Dillon 
85309e1b0d6SMatthew Dillon 	if (arg3 != NULL && strstr(arg3, "any") != NULL)
8545e435c92SMatthew Dillon 		anylink = 1;
855ff1c9800SMatthew Dillon 
856ff1c9800SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
857ff1c9800SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
858ff1c9800SMatthew Dillon 			if (den->d_name[0] == '.')
859ff1c9800SMatthew Dillon 				continue;
860ff1c9800SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
861ff1c9800SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
8625e435c92SMatthew Dillon 			    (anylink || strncmp(den->d_name, "snap-", 5) == 0)
8635e435c92SMatthew Dillon 			) {
864ff1c9800SMatthew Dillon 				if (check_expired(den->d_name, arg2)) {
865ff1c9800SMatthew Dillon 					if (VerboseOpt) {
866ff1c9800SMatthew Dillon 						printf("    expire %s\n",
867ff1c9800SMatthew Dillon 							fpath);
868ff1c9800SMatthew Dillon 					}
869ff1c9800SMatthew Dillon 					remove(fpath);
870ff1c9800SMatthew Dillon 				}
871ff1c9800SMatthew Dillon 			}
872ff1c9800SMatthew Dillon 			free(fpath);
873ff1c9800SMatthew Dillon 		}
874ff1c9800SMatthew Dillon 		closedir(dir);
875ff1c9800SMatthew Dillon 	}
87683f2a3aaSMatthew Dillon 
87783f2a3aaSMatthew Dillon 	/*
87883f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
87983f2a3aaSMatthew Dillon 	 * count those too.
88083f2a3aaSMatthew Dillon 	 */
88183f2a3aaSMatthew Dillon 	if (new_config) {
88283f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
88383f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot dsnapshot;
88483f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *snap;
88583f2a3aaSMatthew Dillon 		struct tm *tp;
88683f2a3aaSMatthew Dillon 		time_t t;
88783f2a3aaSMatthew Dillon 		char snapts[32];
88883f2a3aaSMatthew Dillon 		u_int32_t i;
88983f2a3aaSMatthew Dillon 
89083f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
89183f2a3aaSMatthew Dillon 		bzero(&dsnapshot, sizeof(dsnapshot));
89283f2a3aaSMatthew Dillon 		do {
89383f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
89483f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
89583f2a3aaSMatthew Dillon 					"snapshot error");
89683f2a3aaSMatthew Dillon 				/* not reached */
89783f2a3aaSMatthew Dillon 			}
89883f2a3aaSMatthew Dillon 			for (i = 0; i < snapshot.count; ++i) {
89983f2a3aaSMatthew Dillon 				snap = &snapshot.snaps[i];
90083f2a3aaSMatthew Dillon 				t = time(NULL) - snap->ts / 1000000ULL;
901*068da2c2SMatthew Dillon 				if ((int)t > arg2 && snap->tid != 0) {
90283f2a3aaSMatthew Dillon 					dsnapshot.snaps[dsnapshot.count++] =
90383f2a3aaSMatthew Dillon 						*snap;
90483f2a3aaSMatthew Dillon 				}
90583f2a3aaSMatthew Dillon 				if ((int)t > arg2 && VerboseOpt) {
90683f2a3aaSMatthew Dillon 					tp = localtime(&t);
90783f2a3aaSMatthew Dillon 					strftime(snapts, sizeof(snapts),
90883f2a3aaSMatthew Dillon 						 "%Y-%m-%d %H:%M:%S %Z", tp);
90983f2a3aaSMatthew Dillon 					printf("    expire 0x%016jx %s %s\n",
91083f2a3aaSMatthew Dillon 					       (uintmax_t)snap->tid,
91183f2a3aaSMatthew Dillon 					       snapts,
91283f2a3aaSMatthew Dillon 					       snap->label);
91383f2a3aaSMatthew Dillon 				}
91483f2a3aaSMatthew Dillon 				if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL) {
91583f2a3aaSMatthew Dillon 					if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) {
916*068da2c2SMatthew Dillon 						printf("    Ioctl to delete snapshots failed: %s index %d\n", strerror(errno), dsnapshot.index);
91783f2a3aaSMatthew Dillon 					} else if (dsnapshot.head.error) {
91883f2a3aaSMatthew Dillon 						printf("    Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error));
919*068da2c2SMatthew Dillon 						exit(1);
92083f2a3aaSMatthew Dillon 					}
921*068da2c2SMatthew Dillon 					dsnapshot.index = 0;
92283f2a3aaSMatthew Dillon 					dsnapshot.count = 0;
92383f2a3aaSMatthew Dillon 					dsnapshot.head.error = 0;
92483f2a3aaSMatthew Dillon 				}
92583f2a3aaSMatthew Dillon 			}
92683f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
92783f2a3aaSMatthew Dillon 
92883f2a3aaSMatthew Dillon 		if (dsnapshot.count) {
92983f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, &dsnapshot) < 0) {
93083f2a3aaSMatthew Dillon 				printf("    Ioctl to delete snapshots failed: %s\n", strerror(errno));
93183f2a3aaSMatthew Dillon 			} else if (dsnapshot.head.error) {
93283f2a3aaSMatthew Dillon 				printf("    Ioctl to delete snapshots failed: %s\n", strerror(dsnapshot.head.error));
93383f2a3aaSMatthew Dillon 			}
93483f2a3aaSMatthew Dillon 			dsnapshot.count = 0;
935*068da2c2SMatthew Dillon 			dsnapshot.index = 0;
93683f2a3aaSMatthew Dillon 			dsnapshot.head.error = 0;
93783f2a3aaSMatthew Dillon 		}
93883f2a3aaSMatthew Dillon 	}
939ff1c9800SMatthew Dillon }
940ff1c9800SMatthew Dillon 
941ff1c9800SMatthew Dillon /*
942ff1c9800SMatthew Dillon  * Take a softlink path in the form snap-yyyymmdd-hhmm and the
943ff1c9800SMatthew Dillon  * expiration in seconds (arg2) and return non-zero if the softlink
944ff1c9800SMatthew Dillon  * has expired.
945ff1c9800SMatthew Dillon  */
946ff1c9800SMatthew Dillon static int
947ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2)
948ff1c9800SMatthew Dillon {
949ff1c9800SMatthew Dillon 	struct tm tm;
950ff1c9800SMatthew Dillon 	time_t t;
951ff1c9800SMatthew Dillon 	int year;
952ff1c9800SMatthew Dillon 	int month;
9535e435c92SMatthew Dillon 	int day = 0;
9545e435c92SMatthew Dillon 	int hour = 0;
9555e435c92SMatthew Dillon 	int minute = 0;
956ff1c9800SMatthew Dillon 	int r;
957ff1c9800SMatthew Dillon 
9585e435c92SMatthew Dillon 	while (*fpath && *fpath != '-' && *fpath != '.')
9595e435c92SMatthew Dillon 		++fpath;
9605e435c92SMatthew Dillon 	if (*fpath)
9615e435c92SMatthew Dillon 		++fpath;
9625e435c92SMatthew Dillon 
9635e435c92SMatthew Dillon 	r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
964ff1c9800SMatthew Dillon 		   &year, &month, &day, &hour, &minute);
9655e435c92SMatthew Dillon 
9665e435c92SMatthew Dillon 	if (r >= 3) {
967ff1c9800SMatthew Dillon 		bzero(&tm, sizeof(tm));
968ff1c9800SMatthew Dillon 		tm.tm_isdst = -1;
969ff1c9800SMatthew Dillon 		tm.tm_min = minute;
970ff1c9800SMatthew Dillon 		tm.tm_hour = hour;
971ff1c9800SMatthew Dillon 		tm.tm_mday = day;
972ff1c9800SMatthew Dillon 		tm.tm_mon = month - 1;
973ff1c9800SMatthew Dillon 		tm.tm_year = year - 1900;
9745e435c92SMatthew Dillon 		t = mktime(&tm);
9755e435c92SMatthew Dillon 		if (t == (time_t)-1)
9765e435c92SMatthew Dillon 			return(0);
9775e435c92SMatthew Dillon 		t = time(NULL) - t;
978ff1c9800SMatthew Dillon 		if ((int)t > arg2)
979ff1c9800SMatthew Dillon 			return(1);
980ff1c9800SMatthew Dillon 	}
981ff1c9800SMatthew Dillon 	return(0);
982ff1c9800SMatthew Dillon }
983ff1c9800SMatthew Dillon 
984ff1c9800SMatthew Dillon /*
9856a6e350fSMatthew Dillon  * Issue a snapshot.
9866a6e350fSMatthew Dillon  */
9876a6e350fSMatthew Dillon static int
988b5ec5ad4SMatthew Dillon create_snapshot(const char *path, const char *snapshots_path)
9896a6e350fSMatthew Dillon {
9906a6e350fSMatthew Dillon 	int r;
9916a6e350fSMatthew Dillon 
992ff1c9800SMatthew Dillon 	runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
9936a6e350fSMatthew Dillon 	return(r);
9946a6e350fSMatthew Dillon }
9956a6e350fSMatthew Dillon 
9966a6e350fSMatthew Dillon static int
9976a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path,
998c6c298a7SMatthew Dillon 		  int arg1 __unused, int arg2, int snapshots_disabled)
9996a6e350fSMatthew Dillon {
1000c6c298a7SMatthew Dillon 	/*
1001c6c298a7SMatthew Dillon 	 * If snapshots have been disabled run prune-everything instead
1002c6c298a7SMatthew Dillon 	 * of prune.
1003c6c298a7SMatthew Dillon 	 */
1004c6c298a7SMatthew Dillon 	if (snapshots_disabled && arg2) {
1005c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
1006c6c298a7SMatthew Dillon 			snapshots_path, arg2, path);
1007c6c298a7SMatthew Dillon 	} else if (snapshots_disabled) {
1008c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer prune-everything %s", path);
1009c6c298a7SMatthew Dillon 	} else if (arg2) {
10106a6e350fSMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
10116a6e350fSMatthew Dillon 			snapshots_path, arg2, snapshots_path);
10126a6e350fSMatthew Dillon 	} else {
10136a6e350fSMatthew Dillon 		runcmd(NULL, "hammer prune %s", snapshots_path);
10146a6e350fSMatthew Dillon 	}
10156a6e350fSMatthew Dillon 	return(0);
10166a6e350fSMatthew Dillon }
10176a6e350fSMatthew Dillon 
10186a6e350fSMatthew Dillon static int
10190b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path,
10200b8bd7daSMatthew Dillon 		  int arg1 __unused, int arg2)
10210b8bd7daSMatthew Dillon {
10220b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
10230b8bd7daSMatthew Dillon 		printf(".");
10240b8bd7daSMatthew Dillon 		fflush(stdout);
10250b8bd7daSMatthew Dillon 	}
10260b8bd7daSMatthew Dillon 
10270b8bd7daSMatthew Dillon 	runcmd(NULL,
10280b8bd7daSMatthew Dillon 	       "hammer -c %s/.rebalance.cycle -t %d rebalance %s",
10290b8bd7daSMatthew Dillon 	       snapshots_path, arg2, path);
10300b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
10310b8bd7daSMatthew Dillon 		printf(".");
10320b8bd7daSMatthew Dillon 		fflush(stdout);
10330b8bd7daSMatthew Dillon 	}
10340b8bd7daSMatthew Dillon 	if (VerboseOpt == 0)
10350b8bd7daSMatthew Dillon 		printf("\n");
10360b8bd7daSMatthew Dillon 	return(0);
10370b8bd7daSMatthew Dillon }
10380b8bd7daSMatthew Dillon 
10390b8bd7daSMatthew Dillon static int
10406a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path,
10416a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
10426a6e350fSMatthew Dillon {
10436a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
10446a6e350fSMatthew Dillon 		printf(".");
10456a6e350fSMatthew Dillon 		fflush(stdout);
10466a6e350fSMatthew Dillon 	}
1047797a0b63SMatthew Dillon 
1048797a0b63SMatthew Dillon 	/*
1049797a0b63SMatthew Dillon 	 * When reblocking the B-Tree always reblock everything in normal
1050797a0b63SMatthew Dillon 	 * mode.
1051797a0b63SMatthew Dillon 	 */
10526a6e350fSMatthew Dillon 	runcmd(NULL,
1053797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
10546a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
10556a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
10566a6e350fSMatthew Dillon 		printf(".");
10576a6e350fSMatthew Dillon 		fflush(stdout);
10586a6e350fSMatthew Dillon 	}
1059797a0b63SMatthew Dillon 
1060797a0b63SMatthew Dillon 	/*
1061797a0b63SMatthew Dillon 	 * When reblocking the inodes always reblock everything in normal
1062797a0b63SMatthew Dillon 	 * mode.
1063797a0b63SMatthew Dillon 	 */
10646a6e350fSMatthew Dillon 	runcmd(NULL,
1065797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
10666a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
10676a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
10686a6e350fSMatthew Dillon 		printf(".");
10696a6e350fSMatthew Dillon 		fflush(stdout);
10706a6e350fSMatthew Dillon 	}
1071797a0b63SMatthew Dillon 
1072797a0b63SMatthew Dillon 	/*
1073797a0b63SMatthew Dillon 	 * When reblocking the directories always reblock everything in normal
1074797a0b63SMatthew Dillon 	 * mode.
1075797a0b63SMatthew Dillon 	 */
1076797a0b63SMatthew Dillon 	runcmd(NULL,
1077797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
1078797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1079797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1080797a0b63SMatthew Dillon 		printf(".");
1081797a0b63SMatthew Dillon 		fflush(stdout);
1082797a0b63SMatthew Dillon 	}
1083797a0b63SMatthew Dillon 
1084797a0b63SMatthew Dillon 	/*
1085797a0b63SMatthew Dillon 	 * Do not reblock all the data in normal mode.
1086797a0b63SMatthew Dillon 	 */
10876a6e350fSMatthew Dillon 	runcmd(NULL,
10886a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
10896a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
10906a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
10916a6e350fSMatthew Dillon 		printf("\n");
10926a6e350fSMatthew Dillon 	return(0);
10936a6e350fSMatthew Dillon }
10946a6e350fSMatthew Dillon 
10956a6e350fSMatthew Dillon static int
10966a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path,
10976a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
10986a6e350fSMatthew Dillon {
10996a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11006a6e350fSMatthew Dillon 		printf(".");
11016a6e350fSMatthew Dillon 		fflush(stdout);
11026a6e350fSMatthew Dillon 	}
11036a6e350fSMatthew Dillon 	runcmd(NULL,
11046a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
11056a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11066a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11076a6e350fSMatthew Dillon 		printf(".");
11086a6e350fSMatthew Dillon 		fflush(stdout);
11096a6e350fSMatthew Dillon 	}
11106a6e350fSMatthew Dillon 	runcmd(NULL,
11116a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
11126a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11136a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11146a6e350fSMatthew Dillon 		printf(".");
11156a6e350fSMatthew Dillon 		fflush(stdout);
11166a6e350fSMatthew Dillon 	}
11176a6e350fSMatthew Dillon 	runcmd(NULL,
1118797a0b63SMatthew Dillon 	       "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
1119797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1120797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1121797a0b63SMatthew Dillon 		printf(".");
1122797a0b63SMatthew Dillon 		fflush(stdout);
1123797a0b63SMatthew Dillon 	}
1124797a0b63SMatthew Dillon 	runcmd(NULL,
11256a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
11266a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11276a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
11286a6e350fSMatthew Dillon 		printf("\n");
11296a6e350fSMatthew Dillon 	return(0);
11306a6e350fSMatthew Dillon }
11316a6e350fSMatthew Dillon 
11326a6e350fSMatthew Dillon static
11336a6e350fSMatthew Dillon void
11346a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...)
11356a6e350fSMatthew Dillon {
11366a6e350fSMatthew Dillon 	va_list va;
11376a6e350fSMatthew Dillon 	char *cmd;
11386a6e350fSMatthew Dillon 	char *arg;
11396a6e350fSMatthew Dillon 	char **av;
11406a6e350fSMatthew Dillon 	int n;
11416a6e350fSMatthew Dillon 	int nmax;
11426a6e350fSMatthew Dillon 	int res;
11436a6e350fSMatthew Dillon 	pid_t pid;
11446a6e350fSMatthew Dillon 
11456a6e350fSMatthew Dillon 	/*
11466a6e350fSMatthew Dillon 	 * Generate the command
11476a6e350fSMatthew Dillon 	 */
11486a6e350fSMatthew Dillon 	va_start(va, ctl);
11496a6e350fSMatthew Dillon 	vasprintf(&cmd, ctl, va);
11506a6e350fSMatthew Dillon 	va_end(va);
11516a6e350fSMatthew Dillon 	if (VerboseOpt)
11526a6e350fSMatthew Dillon 		printf("    %s\n", cmd);
11536a6e350fSMatthew Dillon 
11546a6e350fSMatthew Dillon 	/*
11556a6e350fSMatthew Dillon 	 * Break us down into arguments.  We do not just use system() here
11566a6e350fSMatthew Dillon 	 * because it blocks SIGINT and friends.
11576a6e350fSMatthew Dillon 	 */
11586a6e350fSMatthew Dillon 	n = 0;
11596a6e350fSMatthew Dillon 	nmax = 16;
11606a6e350fSMatthew Dillon 	av = malloc(sizeof(char *) * nmax);
11616a6e350fSMatthew Dillon 
11626a6e350fSMatthew Dillon 	for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
1163c453712aSMatthew Dillon 		if (n == nmax - 1) {
11646a6e350fSMatthew Dillon 			nmax += 16;
11656a6e350fSMatthew Dillon 			av = realloc(av, sizeof(char *) * nmax);
11666a6e350fSMatthew Dillon 		}
11676a6e350fSMatthew Dillon 		av[n++] = arg;
11686a6e350fSMatthew Dillon 	}
1169c453712aSMatthew Dillon 	av[n++] = NULL;
11706a6e350fSMatthew Dillon 
11716a6e350fSMatthew Dillon 	/*
11726a6e350fSMatthew Dillon 	 * Run the command.
11736a6e350fSMatthew Dillon 	 */
1174445faa69SMatthew Dillon 	RunningIoctl = 1;
11756a6e350fSMatthew Dillon 	if ((pid = fork()) == 0) {
11766a6e350fSMatthew Dillon 		if (VerboseOpt < 2) {
11776a6e350fSMatthew Dillon 			int fd = open("/dev/null", O_RDWR);
11786a6e350fSMatthew Dillon 			dup2(fd, 1);
11796a6e350fSMatthew Dillon 			close(fd);
11806a6e350fSMatthew Dillon 		}
11816a6e350fSMatthew Dillon 		execvp(av[0], av);
11826a6e350fSMatthew Dillon 		_exit(127);
11836a6e350fSMatthew Dillon 	} else if (pid < 0) {
11846a6e350fSMatthew Dillon 		res = 127;
11856a6e350fSMatthew Dillon 	} else {
11866a6e350fSMatthew Dillon 		int status;
1187445faa69SMatthew Dillon 
11886a6e350fSMatthew Dillon 		while (waitpid(pid, &status, 0) != pid)
11896a6e350fSMatthew Dillon 			;
11906a6e350fSMatthew Dillon 		res = WEXITSTATUS(status);
11916a6e350fSMatthew Dillon 	}
1192445faa69SMatthew Dillon 	RunningIoctl = 0;
1193445faa69SMatthew Dillon 	if (DidInterrupt)
1194445faa69SMatthew Dillon 		_exit(1);
11956a6e350fSMatthew Dillon 
11966a6e350fSMatthew Dillon 	free(cmd);
11976a6e350fSMatthew Dillon 	free(av);
11986a6e350fSMatthew Dillon 	if (resp)
11996a6e350fSMatthew Dillon 		*resp = res;
12006a6e350fSMatthew Dillon }
1201