xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision d50f9ae3)
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  */
346a6e350fSMatthew Dillon /*
3516265794SThomas Nikolajsen  * Clean up specific HAMMER filesystems or all HAMMER filesystems.
366a6e350fSMatthew Dillon  *
37f6532f03SThomas Nikolajsen  * If no filesystems are specified any HAMMER- or null-mounted hammer PFS's
3816265794SThomas Nikolajsen  * are cleaned.
396a6e350fSMatthew Dillon  *
4016265794SThomas Nikolajsen  * Each HAMMER filesystem may contain a configuration file.  If no
4116265794SThomas Nikolajsen  * configuration file is present one will be created with the following
426a6e350fSMatthew Dillon  * defaults:
436a6e350fSMatthew Dillon  *
445e435c92SMatthew Dillon  *	snapshots 1d 60d	(0d 0d for /tmp, /var/tmp, /usr/obj)
456a6e350fSMatthew Dillon  *	prune     1d 5m
46f13fea8bSSascha Wildner  *	rebalance 1d 5m
47bb29b5d8SMatthew Dillon  *	#dedup	  1d 5m		(not enabled by default)
486a6e350fSMatthew Dillon  *	reblock   1d 5m
49f6532f03SThomas Nikolajsen  *	recopy    30d 10m
506a6e350fSMatthew Dillon  *
516a6e350fSMatthew Dillon  * All hammer commands create and maintain cycle files in the snapshots
526a6e350fSMatthew Dillon  * directory.
5316265794SThomas Nikolajsen  *
5416265794SThomas Nikolajsen  * For HAMMER version 2- the configuration file is a named 'config' in
5516265794SThomas Nikolajsen  * the snapshots directory, which defaults to <pfs>/snapshots.
5616265794SThomas Nikolajsen  * For HAMMER version 3+ the configuration file is saved in filesystem
5716265794SThomas Nikolajsen  * meta-data. The snapshots directory defaults to /var/hammer/<pfs>
5816265794SThomas Nikolajsen  * (/var/hammer/root for root mount).
596a6e350fSMatthew Dillon  */
606a6e350fSMatthew Dillon 
616a6e350fSMatthew Dillon #include "hammer.h"
626a6e350fSMatthew Dillon 
6341ae0862STomohiro Kusumi #include <libutil.h>
6441ae0862STomohiro Kusumi 
65c453712aSMatthew Dillon struct didpfs {
66c453712aSMatthew Dillon 	struct didpfs *next;
6790da8fc8STomohiro Kusumi 	hammer_uuid_t uuid;
68c453712aSMatthew Dillon };
69c453712aSMatthew Dillon 
706a6e350fSMatthew Dillon static void do_cleanup(const char *path);
7183f2a3aaSMatthew Dillon static void config_init(const char *path, struct hammer_ioc_config *config);
7283f2a3aaSMatthew Dillon static void migrate_config(FILE *fp, struct hammer_ioc_config *config);
7383f2a3aaSMatthew Dillon static void migrate_snapshots(int fd, const char *snapshots_path);
7483f2a3aaSMatthew Dillon static void migrate_one_snapshot(int fd, const char *fpath,
7583f2a3aaSMatthew Dillon 			struct hammer_ioc_snapshot *snapshot);
766a6e350fSMatthew Dillon static int strtosecs(char *ptr);
776a6e350fSMatthew Dillon static const char *dividing_slash(const char *path);
786a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1,
796a6e350fSMatthew Dillon 			time_t *savep);
806a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd,
816a6e350fSMatthew Dillon 			time_t savet);
8283f2a3aaSMatthew Dillon static int check_softlinks(int fd, int new_config, const char *snapshots_path);
8383f2a3aaSMatthew Dillon static void cleanup_softlinks(int fd, int new_config,
8483f2a3aaSMatthew Dillon 			const char *snapshots_path, int arg2, char *arg3);
85ec2aa11dSMatthew Dillon static void delete_snapshots(int fd, struct hammer_ioc_snapshot *dsnapshot);
86ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2);
876a6e350fSMatthew Dillon 
88b5ec5ad4SMatthew Dillon static int create_snapshot(const char *path, const char *snapshots_path);
890b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path,
900b8bd7daSMatthew Dillon 			int arg1, int arg2);
916a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path,
92c6c298a7SMatthew Dillon 			int arg1, int arg2, int snapshots_disabled);
936a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path,
946a6e350fSMatthew Dillon 			int arg1, int arg2);
956a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path,
966a6e350fSMatthew Dillon 			int arg1, int arg2);
97bb29b5d8SMatthew Dillon static int cleanup_dedup(const char *path, const char *snapshots_path,
98bb29b5d8SMatthew Dillon 			int arg1, int arg2);
996a6e350fSMatthew Dillon 
100b58f1e66SSascha Wildner static void runcmd(int *resp, const char *ctl, ...) __printflike(2, 3);
1016a6e350fSMatthew Dillon 
1026a6e350fSMatthew Dillon #define WS	" \t\r\n"
1036a6e350fSMatthew Dillon 
104*d50f9ae3SSascha Wildner static struct didpfs *FirstPFS;
1056a6e350fSMatthew Dillon 
1066a6e350fSMatthew Dillon void
hammer_cmd_cleanup(char ** av,int ac)1076a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
1086a6e350fSMatthew Dillon {
109eac446c5SMatthew Dillon 	char *fstype, *fs, *path;
110eac446c5SMatthew Dillon 	struct statfs *stfsbuf;
111eac446c5SMatthew Dillon 	int mntsize, i;
1126a6e350fSMatthew Dillon 
1136a6e350fSMatthew Dillon 	tzset();
1146a6e350fSMatthew Dillon 	if (ac == 0) {
115eac446c5SMatthew Dillon 		mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
116f254e677STomohiro Kusumi 		if (mntsize > 0) {
117eac446c5SMatthew Dillon 			for (i=0; i < mntsize; i++) {
118eac446c5SMatthew Dillon 				/*
119eac446c5SMatthew Dillon 				 * We will cleanup in the case fstype is hammer.
120eac446c5SMatthew Dillon 				 * If we have null-mounted PFS, we check the
121eac446c5SMatthew Dillon 				 * mount source. If it looks like a PFS, we
122eac446c5SMatthew Dillon 				 * proceed to cleanup also.
123eac446c5SMatthew Dillon 				 */
124eac446c5SMatthew Dillon 				fstype = stfsbuf[i].f_fstypename;
125eac446c5SMatthew Dillon 				fs = stfsbuf[i].f_mntfromname;
126eac446c5SMatthew Dillon 				if ((strcmp(fstype, "hammer") == 0) ||
127eac446c5SMatthew Dillon 				    ((strcmp(fstype, "null") == 0) &&
128cb3c760cSMatthew Dillon 				     (strstr(fs, "/@@0x") != NULL ||
129cb3c760cSMatthew Dillon 				      strstr(fs, "/@@-1") != NULL))) {
130eac446c5SMatthew Dillon 					path = stfsbuf[i].f_mntonname;
1316a6e350fSMatthew Dillon 					do_cleanup(path);
1326a6e350fSMatthew Dillon 				}
133574066d3SMatthew Dillon 			}
134f254e677STomohiro Kusumi 		}
1356a6e350fSMatthew Dillon 	} else {
1366a6e350fSMatthew Dillon 		while (ac) {
1376a6e350fSMatthew Dillon 			do_cleanup(*av);
1386a6e350fSMatthew Dillon 			--ac;
1396a6e350fSMatthew Dillon 			++av;
1406a6e350fSMatthew Dillon 		}
1416a6e350fSMatthew Dillon 	}
1426a6e350fSMatthew Dillon }
1436a6e350fSMatthew Dillon 
1446a6e350fSMatthew Dillon static
1456a6e350fSMatthew Dillon void
do_cleanup(const char * path)1466a6e350fSMatthew Dillon do_cleanup(const char *path)
1476a6e350fSMatthew Dillon {
1486a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
14983f2a3aaSMatthew Dillon 	struct hammer_ioc_config config;
15083f2a3aaSMatthew Dillon 	struct hammer_ioc_version version;
1516a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
15282893617SThomas Nikolajsen 	char *snapshots_path = NULL;
1536a6e350fSMatthew Dillon 	char *config_path;
1546a6e350fSMatthew Dillon 	struct stat st;
1556a6e350fSMatthew Dillon 	char *cmd;
1566a6e350fSMatthew Dillon 	char *ptr;
1576a6e350fSMatthew Dillon 	int arg1;
1586a6e350fSMatthew Dillon 	int arg2;
1595e435c92SMatthew Dillon 	char *arg3;
1606a6e350fSMatthew Dillon 	time_t savet;
1616a6e350fSMatthew Dillon 	char buf[256];
16283f2a3aaSMatthew Dillon 	char *cbase;
16383f2a3aaSMatthew Dillon 	char *cptr;
16482893617SThomas Nikolajsen 	FILE *fp = NULL;
165c453712aSMatthew Dillon 	struct didpfs *didpfs;
166c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
167c6c298a7SMatthew Dillon 	int prune_warning = 0;
16883f2a3aaSMatthew Dillon 	int new_config = 0;
169b5ec5ad4SMatthew Dillon 	int snapshots_from_pfs = 0;
1706a6e350fSMatthew Dillon 	int fd;
1716a6e350fSMatthew Dillon 	int r;
1720b8bd7daSMatthew Dillon 	int found_rebal = 0;
1736a6e350fSMatthew Dillon 
1746a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1751b23fc22STomohiro Kusumi 	clrpfs(&pfs, &mrec_tmp.pfs.pfsd, -1);
1766a6e350fSMatthew Dillon 
1776a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1786a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1796a6e350fSMatthew Dillon 	if (fd < 0) {
1806a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1816a6e350fSMatthew Dillon 		return;
1826a6e350fSMatthew Dillon 	}
18383f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
1846a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
18583f2a3aaSMatthew Dillon 		close(fd);
1866a6e350fSMatthew Dillon 		return;
1876a6e350fSMatthew Dillon 	}
1886a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1896a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
19083f2a3aaSMatthew Dillon 		close(fd);
1916a6e350fSMatthew Dillon 		return;
1926a6e350fSMatthew Dillon 	}
19383f2a3aaSMatthew Dillon 	bzero(&version, sizeof(version));
19483f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) {
19583f2a3aaSMatthew Dillon 		printf(" HAMMER filesystem but couldn't retrieve version!\n");
19683f2a3aaSMatthew Dillon 		close(fd);
19783f2a3aaSMatthew Dillon 		return;
19883f2a3aaSMatthew Dillon 	}
1994c09d9c4SMatthew Dillon 	HammerVersion = version.cur_version;
20083f2a3aaSMatthew Dillon 
20183f2a3aaSMatthew Dillon 	bzero(&config, sizeof(config));
202f254e677STomohiro Kusumi 	if (version.cur_version >= 3) {
20383f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 &&
204f254e677STomohiro Kusumi 		    config.head.error == 0) {
20583f2a3aaSMatthew Dillon 			new_config = 1;
206f254e677STomohiro Kusumi 		}
207f254e677STomohiro Kusumi 	}
2086a6e350fSMatthew Dillon 
2096a6e350fSMatthew Dillon 	/*
2106a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
2116a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
2126a6e350fSMatthew Dillon 	 */
213f254e677STomohiro Kusumi 	for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
2143cd578edSTomohiro Kusumi 		if (hammer_uuid_compare(&didpfs->uuid,
2153cd578edSTomohiro Kusumi 			&mrec_tmp.pfs.pfsd.unique_uuid) == 0) {
216bb8e52c0SThomas Nikolajsen 			printf(" PFS#%d already handled\n", pfs.pfs_id);
21783f2a3aaSMatthew Dillon 			close(fd);
2186a6e350fSMatthew Dillon 			return;
2196a6e350fSMatthew Dillon 		}
220f254e677STomohiro Kusumi 	}
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;
2392bacc088STomohiro Kusumi 	} else if (hammer_is_pfs_slave(&mrec_tmp.pfs.pfsd)) {
24082893617SThomas Nikolajsen 		if (version.cur_version < 3) {
241ff1c9800SMatthew Dillon 			printf(" WARNING: must configure snapshot dir for PFS slave\n");
242ff1c9800SMatthew Dillon 			printf("\tWe suggest <fs>/var/slaves/<name> where "
243ff1c9800SMatthew Dillon 			       "<fs> is the base HAMMER fs\n");
244bb8e52c0SThomas Nikolajsen 			printf("\tcontaining the slave\n");
24583f2a3aaSMatthew Dillon 			close(fd);
246ff1c9800SMatthew Dillon 			return;
24782893617SThomas Nikolajsen 		}
248ff1c9800SMatthew Dillon 	} else {
249ff1c9800SMatthew Dillon 		asprintf(&snapshots_path,
250ff1c9800SMatthew Dillon 			 "%s%ssnapshots", path, dividing_slash(path));
251ff1c9800SMatthew Dillon 	}
252ff1c9800SMatthew Dillon 
253ff1c9800SMatthew Dillon 	/*
254b5ec5ad4SMatthew Dillon 	 * Check for old-style config file
2556a6e350fSMatthew Dillon 	 */
25682893617SThomas Nikolajsen 	if (snapshots_path) {
2576a6e350fSMatthew Dillon 		asprintf(&config_path, "%s/config", snapshots_path);
2586a6e350fSMatthew Dillon 		fp = fopen(config_path, "r");
25982893617SThomas Nikolajsen 	}
26083f2a3aaSMatthew Dillon 
26183f2a3aaSMatthew Dillon 	/*
26283f2a3aaSMatthew Dillon 	 * Handle upgrades to hammer version 3, move the config
26383f2a3aaSMatthew Dillon 	 * file into meta-data.
26483f2a3aaSMatthew Dillon 	 *
26583f2a3aaSMatthew Dillon 	 * For the old config read the file into the config structure,
26683f2a3aaSMatthew Dillon 	 * we will parse it out of the config structure regardless.
26783f2a3aaSMatthew Dillon 	 */
26883f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
26983f2a3aaSMatthew Dillon 		if (fp) {
27083f2a3aaSMatthew Dillon 			printf("(migrating) ");
27183f2a3aaSMatthew Dillon 			fflush(stdout);
27283f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
27383f2a3aaSMatthew Dillon 			migrate_snapshots(fd, snapshots_path);
27483f2a3aaSMatthew Dillon 			fclose(fp);
27583f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
27683f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
27783f2a3aaSMatthew Dillon 				close(fd);
2786a6e350fSMatthew Dillon 				return;
2796a6e350fSMatthew Dillon 			}
28083f2a3aaSMatthew Dillon 			remove(config_path);
28183f2a3aaSMatthew Dillon 		} else if (new_config == 0) {
28283f2a3aaSMatthew Dillon 			config_init(path, &config);
28383f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
28483f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
28583f2a3aaSMatthew Dillon 				close(fd);
28683f2a3aaSMatthew Dillon 				return;
28783f2a3aaSMatthew Dillon 			}
28883f2a3aaSMatthew Dillon 		}
28983f2a3aaSMatthew Dillon 		new_config = 1;
29083f2a3aaSMatthew Dillon 	} else {
291b5ec5ad4SMatthew Dillon 		/*
292b5ec5ad4SMatthew Dillon 		 * Create missing snapshots directory for HAMMER VERSION < 3
293b5ec5ad4SMatthew Dillon 		 */
294f254e677STomohiro Kusumi 		if (stat(snapshots_path, &st) < 0) {
295b5ec5ad4SMatthew Dillon 			if (mkdir(snapshots_path, 0755) != 0) {
296b5ec5ad4SMatthew Dillon 				free(snapshots_path);
297b5ec5ad4SMatthew Dillon 				printf(" unable to create snapshot dir \"%s\": %s\n",
298b5ec5ad4SMatthew Dillon 					snapshots_path, strerror(errno));
299b5ec5ad4SMatthew Dillon 				close(fd);
300b5ec5ad4SMatthew Dillon 				return;
301b5ec5ad4SMatthew Dillon 			}
302f254e677STomohiro Kusumi 		}
303b5ec5ad4SMatthew Dillon 
304b5ec5ad4SMatthew Dillon 		/*
305b5ec5ad4SMatthew Dillon 		 *  Create missing config file for HAMMER VERSION < 3
306b5ec5ad4SMatthew Dillon 		 */
30783f2a3aaSMatthew Dillon 		if (fp == NULL) {
30883f2a3aaSMatthew Dillon 			config_init(path, &config);
30983f2a3aaSMatthew Dillon 			fp = fopen(config_path, "w");
31083f2a3aaSMatthew Dillon 			if (fp) {
31183f2a3aaSMatthew Dillon 				fwrite(config.config.text, 1,
31283f2a3aaSMatthew Dillon 					strlen(config.config.text), fp);
31383f2a3aaSMatthew Dillon 				fclose(fp);
31483f2a3aaSMatthew Dillon 			}
31583f2a3aaSMatthew Dillon 		} else {
31683f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
31783f2a3aaSMatthew Dillon 			fclose(fp);
31883f2a3aaSMatthew Dillon 		}
31983f2a3aaSMatthew Dillon 	}
3206a6e350fSMatthew Dillon 
321b5ec5ad4SMatthew Dillon 	/*
322b5ec5ad4SMatthew Dillon 	 * If snapshots_from_pfs is not set we calculate the new snapshots
323b5ec5ad4SMatthew Dillon 	 * directory default (in /var) for HAMMER VERSION >= 3 and migrate
324b5ec5ad4SMatthew Dillon 	 * the old snapshots directory over.
325b5ec5ad4SMatthew Dillon 	 *
326b5ec5ad4SMatthew Dillon 	 * People who have set an explicit snapshots directory will have
327b5ec5ad4SMatthew Dillon 	 * to migrate the data manually into /var/hammer, or not bother at
328b5ec5ad4SMatthew Dillon 	 * all.  People running slaves may wish to migrate it and then
329b5ec5ad4SMatthew Dillon 	 * clear the snapshots specification in the PFS config for the
330b5ec5ad4SMatthew Dillon 	 * slave.
331b5ec5ad4SMatthew Dillon 	 */
332b5ec5ad4SMatthew Dillon 	if (new_config && snapshots_from_pfs == 0) {
333b5ec5ad4SMatthew Dillon 		char *npath;
334b5ec5ad4SMatthew Dillon 
335aae41539STomohiro Kusumi 		if (path[0] != '/') {
336aae41539STomohiro Kusumi 			printf(" path must start with '/'\n");
337aae41539STomohiro Kusumi 			return;
338aae41539STomohiro Kusumi 		}
339b5ec5ad4SMatthew Dillon 		if (strcmp(path, "/") == 0)
340b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/root", SNAPSHOTS_BASE);
341b5ec5ad4SMatthew Dillon 		else
342b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1);
34382893617SThomas Nikolajsen 		if (snapshots_path) {
344b5ec5ad4SMatthew Dillon 			if (stat(npath, &st) < 0 && errno == ENOENT) {
345b5ec5ad4SMatthew Dillon 				if (stat(snapshots_path, &st) < 0 && errno == ENOENT) {
346b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Creating snapshots\n"
347b5ec5ad4SMatthew Dillon 					       "\tCreating snapshots in %s\n",
348b5ec5ad4SMatthew Dillon 					       npath);
349b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
350b5ec5ad4SMatthew Dillon 				} else {
351b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Moving snapshots\n"
352b5ec5ad4SMatthew Dillon 					       "\tMoving snapshots from %s to %s\n",
353b5ec5ad4SMatthew Dillon 					       snapshots_path, npath);
354b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
355b5ec5ad4SMatthew Dillon 					runcmd(&r, "cpdup %s %s", snapshots_path, npath);
356b5ec5ad4SMatthew Dillon 					if (r != 0) {
357b5ec5ad4SMatthew Dillon 						printf("Unable to move snapshots directory!\n");
358b5ec5ad4SMatthew Dillon 						printf("Please fix this critical error.\n");
359b5ec5ad4SMatthew Dillon 						printf("Aborting cleanup of %s\n", path);
360b5ec5ad4SMatthew Dillon 						close(fd);
361b5ec5ad4SMatthew Dillon 						return;
362b5ec5ad4SMatthew Dillon 					}
363b5ec5ad4SMatthew Dillon 					runcmd(&r, "rm -rf %s", snapshots_path);
364b5ec5ad4SMatthew Dillon 				}
365b5ec5ad4SMatthew Dillon 			}
366b5ec5ad4SMatthew Dillon 			free(snapshots_path);
36782893617SThomas Nikolajsen 		} else if (stat(npath, &st) < 0 && errno == ENOENT) {
36882893617SThomas Nikolajsen 			runcmd(&r, "mkdir -p %s", npath);
36982893617SThomas Nikolajsen 		}
370b5ec5ad4SMatthew Dillon 		snapshots_path = npath;
371b5ec5ad4SMatthew Dillon 	}
372b5ec5ad4SMatthew Dillon 
373b5ec5ad4SMatthew Dillon 	/*
374b5ec5ad4SMatthew Dillon 	 * Lock the PFS.  fd is the base directory of the mounted PFS.
375b5ec5ad4SMatthew Dillon 	 */
37683f2a3aaSMatthew Dillon 	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
3775bd5f172SSimon Schubert 		if (errno == EWOULDBLOCK)
3785bd5f172SSimon Schubert 			printf(" PFS#%d locked by other process\n", pfs.pfs_id);
3795bd5f172SSimon Schubert 		else
3805bd5f172SSimon Schubert 			printf(" can not lock %s: %s\n", config_path, strerror(errno));
38183f2a3aaSMatthew Dillon 		close(fd);
3825bd5f172SSimon Schubert 		return;
3835bd5f172SSimon Schubert 	}
3845bd5f172SSimon Schubert 
385ff1c9800SMatthew Dillon 	printf(" handle PFS#%d using %s\n", pfs.pfs_id, snapshots_path);
3866a6e350fSMatthew Dillon 
387a360fddeSJohn Marino 	struct pidfh	*pfh = NULL;
388a360fddeSJohn Marino 	static char	pidfile[PIDFILE_BUFSIZE];
389a360fddeSJohn Marino 
390a360fddeSJohn Marino 	snprintf (pidfile, PIDFILE_BUFSIZE, "%s/hammer.cleanup.%d",
391a360fddeSJohn Marino 		pidfile_loc, getpid());
392a360fddeSJohn Marino 	pfh = pidfile_open(pidfile, 0644, NULL);
39352e2f1b5STomohiro Kusumi 	if (pfh == NULL)
394a360fddeSJohn Marino 		warn ("Unable to open or create %s", pidfile);
395a360fddeSJohn Marino 	pidfile_write(pfh);
396a360fddeSJohn Marino 
3976a6e350fSMatthew Dillon 	/*
3986a6e350fSMatthew Dillon 	 * Process the config file
3996a6e350fSMatthew Dillon 	 */
40083f2a3aaSMatthew Dillon 	cbase = config.config.text;
40183f2a3aaSMatthew Dillon 
40283f2a3aaSMatthew Dillon 	while ((cptr = strchr(cbase, '\n')) != NULL) {
40383f2a3aaSMatthew Dillon 		bcopy(cbase, buf, cptr - cbase);
40483f2a3aaSMatthew Dillon 		buf[cptr - cbase] = 0;
40583f2a3aaSMatthew Dillon 		cbase = cptr + 1;
40683f2a3aaSMatthew Dillon 
4076a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
408743332abSMatthew Dillon 		if (cmd == NULL || cmd[0] == '#')
409743332abSMatthew Dillon 			continue;
410743332abSMatthew Dillon 
4116a6e350fSMatthew Dillon 		arg1 = 0;
4126a6e350fSMatthew Dillon 		arg2 = 0;
4135e435c92SMatthew Dillon 		arg3 = NULL;
4146a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
4156a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
4165e435c92SMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL) {
4176a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
4185e435c92SMatthew Dillon 				arg3 = strtok(NULL, WS);
4195e435c92SMatthew Dillon 			}
4206a6e350fSMatthew Dillon 		}
4216a6e350fSMatthew Dillon 
4226a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
4236a6e350fSMatthew Dillon 		fflush(stdout);
4246a6e350fSMatthew Dillon 
4256a6e350fSMatthew Dillon 		r = 1;
4266a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
4275e435c92SMatthew Dillon 			if (arg1 == 0) {
42883f2a3aaSMatthew Dillon 				if (arg2 &&
42983f2a3aaSMatthew Dillon 				    check_softlinks(fd, new_config,
43083f2a3aaSMatthew Dillon 						    snapshots_path)) {
4315e435c92SMatthew Dillon 					printf("only removing old snapshots\n");
4325e435c92SMatthew Dillon 					prune_warning = 1;
43383f2a3aaSMatthew Dillon 					cleanup_softlinks(fd, new_config,
43483f2a3aaSMatthew Dillon 							  snapshots_path,
4355e435c92SMatthew Dillon 							  arg2, arg3);
4365e435c92SMatthew Dillon 				} else {
4375e435c92SMatthew Dillon 					printf("disabled\n");
4385e435c92SMatthew Dillon 					snapshots_disabled = 1;
4395e435c92SMatthew Dillon 				}
4405e435c92SMatthew Dillon 			} else
4416a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4426a6e350fSMatthew Dillon 				printf("run\n");
44383f2a3aaSMatthew Dillon 				cleanup_softlinks(fd, new_config,
44483f2a3aaSMatthew Dillon 						  snapshots_path,
4455e435c92SMatthew Dillon 						  arg2, arg3);
446b5ec5ad4SMatthew Dillon 				r = create_snapshot(path, snapshots_path);
4476a6e350fSMatthew Dillon 			} else {
4486a6e350fSMatthew Dillon 				printf("skip\n");
4496a6e350fSMatthew Dillon 			}
4505e435c92SMatthew Dillon 		} else if (arg1 == 0) {
4515e435c92SMatthew Dillon 			/*
4525e435c92SMatthew Dillon 			 * The commands following this check can't handle
4535e435c92SMatthew Dillon 			 * a period of 0, so call the feature disabled and
4545e435c92SMatthew Dillon 			 * ignore the directive.
4555e435c92SMatthew Dillon 			 */
4565e435c92SMatthew Dillon 			printf("disabled\n");
4576a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
4586a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
459f254e677STomohiro Kusumi 				if (prune_warning) {
4605e435c92SMatthew Dillon 					printf("run - WARNING snapshot "
4615e435c92SMatthew Dillon 					       "softlinks present "
462226f3799SThomas Nikolajsen 					       "but snapshots disabled\n");
463f254e677STomohiro Kusumi 				} else {
464c6c298a7SMatthew Dillon 					printf("run\n");
465f254e677STomohiro Kusumi 				}
4666a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
467c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
4686a6e350fSMatthew Dillon 			} else {
4696a6e350fSMatthew Dillon 				printf("skip\n");
4706a6e350fSMatthew Dillon 			}
4710b8bd7daSMatthew Dillon 		} else if (strcmp(cmd, "rebalance") == 0) {
4720b8bd7daSMatthew Dillon 			found_rebal = 1;
4730b8bd7daSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4740b8bd7daSMatthew Dillon 				printf("run");
4750b8bd7daSMatthew Dillon 				fflush(stdout);
4760b8bd7daSMatthew Dillon 				if (VerboseOpt)
4770b8bd7daSMatthew Dillon 					printf("\n");
4780b8bd7daSMatthew Dillon 				r = cleanup_rebalance(path, snapshots_path,
4790b8bd7daSMatthew Dillon 						arg1, arg2);
4800b8bd7daSMatthew Dillon 			} else {
4810b8bd7daSMatthew Dillon 				printf("skip\n");
4820b8bd7daSMatthew Dillon 			}
4836a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
4846a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4856a6e350fSMatthew Dillon 				printf("run");
4866a6e350fSMatthew Dillon 				fflush(stdout);
4876a6e350fSMatthew Dillon 				if (VerboseOpt)
4886a6e350fSMatthew Dillon 					printf("\n");
4896a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
4906a6e350fSMatthew Dillon 						arg1, arg2);
4916a6e350fSMatthew Dillon 			} else {
4926a6e350fSMatthew Dillon 				printf("skip\n");
4936a6e350fSMatthew Dillon 			}
4946a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
4956a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4966a6e350fSMatthew Dillon 				printf("run");
4976a6e350fSMatthew Dillon 				fflush(stdout);
4986a6e350fSMatthew Dillon 				if (VerboseOpt)
4996a6e350fSMatthew Dillon 					printf("\n");
5006a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
5016a6e350fSMatthew Dillon 					       arg1, arg2);
5026a6e350fSMatthew Dillon 			} else {
5036a6e350fSMatthew Dillon 				printf("skip\n");
5046a6e350fSMatthew Dillon 			}
505bb29b5d8SMatthew Dillon 		} else if (strcmp(cmd, "dedup") == 0) {
506bb29b5d8SMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
507bb29b5d8SMatthew Dillon 				printf("run");
508bb29b5d8SMatthew Dillon 				fflush(stdout);
509bb29b5d8SMatthew Dillon 				if (VerboseOpt)
510bb29b5d8SMatthew Dillon 					printf("\n");
511bb29b5d8SMatthew Dillon 				r = cleanup_dedup(path, snapshots_path,
512bb29b5d8SMatthew Dillon 						arg1, arg2);
513bb29b5d8SMatthew Dillon 			} else {
514bb29b5d8SMatthew Dillon 				printf("skip\n");
515bb29b5d8SMatthew Dillon 			}
5166a6e350fSMatthew Dillon 		} else {
5176a6e350fSMatthew Dillon 			printf("unknown directive\n");
5186a6e350fSMatthew Dillon 			r = 1;
5196a6e350fSMatthew Dillon 		}
5206a6e350fSMatthew Dillon 		if (r == 0)
5216a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
5226a6e350fSMatthew Dillon 	}
5230b8bd7daSMatthew Dillon 
5240b8bd7daSMatthew Dillon 	/*
52583f2a3aaSMatthew Dillon 	 * Add new rebalance feature if the config doesn't have it.
526b5ec5ad4SMatthew Dillon 	 * (old style config only).
5270b8bd7daSMatthew Dillon 	 */
528f254e677STomohiro Kusumi 	if (new_config == 0 && found_rebal == 0) {
5290b8bd7daSMatthew Dillon 		if ((fp = fopen(config_path, "r+")) != NULL) {
5300b8bd7daSMatthew Dillon 			fseek(fp, 0L, 2);
5310b8bd7daSMatthew Dillon 			fprintf(fp, "rebalance 1d 5m\n");
5320b8bd7daSMatthew Dillon 			fclose(fp);
5330b8bd7daSMatthew Dillon 		}
534f254e677STomohiro Kusumi 	}
53583f2a3aaSMatthew Dillon 
53683f2a3aaSMatthew Dillon 	/*
53783f2a3aaSMatthew Dillon 	 * Cleanup, and delay a little
5381156c998SMatthew Dillon 	 *
5391156c998SMatthew Dillon 	 * NOTE: pidfile_remove() closes, removes, and frees the pfh.
5401156c998SMatthew Dillon 	 *	 pidfile_close() closes and frees.
54183f2a3aaSMatthew Dillon 	 */
54283f2a3aaSMatthew Dillon 	close(fd);
5436a6e350fSMatthew Dillon 	usleep(1000);
544a360fddeSJohn Marino 	pidfile_remove(pfh);
5456a6e350fSMatthew Dillon }
5466a6e350fSMatthew Dillon 
54783f2a3aaSMatthew Dillon /*
54883f2a3aaSMatthew Dillon  * Initialize new config data (new or old style)
54983f2a3aaSMatthew Dillon  */
550005a4da7STomohiro Kusumi static
551005a4da7STomohiro Kusumi void
config_init(const char * path,struct hammer_ioc_config * config)55283f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config)
55383f2a3aaSMatthew Dillon {
55483f2a3aaSMatthew Dillon 	const char *snapshots;
55583f2a3aaSMatthew Dillon 
55683f2a3aaSMatthew Dillon 	if (strcmp(path, "/tmp") == 0 ||
55783f2a3aaSMatthew Dillon 	    strcmp(path, "/var/tmp") == 0 ||
558f254e677STomohiro Kusumi 	    strcmp(path, "/usr/obj") == 0) {
55983f2a3aaSMatthew Dillon 		snapshots = "snapshots 0d 0d\n";
560f254e677STomohiro Kusumi 	} else {
56183f2a3aaSMatthew Dillon 		snapshots = "snapshots 1d 60d\n";
562f254e677STomohiro Kusumi 	}
56383f2a3aaSMatthew Dillon 	bzero(config->config.text, sizeof(config->config.text));
56483f2a3aaSMatthew Dillon 	snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s",
56583f2a3aaSMatthew Dillon 		snapshots,
56683f2a3aaSMatthew Dillon 		"prune     1d 5m\n"
56783f2a3aaSMatthew Dillon 		"rebalance 1d 5m\n"
568bb29b5d8SMatthew Dillon 		"#dedup	   1d 5m\n"
56983f2a3aaSMatthew Dillon 		"reblock   1d 5m\n"
570f6532f03SThomas Nikolajsen 		"recopy    30d 10m\n");
57183f2a3aaSMatthew Dillon }
57283f2a3aaSMatthew Dillon 
57383f2a3aaSMatthew Dillon /*
57483f2a3aaSMatthew Dillon  * Migrate configuration data from the old snapshots/config
57516265794SThomas Nikolajsen  * file to the new meta-data format.
57683f2a3aaSMatthew Dillon  */
577005a4da7STomohiro Kusumi static
578005a4da7STomohiro Kusumi void
migrate_config(FILE * fp,struct hammer_ioc_config * config)57983f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config)
58083f2a3aaSMatthew Dillon {
58183f2a3aaSMatthew Dillon 	int n;
58283f2a3aaSMatthew Dillon 
58383f2a3aaSMatthew Dillon 	n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp);
58483f2a3aaSMatthew Dillon 	if (n >= 0)
58583f2a3aaSMatthew Dillon 		bzero(config->config.text + n, sizeof(config->config.text) - n);
58683f2a3aaSMatthew Dillon }
58783f2a3aaSMatthew Dillon 
58883f2a3aaSMatthew Dillon /*
58983f2a3aaSMatthew Dillon  * Migrate snapshot softlinks in the snapshots directory to the
59083f2a3aaSMatthew Dillon  * new meta-data format.  The softlinks are left intact, but
59183f2a3aaSMatthew Dillon  * this way the pruning code won't lose track of them if you
59283f2a3aaSMatthew Dillon  * happen to blow away the snapshots directory.
59383f2a3aaSMatthew Dillon  */
594005a4da7STomohiro Kusumi static
595005a4da7STomohiro Kusumi void
migrate_snapshots(int fd,const char * snapshots_path)59683f2a3aaSMatthew Dillon migrate_snapshots(int fd, const char *snapshots_path)
59783f2a3aaSMatthew Dillon {
59883f2a3aaSMatthew Dillon 	struct hammer_ioc_snapshot snapshot;
59983f2a3aaSMatthew Dillon 	struct dirent *den;
60083f2a3aaSMatthew Dillon 	struct stat st;
60183f2a3aaSMatthew Dillon 	DIR *dir;
60283f2a3aaSMatthew Dillon 	char *fpath;
60383f2a3aaSMatthew Dillon 
60483f2a3aaSMatthew Dillon 	bzero(&snapshot, sizeof(snapshot));
60583f2a3aaSMatthew Dillon 
60683f2a3aaSMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
60783f2a3aaSMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
60883f2a3aaSMatthew Dillon 			if (den->d_name[0] == '.')
60983f2a3aaSMatthew Dillon 				continue;
61083f2a3aaSMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
61152e2f1b5STomohiro Kusumi 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
61283f2a3aaSMatthew Dillon 				migrate_one_snapshot(fd, fpath, &snapshot);
61383f2a3aaSMatthew Dillon 			free(fpath);
61483f2a3aaSMatthew Dillon 		}
61583f2a3aaSMatthew Dillon 		closedir(dir);
61683f2a3aaSMatthew Dillon 	}
61783f2a3aaSMatthew Dillon 	migrate_one_snapshot(fd, NULL, &snapshot);
61883f2a3aaSMatthew Dillon 
61983f2a3aaSMatthew Dillon }
62083f2a3aaSMatthew Dillon 
62183f2a3aaSMatthew Dillon /*
62283f2a3aaSMatthew Dillon  * Migrate a single snapshot.  If fpath is NULL the ioctl is flushed,
62383f2a3aaSMatthew Dillon  * otherwise it is flushed when it fills up.
62483f2a3aaSMatthew Dillon  */
625005a4da7STomohiro Kusumi static
626005a4da7STomohiro Kusumi void
migrate_one_snapshot(int fd,const char * fpath,struct hammer_ioc_snapshot * snapshot)62783f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath,
62883f2a3aaSMatthew Dillon 		     struct hammer_ioc_snapshot *snapshot)
62983f2a3aaSMatthew Dillon {
63083f2a3aaSMatthew Dillon 	if (fpath) {
63146e9f340STomohiro Kusumi 		hammer_snapshot_data_t snap;
63283f2a3aaSMatthew Dillon 		struct tm tm;
63383f2a3aaSMatthew Dillon 		time_t t;
63483f2a3aaSMatthew Dillon 		int year;
63583f2a3aaSMatthew Dillon 		int month;
63683f2a3aaSMatthew Dillon 		int day = 0;
63783f2a3aaSMatthew Dillon 		int hour = 0;
63883f2a3aaSMatthew Dillon 		int minute = 0;
63983f2a3aaSMatthew Dillon 		int r;
64083f2a3aaSMatthew Dillon 		char linkbuf[1024];
64183f2a3aaSMatthew Dillon 		const char *ptr;
64283f2a3aaSMatthew Dillon 		hammer_tid_t tid;
64383f2a3aaSMatthew Dillon 
64483f2a3aaSMatthew Dillon 		t = (time_t)-1;
64583f2a3aaSMatthew Dillon 		tid = (hammer_tid_t)(int64_t)-1;
64683f2a3aaSMatthew Dillon 
647f510bf3eSYONETANI Tomokazu 		/* fpath may contain directory components */
648f510bf3eSYONETANI Tomokazu 		if ((ptr = strrchr(fpath, '/')) != NULL)
649f510bf3eSYONETANI Tomokazu 			++ptr;
650f510bf3eSYONETANI Tomokazu 		else
65183f2a3aaSMatthew Dillon 			ptr = fpath;
65283f2a3aaSMatthew Dillon 		while (*ptr && *ptr != '-' && *ptr != '.')
65383f2a3aaSMatthew Dillon 			++ptr;
65483f2a3aaSMatthew Dillon 		if (*ptr)
65583f2a3aaSMatthew Dillon 			++ptr;
65683f2a3aaSMatthew Dillon 		r = sscanf(ptr, "%4d%2d%2d-%2d%2d",
65783f2a3aaSMatthew Dillon 			   &year, &month, &day, &hour, &minute);
65883f2a3aaSMatthew Dillon 
65983f2a3aaSMatthew Dillon 		if (r >= 3) {
66083f2a3aaSMatthew Dillon 			bzero(&tm, sizeof(tm));
66183f2a3aaSMatthew Dillon 			tm.tm_isdst = -1;
66283f2a3aaSMatthew Dillon 			tm.tm_min = minute;
66383f2a3aaSMatthew Dillon 			tm.tm_hour = hour;
66483f2a3aaSMatthew Dillon 			tm.tm_mday = day;
66583f2a3aaSMatthew Dillon 			tm.tm_mon = month - 1;
66683f2a3aaSMatthew Dillon 			tm.tm_year = year - 1900;
66783f2a3aaSMatthew Dillon 			t = mktime(&tm);
66883f2a3aaSMatthew Dillon 		}
66983f2a3aaSMatthew Dillon 		bzero(linkbuf, sizeof(linkbuf));
67083f2a3aaSMatthew Dillon 		if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 &&
67183f2a3aaSMatthew Dillon 		    (ptr = strrchr(linkbuf, '@')) != NULL &&
672f254e677STomohiro Kusumi 		    ptr > linkbuf && ptr[-1] == '@') {
67383f2a3aaSMatthew Dillon 			tid = strtoull(ptr + 1, NULL, 16);
674f254e677STomohiro Kusumi 		}
67583f2a3aaSMatthew Dillon 		if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) {
67683f2a3aaSMatthew Dillon 			snap = &snapshot->snaps[snapshot->count];
67783f2a3aaSMatthew Dillon 			bzero(snap, sizeof(*snap));
67883f2a3aaSMatthew Dillon 			snap->tid = tid;
67946137e17STomohiro Kusumi 			snap->ts = (uint64_t)t * 1000000ULL;
68083f2a3aaSMatthew Dillon 			snprintf(snap->label, sizeof(snap->label),
68183f2a3aaSMatthew Dillon 				 "migrated");
68283f2a3aaSMatthew Dillon 			++snapshot->count;
683f510bf3eSYONETANI Tomokazu 		} else {
684f510bf3eSYONETANI Tomokazu 			printf("    non-canonical snapshot softlink: %s->%s\n",
685f510bf3eSYONETANI Tomokazu 			       fpath, linkbuf);
68683f2a3aaSMatthew Dillon 		}
68783f2a3aaSMatthew Dillon 	}
68883f2a3aaSMatthew Dillon 
68983f2a3aaSMatthew Dillon 	if ((fpath == NULL && snapshot->count) ||
69083f2a3aaSMatthew Dillon 	    snapshot->count == HAMMER_SNAPS_PER_IOCTL) {
69183f2a3aaSMatthew Dillon 		printf(" (%d snapshots)", snapshot->count);
69283f2a3aaSMatthew Dillon again:
69383f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) {
69483f2a3aaSMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
69583f2a3aaSMatthew Dillon 			       strerror(errno));
69683f2a3aaSMatthew Dillon 		} else if (snapshot->head.error == EALREADY) {
69783f2a3aaSMatthew Dillon 			++snapshot->index;
69883f2a3aaSMatthew Dillon 			goto again;
69983f2a3aaSMatthew Dillon 		} else if (snapshot->head.error) {
700068da2c2SMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
70183f2a3aaSMatthew Dillon 			       strerror(snapshot->head.error));
70283f2a3aaSMatthew Dillon 		}
70383f2a3aaSMatthew Dillon 		printf("index %d\n", snapshot->index);
70483f2a3aaSMatthew Dillon 		snapshot->index = 0;
70583f2a3aaSMatthew Dillon 		snapshot->count = 0;
70683f2a3aaSMatthew Dillon 		snapshot->head.error = 0;
70783f2a3aaSMatthew Dillon 	}
70883f2a3aaSMatthew Dillon }
70983f2a3aaSMatthew Dillon 
7106a6e350fSMatthew Dillon static
7116a6e350fSMatthew Dillon int
strtosecs(char * ptr)7126a6e350fSMatthew Dillon strtosecs(char *ptr)
7136a6e350fSMatthew Dillon {
7146a6e350fSMatthew Dillon 	int val;
7156a6e350fSMatthew Dillon 
7166a6e350fSMatthew Dillon 	val = strtol(ptr, &ptr, 0);
7176a6e350fSMatthew Dillon 	switch(*ptr) {
7186a6e350fSMatthew Dillon 	case 'd':
7196a6e350fSMatthew Dillon 		val *= 24;
7206a6e350fSMatthew Dillon 		/* fall through */
7216a6e350fSMatthew Dillon 	case 'h':
7226a6e350fSMatthew Dillon 		val *= 60;
7236a6e350fSMatthew Dillon 		/* fall through */
7246a6e350fSMatthew Dillon 	case 'm':
7256a6e350fSMatthew Dillon 		val *= 60;
7266a6e350fSMatthew Dillon 		/* fall through */
7276a6e350fSMatthew Dillon 	case 's':
7286a6e350fSMatthew Dillon 		break;
7296a6e350fSMatthew Dillon 	default:
730c3e30e91STomohiro Kusumi 		errx(1, "illegal suffix converting %s", ptr);
731052fd72bSTomohiro Kusumi 		/* not reached */
7326a6e350fSMatthew Dillon 		break;
7336a6e350fSMatthew Dillon 	}
7346a6e350fSMatthew Dillon 	return(val);
7356a6e350fSMatthew Dillon }
7366a6e350fSMatthew Dillon 
737005a4da7STomohiro Kusumi static
738005a4da7STomohiro Kusumi const char *
dividing_slash(const char * path)7396a6e350fSMatthew Dillon dividing_slash(const char *path)
7406a6e350fSMatthew Dillon {
7416a6e350fSMatthew Dillon 	int len = strlen(path);
7426a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
7436a6e350fSMatthew Dillon 		return("");
7446a6e350fSMatthew Dillon 	else
7456a6e350fSMatthew Dillon 		return("/");
7466a6e350fSMatthew Dillon }
7476a6e350fSMatthew Dillon 
7486a6e350fSMatthew Dillon /*
7496a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
7506a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
7516a6e350fSMatthew Dillon  * current time_t so we can save it later on.
7526a6e350fSMatthew Dillon  *
7536a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
7546a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
7556a6e350fSMatthew Dillon  * of how close the last operation actually was.
756bdc54107SMatthew Dillon  *
757bdc54107SMatthew Dillon  * If ForceOpt is set always return true.
7586a6e350fSMatthew Dillon  */
759005a4da7STomohiro Kusumi static
760005a4da7STomohiro Kusumi int
check_period(const char * snapshots_path,const char * cmd,int arg1,time_t * savep)7616a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
7626a6e350fSMatthew Dillon 	time_t *savep)
7636a6e350fSMatthew Dillon {
7646a6e350fSMatthew Dillon 	char *check_path;
7656a6e350fSMatthew Dillon 	struct tm tp1;
7666a6e350fSMatthew Dillon 	struct tm tp2;
7676a6e350fSMatthew Dillon 	FILE *fp;
7686a6e350fSMatthew Dillon 	time_t baset, lastt;
7696a6e350fSMatthew Dillon 	char buf[256];
7706a6e350fSMatthew Dillon 
7716a6e350fSMatthew Dillon 	time(savep);
7726a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
7736a6e350fSMatthew Dillon 
7746a6e350fSMatthew Dillon 	/*
775bdc54107SMatthew Dillon 	 * Force run if -F
776bdc54107SMatthew Dillon 	 */
777bdc54107SMatthew Dillon 	if (ForceOpt)
778bdc54107SMatthew Dillon 		return(1);
779bdc54107SMatthew Dillon 
780bdc54107SMatthew Dillon 	/*
7816a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
7826a6e350fSMatthew Dillon 	 */
7836a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
7846a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
7856a6e350fSMatthew Dillon 	free(check_path);
7866a6e350fSMatthew Dillon 	if (fp == NULL)
7876a6e350fSMatthew Dillon 		return(1);
7886a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
7896a6e350fSMatthew Dillon 		fclose(fp);
7906a6e350fSMatthew Dillon 		return(1);
7916a6e350fSMatthew Dillon 	}
7926a6e350fSMatthew Dillon 	fclose(fp);
7936a6e350fSMatthew Dillon 
7946a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
7956a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
7966a6e350fSMatthew Dillon 
7976a6e350fSMatthew Dillon 	/*
7986a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
7996a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
8006a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
8016a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
8026a6e350fSMatthew Dillon 	 */
8036a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
8046a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
8056a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
8066a6e350fSMatthew Dillon 	}
8076a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
8086a6e350fSMatthew Dillon 		tp1.tm_min = 0;
8096a6e350fSMatthew Dillon 		tp2.tm_min = 0;
8106a6e350fSMatthew Dillon 	}
8116a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
8126a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
8136a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
8146a6e350fSMatthew Dillon 	}
8156a6e350fSMatthew Dillon 
8166a6e350fSMatthew Dillon 	baset = mktime(&tp1);
8176a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
8186a6e350fSMatthew Dillon 
8196a6e350fSMatthew Dillon #if 0
8206a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
8216a6e350fSMatthew Dillon #endif
8226a6e350fSMatthew Dillon 
8236a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
8246a6e350fSMatthew Dillon 		return(1);
8256a6e350fSMatthew Dillon 	return(0);
8266a6e350fSMatthew Dillon }
8276a6e350fSMatthew Dillon 
8286a6e350fSMatthew Dillon /*
8296a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
8306a6e350fSMatthew Dillon  */
831005a4da7STomohiro Kusumi static
832005a4da7STomohiro Kusumi void
save_period(const char * snapshots_path,const char * cmd,time_t savet)8336a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
8346a6e350fSMatthew Dillon 			time_t savet)
8356a6e350fSMatthew Dillon {
8366a6e350fSMatthew Dillon 	char *ocheck_path;
8376a6e350fSMatthew Dillon 	char *ncheck_path;
8386a6e350fSMatthew Dillon 	FILE *fp;
8396a6e350fSMatthew Dillon 
8406a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
8416a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
8426a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
8433b9fbdeaSMatthew Dillon 	if (fp) {
8446a6e350fSMatthew Dillon 		fprintf(fp, "0x%08llx\n", (long long)savet);
8456a6e350fSMatthew Dillon 		if (fclose(fp) == 0)
8466a6e350fSMatthew Dillon 			rename(ncheck_path, ocheck_path);
8476a6e350fSMatthew Dillon 		remove(ncheck_path);
8483b9fbdeaSMatthew Dillon 	} else {
8493b9fbdeaSMatthew Dillon 		fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
8503b9fbdeaSMatthew Dillon 			ncheck_path, strerror(errno));
8513b9fbdeaSMatthew Dillon 	}
8526a6e350fSMatthew Dillon }
8536a6e350fSMatthew Dillon 
854ff1c9800SMatthew Dillon /*
855ff1c9800SMatthew Dillon  * Simply count the number of softlinks in the snapshots dir
856ff1c9800SMatthew Dillon  */
857005a4da7STomohiro Kusumi static
858005a4da7STomohiro Kusumi int
check_softlinks(int fd,int new_config,const char * snapshots_path)85983f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path)
860c6c298a7SMatthew Dillon {
861c6c298a7SMatthew Dillon 	struct dirent *den;
862c6c298a7SMatthew Dillon 	struct stat st;
863c6c298a7SMatthew Dillon 	DIR *dir;
864c6c298a7SMatthew Dillon 	char *fpath;
865c6c298a7SMatthew Dillon 	int res = 0;
866c6c298a7SMatthew Dillon 
86783f2a3aaSMatthew Dillon 	/*
86883f2a3aaSMatthew Dillon 	 * Old-style softlink-based snapshots
86983f2a3aaSMatthew Dillon 	 */
870c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
871c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
872c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
873c6c298a7SMatthew Dillon 				continue;
874c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
875c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
876c6c298a7SMatthew Dillon 				++res;
877c6c298a7SMatthew Dillon 			free(fpath);
878c6c298a7SMatthew Dillon 		}
879c6c298a7SMatthew Dillon 		closedir(dir);
880c6c298a7SMatthew Dillon 	}
88183f2a3aaSMatthew Dillon 
88283f2a3aaSMatthew Dillon 	/*
88383f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
88483f2a3aaSMatthew Dillon 	 * count those too.
88583f2a3aaSMatthew Dillon 	 */
88683f2a3aaSMatthew Dillon 	if (new_config) {
88783f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
88883f2a3aaSMatthew Dillon 
88983f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
89083f2a3aaSMatthew Dillon 		do {
891f254e677STomohiro Kusumi 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
89283f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
89383f2a3aaSMatthew Dillon 					"snapshot error");
894f254e677STomohiro Kusumi 				/* not reached */
895f254e677STomohiro Kusumi 			}
89683f2a3aaSMatthew Dillon 			res += snapshot.count;
89783f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
89883f2a3aaSMatthew Dillon 	}
899c6c298a7SMatthew Dillon 	return (res);
900c6c298a7SMatthew Dillon }
901c6c298a7SMatthew Dillon 
9026a6e350fSMatthew Dillon /*
903ff1c9800SMatthew Dillon  * Clean up expired softlinks in the snapshots dir
904ff1c9800SMatthew Dillon  */
905005a4da7STomohiro Kusumi static
906005a4da7STomohiro Kusumi void
cleanup_softlinks(int fd,int new_config,const char * snapshots_path,int arg2,char * arg3)90783f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config,
90883f2a3aaSMatthew Dillon 		  const char *snapshots_path, int arg2, char *arg3)
909ff1c9800SMatthew Dillon {
910ff1c9800SMatthew Dillon 	struct dirent *den;
911ff1c9800SMatthew Dillon 	struct stat st;
912ff1c9800SMatthew Dillon 	DIR *dir;
913ff1c9800SMatthew Dillon 	char *fpath;
9145e435c92SMatthew Dillon 	int anylink = 0;
9155e435c92SMatthew Dillon 
91609e1b0d6SMatthew Dillon 	if (arg3 != NULL && strstr(arg3, "any") != NULL)
9175e435c92SMatthew Dillon 		anylink = 1;
918ff1c9800SMatthew Dillon 
919ff1c9800SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
920ff1c9800SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
921ff1c9800SMatthew Dillon 			if (den->d_name[0] == '.')
922ff1c9800SMatthew Dillon 				continue;
923ff1c9800SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
924ff1c9800SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
9259a620123STomohiro Kusumi 			    (anylink || strncmp(den->d_name, "snap-", 5) == 0)) {
926ff1c9800SMatthew Dillon 				if (check_expired(den->d_name, arg2)) {
927f254e677STomohiro Kusumi 					if (VerboseOpt) {
928ff1c9800SMatthew Dillon 						printf("    expire %s\n",
929ff1c9800SMatthew Dillon 							fpath);
930f254e677STomohiro Kusumi 					}
931ff1c9800SMatthew Dillon 					remove(fpath);
932ff1c9800SMatthew Dillon 				}
933ff1c9800SMatthew Dillon 			}
934ff1c9800SMatthew Dillon 			free(fpath);
935ff1c9800SMatthew Dillon 		}
936ff1c9800SMatthew Dillon 		closedir(dir);
937ff1c9800SMatthew Dillon 	}
93883f2a3aaSMatthew Dillon 
93983f2a3aaSMatthew Dillon 	/*
94083f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
94183f2a3aaSMatthew Dillon 	 * count those too.
94283f2a3aaSMatthew Dillon 	 */
94383f2a3aaSMatthew Dillon 	if (new_config) {
94483f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
94583f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot dsnapshot;
94646e9f340STomohiro Kusumi 		hammer_snapshot_data_t snap;
94783f2a3aaSMatthew Dillon 		struct tm *tp;
94883f2a3aaSMatthew Dillon 		time_t t;
949ec2aa11dSMatthew Dillon 		time_t dt;
95083f2a3aaSMatthew Dillon 		char snapts[32];
95146137e17STomohiro Kusumi 		uint32_t i;
95283f2a3aaSMatthew Dillon 
95383f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
95483f2a3aaSMatthew Dillon 		bzero(&dsnapshot, sizeof(dsnapshot));
95583f2a3aaSMatthew Dillon 		do {
956f254e677STomohiro Kusumi 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
95783f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
95883f2a3aaSMatthew Dillon 					"snapshot error");
959f254e677STomohiro Kusumi 				/* not reached */
960f254e677STomohiro Kusumi 			}
96183f2a3aaSMatthew Dillon 			for (i = 0; i < snapshot.count; ++i) {
96283f2a3aaSMatthew Dillon 				snap = &snapshot.snaps[i];
963ec2aa11dSMatthew Dillon 				t = snap->ts / 1000000ULL;
964ec2aa11dSMatthew Dillon 				dt = time(NULL) - t;
965f254e677STomohiro Kusumi 				if ((int)dt > arg2 || snap->tid == 0) {
96683f2a3aaSMatthew Dillon 					dsnapshot.snaps[dsnapshot.count++] =
96783f2a3aaSMatthew Dillon 						*snap;
968f254e677STomohiro Kusumi 				}
969ec2aa11dSMatthew Dillon 				if ((int)dt > arg2 && VerboseOpt) {
97083f2a3aaSMatthew Dillon 					tp = localtime(&t);
97183f2a3aaSMatthew Dillon 					strftime(snapts, sizeof(snapts),
97283f2a3aaSMatthew Dillon 						 "%Y-%m-%d %H:%M:%S %Z", tp);
97383f2a3aaSMatthew Dillon 					printf("    expire 0x%016jx %s %s\n",
97483f2a3aaSMatthew Dillon 					       (uintmax_t)snap->tid,
97583f2a3aaSMatthew Dillon 					       snapts,
97683f2a3aaSMatthew Dillon 					       snap->label);
97783f2a3aaSMatthew Dillon 				}
978ec2aa11dSMatthew Dillon 				if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL)
979ec2aa11dSMatthew Dillon 					delete_snapshots(fd, &dsnapshot);
98083f2a3aaSMatthew Dillon 			}
98183f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
98283f2a3aaSMatthew Dillon 
983ec2aa11dSMatthew Dillon 		if (dsnapshot.count)
984ec2aa11dSMatthew Dillon 			delete_snapshots(fd, &dsnapshot);
98583f2a3aaSMatthew Dillon 	}
98683f2a3aaSMatthew Dillon }
987ec2aa11dSMatthew Dillon 
988005a4da7STomohiro Kusumi static
989005a4da7STomohiro Kusumi void
delete_snapshots(int fd,struct hammer_ioc_snapshot * dsnapshot)990ec2aa11dSMatthew Dillon delete_snapshots(int fd, struct hammer_ioc_snapshot *dsnapshot)
991ec2aa11dSMatthew Dillon {
992ec2aa11dSMatthew Dillon 	for (;;) {
993ec2aa11dSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, dsnapshot) < 0) {
994ec2aa11dSMatthew Dillon 			printf("    Ioctl to delete snapshots failed: %s\n",
995ec2aa11dSMatthew Dillon 			       strerror(errno));
996ec2aa11dSMatthew Dillon 			break;
997ec2aa11dSMatthew Dillon 		}
998ec2aa11dSMatthew Dillon 		if (dsnapshot->head.error) {
999ec2aa11dSMatthew Dillon 			printf("    Ioctl to delete snapshots failed at "
1000ec2aa11dSMatthew Dillon 			       "snap=%016jx: %s\n",
1001ec2aa11dSMatthew Dillon 			       dsnapshot->snaps[dsnapshot->index].tid,
1002ec2aa11dSMatthew Dillon 			       strerror(dsnapshot->head.error));
1003ec2aa11dSMatthew Dillon 			if (++dsnapshot->index < dsnapshot->count)
1004ec2aa11dSMatthew Dillon 				continue;
1005ec2aa11dSMatthew Dillon 		}
1006ec2aa11dSMatthew Dillon 		break;
1007ec2aa11dSMatthew Dillon 	}
1008ec2aa11dSMatthew Dillon 	dsnapshot->index = 0;
1009ec2aa11dSMatthew Dillon 	dsnapshot->count = 0;
1010ec2aa11dSMatthew Dillon 	dsnapshot->head.error = 0;
1011ff1c9800SMatthew Dillon }
1012ff1c9800SMatthew Dillon 
1013ff1c9800SMatthew Dillon /*
1014ff1c9800SMatthew Dillon  * Take a softlink path in the form snap-yyyymmdd-hhmm and the
1015ff1c9800SMatthew Dillon  * expiration in seconds (arg2) and return non-zero if the softlink
1016ff1c9800SMatthew Dillon  * has expired.
1017ff1c9800SMatthew Dillon  */
1018005a4da7STomohiro Kusumi static
1019005a4da7STomohiro Kusumi int
check_expired(const char * fpath,int arg2)1020ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2)
1021ff1c9800SMatthew Dillon {
1022ff1c9800SMatthew Dillon 	struct tm tm;
1023ff1c9800SMatthew Dillon 	time_t t;
1024ff1c9800SMatthew Dillon 	int year;
1025ff1c9800SMatthew Dillon 	int month;
10265e435c92SMatthew Dillon 	int day = 0;
10275e435c92SMatthew Dillon 	int hour = 0;
10285e435c92SMatthew Dillon 	int minute = 0;
1029ff1c9800SMatthew Dillon 	int r;
1030ff1c9800SMatthew Dillon 
10315e435c92SMatthew Dillon 	while (*fpath && *fpath != '-' && *fpath != '.')
10325e435c92SMatthew Dillon 		++fpath;
10335e435c92SMatthew Dillon 	if (*fpath)
10345e435c92SMatthew Dillon 		++fpath;
10355e435c92SMatthew Dillon 
10365e435c92SMatthew Dillon 	r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
1037ff1c9800SMatthew Dillon 		   &year, &month, &day, &hour, &minute);
10385e435c92SMatthew Dillon 
10395e435c92SMatthew Dillon 	if (r >= 3) {
1040ff1c9800SMatthew Dillon 		bzero(&tm, sizeof(tm));
1041ff1c9800SMatthew Dillon 		tm.tm_isdst = -1;
1042ff1c9800SMatthew Dillon 		tm.tm_min = minute;
1043ff1c9800SMatthew Dillon 		tm.tm_hour = hour;
1044ff1c9800SMatthew Dillon 		tm.tm_mday = day;
1045ff1c9800SMatthew Dillon 		tm.tm_mon = month - 1;
1046ff1c9800SMatthew Dillon 		tm.tm_year = year - 1900;
10475e435c92SMatthew Dillon 		t = mktime(&tm);
10485e435c92SMatthew Dillon 		if (t == (time_t)-1)
10495e435c92SMatthew Dillon 			return(0);
10505e435c92SMatthew Dillon 		t = time(NULL) - t;
1051ff1c9800SMatthew Dillon 		if ((int)t > arg2)
1052ff1c9800SMatthew Dillon 			return(1);
1053ff1c9800SMatthew Dillon 	}
1054ff1c9800SMatthew Dillon 	return(0);
1055ff1c9800SMatthew Dillon }
1056ff1c9800SMatthew Dillon 
1057ff1c9800SMatthew Dillon /*
10586a6e350fSMatthew Dillon  * Issue a snapshot.
10596a6e350fSMatthew Dillon  */
1060005a4da7STomohiro Kusumi static
1061005a4da7STomohiro Kusumi int
create_snapshot(const char * path,const char * snapshots_path)1062b5ec5ad4SMatthew Dillon create_snapshot(const char *path, const char *snapshots_path)
10636a6e350fSMatthew Dillon {
10646a6e350fSMatthew Dillon 	int r;
10656a6e350fSMatthew Dillon 
1066ff1c9800SMatthew Dillon 	runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
10676a6e350fSMatthew Dillon 	return(r);
10686a6e350fSMatthew Dillon }
10696a6e350fSMatthew Dillon 
1070005a4da7STomohiro Kusumi static
1071005a4da7STomohiro Kusumi int
cleanup_prune(const char * path,const char * snapshots_path,int arg1 __unused,int arg2,int snapshots_disabled)1072eb5751f6SMatthew Dillon cleanup_prune(const char *path, const char *snapshots_path,
1073c6c298a7SMatthew Dillon 		  int arg1 __unused, int arg2, int snapshots_disabled)
10746a6e350fSMatthew Dillon {
1075eb5751f6SMatthew Dillon 	const char *path_or_snapshots_path;
1076eb5751f6SMatthew Dillon 
1077eb5751f6SMatthew Dillon 	/*
1078eb5751f6SMatthew Dillon 	 * If the snapshots_path (e.g. /var/hammer/...) has no snapshots
1079eb5751f6SMatthew Dillon 	 * in it then prune will get confused and prune the filesystem
1080eb5751f6SMatthew Dillon 	 * containing the snapshots_path instead of the requested
1081eb5751f6SMatthew Dillon 	 * filesystem.  De-confuse prune.  We need a better way.
1082eb5751f6SMatthew Dillon 	 */
1083ce6ddec9STomohiro Kusumi 	if (hammer_softprune_testdir(snapshots_path))
1084ce6ddec9STomohiro Kusumi 		path_or_snapshots_path = snapshots_path;
1085ce6ddec9STomohiro Kusumi 	else
1086ce6ddec9STomohiro Kusumi 		path_or_snapshots_path = path;
1087eb5751f6SMatthew Dillon 
1088c6c298a7SMatthew Dillon 	/*
1089c6c298a7SMatthew Dillon 	 * If snapshots have been disabled run prune-everything instead
1090c6c298a7SMatthew Dillon 	 * of prune.
1091c6c298a7SMatthew Dillon 	 */
1092f254e677STomohiro Kusumi 	if (snapshots_disabled && arg2) {
1093eb5751f6SMatthew Dillon 		runcmd(NULL,
1094eb5751f6SMatthew Dillon 		       "hammer -c %s/.prune.cycle -t %d prune-everything %s",
1095c6c298a7SMatthew Dillon 		       snapshots_path, arg2, path);
1096f254e677STomohiro Kusumi 	} else if (snapshots_disabled) {
1097c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer prune-everything %s", path);
1098f254e677STomohiro Kusumi 	} else if (arg2) {
10996a6e350fSMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
1100eb5751f6SMatthew Dillon 			snapshots_path, arg2, path_or_snapshots_path);
1101f254e677STomohiro Kusumi 	} else {
1102eb5751f6SMatthew Dillon 		runcmd(NULL, "hammer prune %s", path_or_snapshots_path);
1103f254e677STomohiro Kusumi 	}
11046a6e350fSMatthew Dillon 	return(0);
11056a6e350fSMatthew Dillon }
11066a6e350fSMatthew Dillon 
1107005a4da7STomohiro Kusumi static
1108005a4da7STomohiro Kusumi int
cleanup_rebalance(const char * path,const char * snapshots_path,int arg1 __unused,int arg2)11090b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path,
11100b8bd7daSMatthew Dillon 		  int arg1 __unused, int arg2)
11110b8bd7daSMatthew Dillon {
11120b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
11130b8bd7daSMatthew Dillon 		printf(".");
11140b8bd7daSMatthew Dillon 		fflush(stdout);
11150b8bd7daSMatthew Dillon 	}
11160b8bd7daSMatthew Dillon 
11170b8bd7daSMatthew Dillon 	runcmd(NULL,
11180b8bd7daSMatthew Dillon 	       "hammer -c %s/.rebalance.cycle -t %d rebalance %s",
11190b8bd7daSMatthew Dillon 	       snapshots_path, arg2, path);
11200b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
11210b8bd7daSMatthew Dillon 		printf(".");
11220b8bd7daSMatthew Dillon 		fflush(stdout);
11230b8bd7daSMatthew Dillon 	}
11240b8bd7daSMatthew Dillon 	if (VerboseOpt == 0)
11250b8bd7daSMatthew Dillon 		printf("\n");
11260b8bd7daSMatthew Dillon 	return(0);
11270b8bd7daSMatthew Dillon }
11280b8bd7daSMatthew Dillon 
1129005a4da7STomohiro Kusumi static
1130005a4da7STomohiro Kusumi int
cleanup_reblock(const char * path,const char * snapshots_path,int arg1 __unused,int arg2)11316a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path,
11326a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
11336a6e350fSMatthew Dillon {
11346a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11356a6e350fSMatthew Dillon 		printf(".");
11366a6e350fSMatthew Dillon 		fflush(stdout);
11376a6e350fSMatthew Dillon 	}
1138797a0b63SMatthew Dillon 
1139797a0b63SMatthew Dillon 	/*
1140797a0b63SMatthew Dillon 	 * When reblocking the B-Tree always reblock everything in normal
1141797a0b63SMatthew Dillon 	 * mode.
1142797a0b63SMatthew Dillon 	 */
11436a6e350fSMatthew Dillon 	runcmd(NULL,
1144797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
11456a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11466a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11476a6e350fSMatthew Dillon 		printf(".");
11486a6e350fSMatthew Dillon 		fflush(stdout);
11496a6e350fSMatthew Dillon 	}
1150797a0b63SMatthew Dillon 
1151797a0b63SMatthew Dillon 	/*
1152797a0b63SMatthew Dillon 	 * When reblocking the inodes always reblock everything in normal
1153797a0b63SMatthew Dillon 	 * mode.
1154797a0b63SMatthew Dillon 	 */
11556a6e350fSMatthew Dillon 	runcmd(NULL,
1156797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
11576a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11586a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11596a6e350fSMatthew Dillon 		printf(".");
11606a6e350fSMatthew Dillon 		fflush(stdout);
11616a6e350fSMatthew Dillon 	}
1162797a0b63SMatthew Dillon 
1163797a0b63SMatthew Dillon 	/*
1164797a0b63SMatthew Dillon 	 * When reblocking the directories always reblock everything in normal
1165797a0b63SMatthew Dillon 	 * mode.
1166797a0b63SMatthew Dillon 	 */
1167797a0b63SMatthew Dillon 	runcmd(NULL,
1168797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
1169797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1170797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1171797a0b63SMatthew Dillon 		printf(".");
1172797a0b63SMatthew Dillon 		fflush(stdout);
1173797a0b63SMatthew Dillon 	}
1174797a0b63SMatthew Dillon 
1175797a0b63SMatthew Dillon 	/*
1176797a0b63SMatthew Dillon 	 * Do not reblock all the data in normal mode.
1177797a0b63SMatthew Dillon 	 */
11786a6e350fSMatthew Dillon 	runcmd(NULL,
11796a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
11806a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11816a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
11826a6e350fSMatthew Dillon 		printf("\n");
11836a6e350fSMatthew Dillon 	return(0);
11846a6e350fSMatthew Dillon }
11856a6e350fSMatthew Dillon 
1186005a4da7STomohiro Kusumi static
1187005a4da7STomohiro Kusumi int
cleanup_recopy(const char * path,const char * snapshots_path,int arg1 __unused,int arg2)11886a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path,
11896a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
11906a6e350fSMatthew Dillon {
11916a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11926a6e350fSMatthew Dillon 		printf(".");
11936a6e350fSMatthew Dillon 		fflush(stdout);
11946a6e350fSMatthew Dillon 	}
11956a6e350fSMatthew Dillon 	runcmd(NULL,
11966a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
11976a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11986a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11996a6e350fSMatthew Dillon 		printf(".");
12006a6e350fSMatthew Dillon 		fflush(stdout);
12016a6e350fSMatthew Dillon 	}
12026a6e350fSMatthew Dillon 	runcmd(NULL,
12036a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
12046a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
12056a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
12066a6e350fSMatthew Dillon 		printf(".");
12076a6e350fSMatthew Dillon 		fflush(stdout);
12086a6e350fSMatthew Dillon 	}
12096a6e350fSMatthew Dillon 	runcmd(NULL,
1210797a0b63SMatthew Dillon 	       "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
1211797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1212797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1213797a0b63SMatthew Dillon 		printf(".");
1214797a0b63SMatthew Dillon 		fflush(stdout);
1215797a0b63SMatthew Dillon 	}
1216797a0b63SMatthew Dillon 	runcmd(NULL,
12176a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
12186a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
12196a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
12206a6e350fSMatthew Dillon 		printf("\n");
12216a6e350fSMatthew Dillon 	return(0);
12226a6e350fSMatthew Dillon }
12236a6e350fSMatthew Dillon 
1224005a4da7STomohiro Kusumi static
1225005a4da7STomohiro Kusumi int
cleanup_dedup(const char * path,const char * snapshots_path __unused,int arg1 __unused,int arg2)1226bb29b5d8SMatthew Dillon cleanup_dedup(const char *path, const char *snapshots_path __unused,
1227bb29b5d8SMatthew Dillon 		  int arg1 __unused, int arg2)
1228bb29b5d8SMatthew Dillon {
1229bb29b5d8SMatthew Dillon 	if (VerboseOpt == 0) {
1230bb29b5d8SMatthew Dillon 		printf(".");
1231bb29b5d8SMatthew Dillon 		fflush(stdout);
1232bb29b5d8SMatthew Dillon 	}
1233bb29b5d8SMatthew Dillon 
1234bb29b5d8SMatthew Dillon 	runcmd(NULL, "hammer -t %d dedup %s", arg2, path);
1235bb29b5d8SMatthew Dillon 	if (VerboseOpt == 0) {
1236bb29b5d8SMatthew Dillon 		printf(".");
1237bb29b5d8SMatthew Dillon 		fflush(stdout);
1238bb29b5d8SMatthew Dillon 	}
1239bb29b5d8SMatthew Dillon 	if (VerboseOpt == 0)
1240bb29b5d8SMatthew Dillon 		printf("\n");
1241bb29b5d8SMatthew Dillon 	return(0);
1242bb29b5d8SMatthew Dillon }
1243bb29b5d8SMatthew Dillon 
12446a6e350fSMatthew Dillon static
12456a6e350fSMatthew Dillon void
runcmd(int * resp,const char * ctl,...)12466a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...)
12476a6e350fSMatthew Dillon {
12486a6e350fSMatthew Dillon 	va_list va;
12496a6e350fSMatthew Dillon 	char *cmd;
12506a6e350fSMatthew Dillon 	char *arg;
12516a6e350fSMatthew Dillon 	char **av;
12526a6e350fSMatthew Dillon 	int n;
12536a6e350fSMatthew Dillon 	int nmax;
12546a6e350fSMatthew Dillon 	int res;
12556a6e350fSMatthew Dillon 	pid_t pid;
12566a6e350fSMatthew Dillon 
12576a6e350fSMatthew Dillon 	/*
12586a6e350fSMatthew Dillon 	 * Generate the command
12596a6e350fSMatthew Dillon 	 */
12606a6e350fSMatthew Dillon 	va_start(va, ctl);
12616a6e350fSMatthew Dillon 	vasprintf(&cmd, ctl, va);
12626a6e350fSMatthew Dillon 	va_end(va);
12636a6e350fSMatthew Dillon 	if (VerboseOpt)
12646a6e350fSMatthew Dillon 		printf("    %s\n", cmd);
12656a6e350fSMatthew Dillon 
12666a6e350fSMatthew Dillon 	/*
12676a6e350fSMatthew Dillon 	 * Break us down into arguments.  We do not just use system() here
12686a6e350fSMatthew Dillon 	 * because it blocks SIGINT and friends.
12696a6e350fSMatthew Dillon 	 */
12706a6e350fSMatthew Dillon 	n = 0;
12716a6e350fSMatthew Dillon 	nmax = 16;
12726a6e350fSMatthew Dillon 	av = malloc(sizeof(char *) * nmax);
12736a6e350fSMatthew Dillon 
12746a6e350fSMatthew Dillon 	for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
1275c453712aSMatthew Dillon 		if (n == nmax - 1) {
12766a6e350fSMatthew Dillon 			nmax += 16;
12776a6e350fSMatthew Dillon 			av = realloc(av, sizeof(char *) * nmax);
12786a6e350fSMatthew Dillon 		}
12796a6e350fSMatthew Dillon 		av[n++] = arg;
12806a6e350fSMatthew Dillon 	}
1281c453712aSMatthew Dillon 	av[n++] = NULL;
12826a6e350fSMatthew Dillon 
12836a6e350fSMatthew Dillon 	/*
12846a6e350fSMatthew Dillon 	 * Run the command.
12856a6e350fSMatthew Dillon 	 */
1286445faa69SMatthew Dillon 	RunningIoctl = 1;
12876a6e350fSMatthew Dillon 	if ((pid = fork()) == 0) {
12886a6e350fSMatthew Dillon 		if (VerboseOpt < 2) {
12896a6e350fSMatthew Dillon 			int fd = open("/dev/null", O_RDWR);
12906a6e350fSMatthew Dillon 			dup2(fd, 1);
12916a6e350fSMatthew Dillon 			close(fd);
12926a6e350fSMatthew Dillon 		}
12936a6e350fSMatthew Dillon 		execvp(av[0], av);
12946a6e350fSMatthew Dillon 		_exit(127);
12956a6e350fSMatthew Dillon 	} else if (pid < 0) {
12966a6e350fSMatthew Dillon 		res = 127;
12976a6e350fSMatthew Dillon 	} else {
12986a6e350fSMatthew Dillon 		int status;
1299445faa69SMatthew Dillon 
13006a6e350fSMatthew Dillon 		while (waitpid(pid, &status, 0) != pid)
13016a6e350fSMatthew Dillon 			;
13026a6e350fSMatthew Dillon 		res = WEXITSTATUS(status);
13036a6e350fSMatthew Dillon 	}
1304445faa69SMatthew Dillon 	RunningIoctl = 0;
1305445faa69SMatthew Dillon 	if (DidInterrupt)
1306445faa69SMatthew Dillon 		_exit(1);
13076a6e350fSMatthew Dillon 
13086a6e350fSMatthew Dillon 	free(cmd);
13096a6e350fSMatthew Dillon 	free(av);
13106a6e350fSMatthew Dillon 	if (resp)
13116a6e350fSMatthew Dillon 		*resp = res;
13126a6e350fSMatthew Dillon }
1313