xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision 46137e17)
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 
61a360fddeSJohn Marino #include <libutil.h>
62b45803e3STomohiro Kusumi 
636a6e350fSMatthew Dillon #include "hammer.h"
646a6e350fSMatthew Dillon 
65c453712aSMatthew Dillon struct didpfs {
66c453712aSMatthew Dillon 	struct didpfs *next;
67c453712aSMatthew Dillon 	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 
102b5ec5ad4SMatthew Dillon /*
103b5ec5ad4SMatthew Dillon  * WARNING: Do not make the SNAPSHOTS_BASE "/var/snapshots" because
104b5ec5ad4SMatthew Dillon  * it will interfere with the older HAMMER VERS < 3 snapshots directory
105b5ec5ad4SMatthew Dillon  * for the /var PFS.
106b5ec5ad4SMatthew Dillon  */
107b5ec5ad4SMatthew Dillon #define SNAPSHOTS_BASE	"/var/hammer"	/* HAMMER VERS >= 3 */
1086a6e350fSMatthew Dillon #define WS	" \t\r\n"
1096a6e350fSMatthew Dillon 
110c453712aSMatthew Dillon struct didpfs *FirstPFS;
1116a6e350fSMatthew Dillon 
1126a6e350fSMatthew Dillon void
1136a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
1146a6e350fSMatthew Dillon {
115eac446c5SMatthew Dillon 	char *fstype, *fs, *path;
116eac446c5SMatthew Dillon 	struct statfs *stfsbuf;
117eac446c5SMatthew Dillon 	int mntsize, i;
1186a6e350fSMatthew Dillon 
1196a6e350fSMatthew Dillon 	tzset();
1206a6e350fSMatthew Dillon 	if (ac == 0) {
121eac446c5SMatthew Dillon 		mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
122eac446c5SMatthew Dillon 		if (mntsize > 0) {
123eac446c5SMatthew Dillon 			for (i=0; i < mntsize; i++) {
124eac446c5SMatthew Dillon 				/*
125eac446c5SMatthew Dillon 				 * We will cleanup in the case fstype is hammer.
126eac446c5SMatthew Dillon 				 * If we have null-mounted PFS, we check the
127eac446c5SMatthew Dillon 				 * mount source. If it looks like a PFS, we
128eac446c5SMatthew Dillon 				 * proceed to cleanup also.
129eac446c5SMatthew Dillon 				 */
130eac446c5SMatthew Dillon 				fstype = stfsbuf[i].f_fstypename;
131eac446c5SMatthew Dillon 				fs = stfsbuf[i].f_mntfromname;
132eac446c5SMatthew Dillon 				if ((strcmp(fstype, "hammer") == 0) ||
133eac446c5SMatthew Dillon 				    ((strcmp(fstype, "null") == 0) &&
134cb3c760cSMatthew Dillon 				     (strstr(fs, "/@@0x") != NULL ||
135cb3c760cSMatthew Dillon 				      strstr(fs, "/@@-1") != NULL))) {
136eac446c5SMatthew Dillon 					path = stfsbuf[i].f_mntonname;
1376a6e350fSMatthew Dillon 					do_cleanup(path);
1386a6e350fSMatthew Dillon 				}
139574066d3SMatthew Dillon 			}
140eac446c5SMatthew Dillon 		}
141eac446c5SMatthew Dillon 
1426a6e350fSMatthew Dillon 	} else {
1436a6e350fSMatthew Dillon 		while (ac) {
1446a6e350fSMatthew Dillon 			do_cleanup(*av);
1456a6e350fSMatthew Dillon 			--ac;
1466a6e350fSMatthew Dillon 			++av;
1476a6e350fSMatthew Dillon 		}
1486a6e350fSMatthew Dillon 	}
1496a6e350fSMatthew Dillon }
1506a6e350fSMatthew Dillon 
1516a6e350fSMatthew Dillon static
1526a6e350fSMatthew Dillon void
1536a6e350fSMatthew Dillon do_cleanup(const char *path)
1546a6e350fSMatthew Dillon {
1556a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
15683f2a3aaSMatthew Dillon 	struct hammer_ioc_config config;
15783f2a3aaSMatthew Dillon 	struct hammer_ioc_version version;
1586a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
15982893617SThomas Nikolajsen 	char *snapshots_path = NULL;
1606a6e350fSMatthew Dillon 	char *config_path;
1616a6e350fSMatthew Dillon 	struct stat st;
1626a6e350fSMatthew Dillon 	char *cmd;
1636a6e350fSMatthew Dillon 	char *ptr;
1646a6e350fSMatthew Dillon 	int arg1;
1656a6e350fSMatthew Dillon 	int arg2;
1665e435c92SMatthew Dillon 	char *arg3;
1676a6e350fSMatthew Dillon 	time_t savet;
1686a6e350fSMatthew Dillon 	char buf[256];
16983f2a3aaSMatthew Dillon 	char *cbase;
17083f2a3aaSMatthew Dillon 	char *cptr;
17182893617SThomas Nikolajsen 	FILE *fp = NULL;
172c453712aSMatthew Dillon 	struct didpfs *didpfs;
173c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
174c6c298a7SMatthew Dillon 	int prune_warning = 0;
17583f2a3aaSMatthew Dillon 	int new_config = 0;
176b5ec5ad4SMatthew Dillon 	int snapshots_from_pfs = 0;
1776a6e350fSMatthew Dillon 	int fd;
1786a6e350fSMatthew Dillon 	int r;
1790b8bd7daSMatthew Dillon 	int found_rebal = 0;
1806a6e350fSMatthew Dillon 
1816a6e350fSMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1826a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1836a6e350fSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
1846a6e350fSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
1856a6e350fSMatthew Dillon 	pfs.pfs_id = -1;
1866a6e350fSMatthew Dillon 
1876a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1886a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1896a6e350fSMatthew Dillon 	if (fd < 0) {
1906a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1916a6e350fSMatthew Dillon 		return;
1926a6e350fSMatthew Dillon 	}
19383f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
1946a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
19583f2a3aaSMatthew Dillon 		close(fd);
1966a6e350fSMatthew Dillon 		return;
1976a6e350fSMatthew Dillon 	}
1986a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1996a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
20083f2a3aaSMatthew Dillon 		close(fd);
2016a6e350fSMatthew Dillon 		return;
2026a6e350fSMatthew Dillon 	}
20383f2a3aaSMatthew Dillon 	bzero(&version, sizeof(version));
20483f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) {
20583f2a3aaSMatthew Dillon 		printf(" HAMMER filesystem but couldn't retrieve version!\n");
20683f2a3aaSMatthew Dillon 		close(fd);
20783f2a3aaSMatthew Dillon 		return;
20883f2a3aaSMatthew Dillon 	}
20983f2a3aaSMatthew Dillon 
21083f2a3aaSMatthew Dillon 	bzero(&config, sizeof(config));
21183f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
21283f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 &&
21383f2a3aaSMatthew Dillon 		    config.head.error == 0) {
21483f2a3aaSMatthew Dillon 			new_config = 1;
21583f2a3aaSMatthew Dillon 		}
21683f2a3aaSMatthew Dillon 	}
2176a6e350fSMatthew Dillon 
2186a6e350fSMatthew Dillon 	/*
2196a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
2206a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
2216a6e350fSMatthew Dillon 	 */
222c453712aSMatthew Dillon 	for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
223c453712aSMatthew Dillon 		if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
224bb8e52c0SThomas Nikolajsen 			printf(" PFS #%d already handled\n", pfs.pfs_id);
22583f2a3aaSMatthew Dillon 			close(fd);
2266a6e350fSMatthew Dillon 			return;
2276a6e350fSMatthew Dillon 		}
228c453712aSMatthew Dillon 	}
229c453712aSMatthew Dillon 	didpfs = malloc(sizeof(*didpfs));
230c453712aSMatthew Dillon 	didpfs->next = FirstPFS;
231c453712aSMatthew Dillon 	FirstPFS = didpfs;
232c453712aSMatthew Dillon 	didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
2336a6e350fSMatthew Dillon 
2346a6e350fSMatthew Dillon 	/*
235b5ec5ad4SMatthew Dillon 	 * Calculate the old snapshots directory for HAMMER VERSION < 3
236b5ec5ad4SMatthew Dillon 	 *
237b5ec5ad4SMatthew Dillon 	 * If the directory is explicitly specified in the PFS config
238b5ec5ad4SMatthew Dillon 	 * we flag it and will not migrate it later.
239ff1c9800SMatthew Dillon 	 */
240ff1c9800SMatthew Dillon 	if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
241ff1c9800SMatthew Dillon 		asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
242b5ec5ad4SMatthew Dillon 		snapshots_from_pfs = 1;
243ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
244ff1c9800SMatthew Dillon 		printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
24583f2a3aaSMatthew Dillon 		close(fd);
246ff1c9800SMatthew Dillon 		return;
247ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
24882893617SThomas Nikolajsen 		if (version.cur_version < 3) {
249ff1c9800SMatthew Dillon 			printf(" WARNING: must configure snapshot dir for PFS slave\n");
250ff1c9800SMatthew Dillon 			printf("\tWe suggest <fs>/var/slaves/<name> where "
251ff1c9800SMatthew Dillon 			       "<fs> is the base HAMMER fs\n");
252bb8e52c0SThomas Nikolajsen 			printf("\tcontaining the slave\n");
25383f2a3aaSMatthew Dillon 			close(fd);
254ff1c9800SMatthew Dillon 			return;
25582893617SThomas Nikolajsen 		}
256ff1c9800SMatthew Dillon 	} else {
257ff1c9800SMatthew Dillon 		asprintf(&snapshots_path,
258ff1c9800SMatthew Dillon 			 "%s%ssnapshots", path, dividing_slash(path));
259ff1c9800SMatthew Dillon 	}
260ff1c9800SMatthew Dillon 
261ff1c9800SMatthew Dillon 	/*
262b5ec5ad4SMatthew Dillon 	 * Check for old-style config file
2636a6e350fSMatthew Dillon 	 */
26482893617SThomas Nikolajsen 	if (snapshots_path) {
2656a6e350fSMatthew Dillon 		asprintf(&config_path, "%s/config", snapshots_path);
2666a6e350fSMatthew Dillon 		fp = fopen(config_path, "r");
26782893617SThomas Nikolajsen 	}
26883f2a3aaSMatthew Dillon 
26983f2a3aaSMatthew Dillon 	/*
27083f2a3aaSMatthew Dillon 	 * Handle upgrades to hammer version 3, move the config
27183f2a3aaSMatthew Dillon 	 * file into meta-data.
27283f2a3aaSMatthew Dillon 	 *
27383f2a3aaSMatthew Dillon 	 * For the old config read the file into the config structure,
27483f2a3aaSMatthew Dillon 	 * we will parse it out of the config structure regardless.
27583f2a3aaSMatthew Dillon 	 */
27683f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
27783f2a3aaSMatthew Dillon 		if (fp) {
27883f2a3aaSMatthew Dillon 			printf("(migrating) ");
27983f2a3aaSMatthew Dillon 			fflush(stdout);
28083f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
28183f2a3aaSMatthew Dillon 			migrate_snapshots(fd, snapshots_path);
28283f2a3aaSMatthew Dillon 			fclose(fp);
28383f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
28483f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
28583f2a3aaSMatthew Dillon 				close(fd);
2866a6e350fSMatthew Dillon 				return;
2876a6e350fSMatthew Dillon 			}
28883f2a3aaSMatthew Dillon 			remove(config_path);
28983f2a3aaSMatthew Dillon 		} else if (new_config == 0) {
29083f2a3aaSMatthew Dillon 			config_init(path, &config);
29183f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
29283f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
29383f2a3aaSMatthew Dillon 				close(fd);
29483f2a3aaSMatthew Dillon 				return;
29583f2a3aaSMatthew Dillon 			}
29683f2a3aaSMatthew Dillon 		}
29783f2a3aaSMatthew Dillon 		new_config = 1;
29883f2a3aaSMatthew Dillon 	} else {
299b5ec5ad4SMatthew Dillon 		/*
300b5ec5ad4SMatthew Dillon 		 * Create missing snapshots directory for HAMMER VERSION < 3
301b5ec5ad4SMatthew Dillon 		 */
302b5ec5ad4SMatthew Dillon 		if (stat(snapshots_path, &st) < 0) {
303b5ec5ad4SMatthew Dillon 			if (mkdir(snapshots_path, 0755) != 0) {
304b5ec5ad4SMatthew Dillon 				free(snapshots_path);
305b5ec5ad4SMatthew Dillon 				printf(" unable to create snapshot dir \"%s\": %s\n",
306b5ec5ad4SMatthew Dillon 					snapshots_path, strerror(errno));
307b5ec5ad4SMatthew Dillon 				close(fd);
308b5ec5ad4SMatthew Dillon 				return;
309b5ec5ad4SMatthew Dillon 			}
310b5ec5ad4SMatthew Dillon 		}
311b5ec5ad4SMatthew Dillon 
312b5ec5ad4SMatthew Dillon 		/*
313b5ec5ad4SMatthew Dillon 		 *  Create missing config file for HAMMER VERSION < 3
314b5ec5ad4SMatthew Dillon 		 */
31583f2a3aaSMatthew Dillon 		if (fp == NULL) {
31683f2a3aaSMatthew Dillon 			config_init(path, &config);
31783f2a3aaSMatthew Dillon 			fp = fopen(config_path, "w");
31883f2a3aaSMatthew Dillon 			if (fp) {
31983f2a3aaSMatthew Dillon 				fwrite(config.config.text, 1,
32083f2a3aaSMatthew Dillon 					strlen(config.config.text), fp);
32183f2a3aaSMatthew Dillon 				fclose(fp);
32283f2a3aaSMatthew Dillon 			}
32383f2a3aaSMatthew Dillon 		} else {
32483f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
32583f2a3aaSMatthew Dillon 			fclose(fp);
32683f2a3aaSMatthew Dillon 		}
32783f2a3aaSMatthew Dillon 	}
3286a6e350fSMatthew Dillon 
329b5ec5ad4SMatthew Dillon 	/*
330b5ec5ad4SMatthew Dillon 	 * If snapshots_from_pfs is not set we calculate the new snapshots
331b5ec5ad4SMatthew Dillon 	 * directory default (in /var) for HAMMER VERSION >= 3 and migrate
332b5ec5ad4SMatthew Dillon 	 * the old snapshots directory over.
333b5ec5ad4SMatthew Dillon 	 *
334b5ec5ad4SMatthew Dillon 	 * People who have set an explicit snapshots directory will have
335b5ec5ad4SMatthew Dillon 	 * to migrate the data manually into /var/hammer, or not bother at
336b5ec5ad4SMatthew Dillon 	 * all.  People running slaves may wish to migrate it and then
337b5ec5ad4SMatthew Dillon 	 * clear the snapshots specification in the PFS config for the
338b5ec5ad4SMatthew Dillon 	 * slave.
339b5ec5ad4SMatthew Dillon 	 */
340b5ec5ad4SMatthew Dillon 	if (new_config && snapshots_from_pfs == 0) {
341b5ec5ad4SMatthew Dillon 		char *npath;
342b5ec5ad4SMatthew Dillon 
343b5ec5ad4SMatthew Dillon 		assert(path[0] == '/');
344b5ec5ad4SMatthew Dillon 		if (strcmp(path, "/") == 0)
345b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/root", SNAPSHOTS_BASE);
346b5ec5ad4SMatthew Dillon 		else
347b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1);
34882893617SThomas Nikolajsen 		if (snapshots_path) {
349b5ec5ad4SMatthew Dillon 			if (stat(npath, &st) < 0 && errno == ENOENT) {
350b5ec5ad4SMatthew Dillon 				if (stat(snapshots_path, &st) < 0 && errno == ENOENT) {
351b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Creating snapshots\n"
352b5ec5ad4SMatthew Dillon 					       "\tCreating snapshots in %s\n",
353b5ec5ad4SMatthew Dillon 					       npath);
354b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
355b5ec5ad4SMatthew Dillon 				} else {
356b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Moving snapshots\n"
357b5ec5ad4SMatthew Dillon 					       "\tMoving snapshots from %s to %s\n",
358b5ec5ad4SMatthew Dillon 					       snapshots_path, npath);
359b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
360b5ec5ad4SMatthew Dillon 					runcmd(&r, "cpdup %s %s", snapshots_path, npath);
361b5ec5ad4SMatthew Dillon 					if (r != 0) {
362b5ec5ad4SMatthew Dillon 				    printf("Unable to move snapshots directory!\n");
363b5ec5ad4SMatthew Dillon 				    printf("Please fix this critical error.\n");
364b5ec5ad4SMatthew Dillon 				    printf("Aborting cleanup of %s\n", path);
365b5ec5ad4SMatthew Dillon 						close(fd);
366b5ec5ad4SMatthew Dillon 						return;
367b5ec5ad4SMatthew Dillon 					}
368b5ec5ad4SMatthew Dillon 					runcmd(&r, "rm -rf %s", snapshots_path);
369b5ec5ad4SMatthew Dillon 				}
370b5ec5ad4SMatthew Dillon 			}
371b5ec5ad4SMatthew Dillon 			free(snapshots_path);
37282893617SThomas Nikolajsen 		} else if (stat(npath, &st) < 0 && errno == ENOENT) {
37382893617SThomas Nikolajsen 			runcmd(&r, "mkdir -p %s", npath);
37482893617SThomas Nikolajsen 		}
375b5ec5ad4SMatthew Dillon 		snapshots_path = npath;
376b5ec5ad4SMatthew Dillon 	}
377b5ec5ad4SMatthew Dillon 
378b5ec5ad4SMatthew Dillon 	/*
379b5ec5ad4SMatthew Dillon 	 * Lock the PFS.  fd is the base directory of the mounted PFS.
380b5ec5ad4SMatthew Dillon 	 */
38183f2a3aaSMatthew Dillon 	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
3825bd5f172SSimon Schubert 		if (errno == EWOULDBLOCK)
3835bd5f172SSimon Schubert 			printf(" PFS #%d locked by other process\n", pfs.pfs_id);
3845bd5f172SSimon Schubert 		else
3855bd5f172SSimon Schubert 			printf(" can not lock %s: %s\n", config_path, strerror(errno));
38683f2a3aaSMatthew Dillon 		close(fd);
3875bd5f172SSimon Schubert 		return;
3885bd5f172SSimon Schubert 	}
3895bd5f172SSimon Schubert 
390ff1c9800SMatthew Dillon 	printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
3916a6e350fSMatthew Dillon 
392a360fddeSJohn Marino 	struct pidfh	*pfh = NULL;
393a360fddeSJohn Marino 	static char	pidfile[PIDFILE_BUFSIZE];
394a360fddeSJohn Marino 
395a360fddeSJohn Marino 	snprintf (pidfile, PIDFILE_BUFSIZE, "%s/hammer.cleanup.%d",
396a360fddeSJohn Marino 		pidfile_loc, getpid());
397a360fddeSJohn Marino 	pfh = pidfile_open(pidfile, 0644, NULL);
398a360fddeSJohn Marino 	if (pfh == NULL) {
399a360fddeSJohn Marino 		warn ("Unable to open or create %s", pidfile);
400a360fddeSJohn Marino 	}
401a360fddeSJohn Marino 	pidfile_write(pfh);
402a360fddeSJohn Marino 
4036a6e350fSMatthew Dillon 	/*
4046a6e350fSMatthew Dillon 	 * Process the config file
4056a6e350fSMatthew Dillon 	 */
40683f2a3aaSMatthew Dillon 	cbase = config.config.text;
40783f2a3aaSMatthew Dillon 
40883f2a3aaSMatthew Dillon 	while ((cptr = strchr(cbase, '\n')) != NULL) {
40983f2a3aaSMatthew Dillon 		bcopy(cbase, buf, cptr - cbase);
41083f2a3aaSMatthew Dillon 		buf[cptr - cbase] = 0;
41183f2a3aaSMatthew Dillon 		cbase = cptr + 1;
41283f2a3aaSMatthew Dillon 
4136a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
414743332abSMatthew Dillon 		if (cmd == NULL || cmd[0] == '#')
415743332abSMatthew Dillon 			continue;
416743332abSMatthew Dillon 
4176a6e350fSMatthew Dillon 		arg1 = 0;
4186a6e350fSMatthew Dillon 		arg2 = 0;
4195e435c92SMatthew Dillon 		arg3 = NULL;
4206a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
4216a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
4225e435c92SMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL) {
4236a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
4245e435c92SMatthew Dillon 				arg3 = strtok(NULL, WS);
4255e435c92SMatthew Dillon 			}
4266a6e350fSMatthew Dillon 		}
4276a6e350fSMatthew Dillon 
4286a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
4296a6e350fSMatthew Dillon 		fflush(stdout);
4306a6e350fSMatthew Dillon 
4316a6e350fSMatthew Dillon 		r = 1;
4326a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
4335e435c92SMatthew Dillon 			if (arg1 == 0) {
43483f2a3aaSMatthew Dillon 				if (arg2 &&
43583f2a3aaSMatthew Dillon 				    check_softlinks(fd, new_config,
43683f2a3aaSMatthew Dillon 						    snapshots_path)) {
4375e435c92SMatthew Dillon 					printf("only removing old snapshots\n");
4385e435c92SMatthew Dillon 					prune_warning = 1;
43983f2a3aaSMatthew Dillon 					cleanup_softlinks(fd, new_config,
44083f2a3aaSMatthew Dillon 							  snapshots_path,
4415e435c92SMatthew Dillon 							  arg2, arg3);
4425e435c92SMatthew Dillon 				} else {
4435e435c92SMatthew Dillon 					printf("disabled\n");
4445e435c92SMatthew Dillon 					snapshots_disabled = 1;
4455e435c92SMatthew Dillon 				}
4465e435c92SMatthew Dillon 			} else
4476a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4486a6e350fSMatthew Dillon 				printf("run\n");
44983f2a3aaSMatthew Dillon 				cleanup_softlinks(fd, new_config,
45083f2a3aaSMatthew Dillon 						  snapshots_path,
4515e435c92SMatthew Dillon 						  arg2, arg3);
452b5ec5ad4SMatthew Dillon 				r = create_snapshot(path, snapshots_path);
4536a6e350fSMatthew Dillon 			} else {
4546a6e350fSMatthew Dillon 				printf("skip\n");
4556a6e350fSMatthew Dillon 			}
4565e435c92SMatthew Dillon 		} else if (arg1 == 0) {
4575e435c92SMatthew Dillon 			/*
4585e435c92SMatthew Dillon 			 * The commands following this check can't handle
4595e435c92SMatthew Dillon 			 * a period of 0, so call the feature disabled and
4605e435c92SMatthew Dillon 			 * ignore the directive.
4615e435c92SMatthew Dillon 			 */
4625e435c92SMatthew Dillon 			printf("disabled\n");
4636a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
4646a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4655e435c92SMatthew Dillon 				if (prune_warning) {
4665e435c92SMatthew Dillon 					printf("run - WARNING snapshot "
4675e435c92SMatthew Dillon 					       "softlinks present "
468226f3799SThomas Nikolajsen 					       "but snapshots disabled\n");
4695e435c92SMatthew Dillon 				} else {
470c6c298a7SMatthew Dillon 					printf("run\n");
4715e435c92SMatthew Dillon 				}
4726a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
473c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
4746a6e350fSMatthew Dillon 			} else {
4756a6e350fSMatthew Dillon 				printf("skip\n");
4766a6e350fSMatthew Dillon 			}
4770b8bd7daSMatthew Dillon 		} else if (strcmp(cmd, "rebalance") == 0) {
4780b8bd7daSMatthew Dillon 			found_rebal = 1;
4790b8bd7daSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4800b8bd7daSMatthew Dillon 				printf("run");
4810b8bd7daSMatthew Dillon 				fflush(stdout);
4820b8bd7daSMatthew Dillon 				if (VerboseOpt)
4830b8bd7daSMatthew Dillon 					printf("\n");
4840b8bd7daSMatthew Dillon 				r = cleanup_rebalance(path, snapshots_path,
4850b8bd7daSMatthew Dillon 						arg1, arg2);
4860b8bd7daSMatthew Dillon 			} else {
4870b8bd7daSMatthew Dillon 				printf("skip\n");
4880b8bd7daSMatthew Dillon 			}
4896a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
4906a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4916a6e350fSMatthew Dillon 				printf("run");
4926a6e350fSMatthew Dillon 				fflush(stdout);
4936a6e350fSMatthew Dillon 				if (VerboseOpt)
4946a6e350fSMatthew Dillon 					printf("\n");
4956a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
4966a6e350fSMatthew Dillon 						arg1, arg2);
4976a6e350fSMatthew Dillon 			} else {
4986a6e350fSMatthew Dillon 				printf("skip\n");
4996a6e350fSMatthew Dillon 			}
5006a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
5016a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
5026a6e350fSMatthew Dillon 				printf("run");
5036a6e350fSMatthew Dillon 				fflush(stdout);
5046a6e350fSMatthew Dillon 				if (VerboseOpt)
5056a6e350fSMatthew Dillon 					printf("\n");
5066a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
5076a6e350fSMatthew Dillon 					       arg1, arg2);
5086a6e350fSMatthew Dillon 			} else {
5096a6e350fSMatthew Dillon 				printf("skip\n");
5106a6e350fSMatthew Dillon 			}
511bb29b5d8SMatthew Dillon 		} else if (strcmp(cmd, "dedup") == 0) {
512bb29b5d8SMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
513bb29b5d8SMatthew Dillon 				printf("run");
514bb29b5d8SMatthew Dillon 				fflush(stdout);
515bb29b5d8SMatthew Dillon 				if (VerboseOpt)
516bb29b5d8SMatthew Dillon 					printf("\n");
517bb29b5d8SMatthew Dillon 				r = cleanup_dedup(path, snapshots_path,
518bb29b5d8SMatthew Dillon 						arg1, arg2);
519bb29b5d8SMatthew Dillon 			} else {
520bb29b5d8SMatthew Dillon 				printf("skip\n");
521bb29b5d8SMatthew Dillon 			}
5226a6e350fSMatthew Dillon 		} else {
5236a6e350fSMatthew Dillon 			printf("unknown directive\n");
5246a6e350fSMatthew Dillon 			r = 1;
5256a6e350fSMatthew Dillon 		}
5266a6e350fSMatthew Dillon 		if (r == 0)
5276a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
5286a6e350fSMatthew Dillon 	}
5290b8bd7daSMatthew Dillon 
5300b8bd7daSMatthew Dillon 	/*
53183f2a3aaSMatthew Dillon 	 * Add new rebalance feature if the config doesn't have it.
532b5ec5ad4SMatthew Dillon 	 * (old style config only).
5330b8bd7daSMatthew Dillon 	 */
53483f2a3aaSMatthew Dillon 	if (new_config == 0 && found_rebal == 0) {
5350b8bd7daSMatthew Dillon 		if ((fp = fopen(config_path, "r+")) != NULL) {
5360b8bd7daSMatthew Dillon 			fseek(fp, 0L, 2);
5370b8bd7daSMatthew Dillon 			fprintf(fp, "rebalance 1d 5m\n");
5380b8bd7daSMatthew Dillon 			fclose(fp);
5390b8bd7daSMatthew Dillon 		}
5400b8bd7daSMatthew Dillon 	}
54183f2a3aaSMatthew Dillon 
54283f2a3aaSMatthew Dillon 	/*
54383f2a3aaSMatthew Dillon 	 * Cleanup, and delay a little
54483f2a3aaSMatthew Dillon 	 */
54583f2a3aaSMatthew Dillon 	close(fd);
5466a6e350fSMatthew Dillon 	usleep(1000);
547a360fddeSJohn Marino 	pidfile_close(pfh);
548a360fddeSJohn Marino 	pidfile_remove(pfh);
5496a6e350fSMatthew Dillon }
5506a6e350fSMatthew Dillon 
55183f2a3aaSMatthew Dillon /*
55283f2a3aaSMatthew Dillon  * Initialize new config data (new or old style)
55383f2a3aaSMatthew Dillon  */
55483f2a3aaSMatthew Dillon static void
55583f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config)
55683f2a3aaSMatthew Dillon {
55783f2a3aaSMatthew Dillon 	const char *snapshots;
55883f2a3aaSMatthew Dillon 
55983f2a3aaSMatthew Dillon 	if (strcmp(path, "/tmp") == 0 ||
56083f2a3aaSMatthew Dillon 	    strcmp(path, "/var/tmp") == 0 ||
56183f2a3aaSMatthew Dillon 	    strcmp(path, "/usr/obj") == 0) {
56283f2a3aaSMatthew Dillon 		snapshots = "snapshots 0d 0d\n";
56383f2a3aaSMatthew Dillon 	} else {
56483f2a3aaSMatthew Dillon 		snapshots = "snapshots 1d 60d\n";
56583f2a3aaSMatthew Dillon 	}
56683f2a3aaSMatthew Dillon 	bzero(config->config.text, sizeof(config->config.text));
56783f2a3aaSMatthew Dillon 	snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s",
56883f2a3aaSMatthew Dillon 		snapshots,
56983f2a3aaSMatthew Dillon 		"prune     1d 5m\n"
57083f2a3aaSMatthew Dillon 		"rebalance 1d 5m\n"
571bb29b5d8SMatthew Dillon 		"#dedup	   1d 5m\n"
57283f2a3aaSMatthew Dillon 		"reblock   1d 5m\n"
573f6532f03SThomas Nikolajsen 		"recopy    30d 10m\n");
57483f2a3aaSMatthew Dillon }
57583f2a3aaSMatthew Dillon 
57683f2a3aaSMatthew Dillon /*
57783f2a3aaSMatthew Dillon  * Migrate configuration data from the old snapshots/config
57816265794SThomas Nikolajsen  * file to the new meta-data format.
57983f2a3aaSMatthew Dillon  */
58083f2a3aaSMatthew Dillon static void
58183f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config)
58283f2a3aaSMatthew Dillon {
58383f2a3aaSMatthew Dillon 	int n;
58483f2a3aaSMatthew Dillon 
58583f2a3aaSMatthew Dillon 	n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp);
58683f2a3aaSMatthew Dillon 	if (n >= 0)
58783f2a3aaSMatthew Dillon 		bzero(config->config.text + n, sizeof(config->config.text) - n);
58883f2a3aaSMatthew Dillon }
58983f2a3aaSMatthew Dillon 
59083f2a3aaSMatthew Dillon /*
59183f2a3aaSMatthew Dillon  * Migrate snapshot softlinks in the snapshots directory to the
59283f2a3aaSMatthew Dillon  * new meta-data format.  The softlinks are left intact, but
59383f2a3aaSMatthew Dillon  * this way the pruning code won't lose track of them if you
59483f2a3aaSMatthew Dillon  * happen to blow away the snapshots directory.
59583f2a3aaSMatthew Dillon  */
59683f2a3aaSMatthew Dillon static void
59783f2a3aaSMatthew Dillon migrate_snapshots(int fd, const char *snapshots_path)
59883f2a3aaSMatthew Dillon {
59983f2a3aaSMatthew Dillon 	struct hammer_ioc_snapshot snapshot;
60083f2a3aaSMatthew Dillon 	struct dirent *den;
60183f2a3aaSMatthew Dillon 	struct stat st;
60283f2a3aaSMatthew Dillon 	DIR *dir;
60383f2a3aaSMatthew Dillon 	char *fpath;
60483f2a3aaSMatthew Dillon 
60583f2a3aaSMatthew Dillon 	bzero(&snapshot, sizeof(snapshot));
60683f2a3aaSMatthew Dillon 
60783f2a3aaSMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
60883f2a3aaSMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
60983f2a3aaSMatthew Dillon 			if (den->d_name[0] == '.')
61083f2a3aaSMatthew Dillon 				continue;
61183f2a3aaSMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
61283f2a3aaSMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) {
61383f2a3aaSMatthew Dillon 				migrate_one_snapshot(fd, fpath, &snapshot);
61483f2a3aaSMatthew Dillon 			}
61583f2a3aaSMatthew Dillon 			free(fpath);
61683f2a3aaSMatthew Dillon 		}
61783f2a3aaSMatthew Dillon 		closedir(dir);
61883f2a3aaSMatthew Dillon 	}
61983f2a3aaSMatthew Dillon 	migrate_one_snapshot(fd, NULL, &snapshot);
62083f2a3aaSMatthew Dillon 
62183f2a3aaSMatthew Dillon }
62283f2a3aaSMatthew Dillon 
62383f2a3aaSMatthew Dillon /*
62483f2a3aaSMatthew Dillon  * Migrate a single snapshot.  If fpath is NULL the ioctl is flushed,
62583f2a3aaSMatthew Dillon  * otherwise it is flushed when it fills up.
62683f2a3aaSMatthew Dillon  */
62783f2a3aaSMatthew Dillon static void
62883f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath,
62983f2a3aaSMatthew Dillon 		     struct hammer_ioc_snapshot *snapshot)
63083f2a3aaSMatthew Dillon {
63183f2a3aaSMatthew Dillon 	if (fpath) {
63283f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *snap;
63383f2a3aaSMatthew Dillon 		struct tm tm;
63483f2a3aaSMatthew Dillon 		time_t t;
63583f2a3aaSMatthew Dillon 		int year;
63683f2a3aaSMatthew Dillon 		int month;
63783f2a3aaSMatthew Dillon 		int day = 0;
63883f2a3aaSMatthew Dillon 		int hour = 0;
63983f2a3aaSMatthew Dillon 		int minute = 0;
64083f2a3aaSMatthew Dillon 		int r;
64183f2a3aaSMatthew Dillon 		char linkbuf[1024];
64283f2a3aaSMatthew Dillon 		const char *ptr;
64383f2a3aaSMatthew Dillon 		hammer_tid_t tid;
64483f2a3aaSMatthew Dillon 
64583f2a3aaSMatthew Dillon 		t = (time_t)-1;
64683f2a3aaSMatthew Dillon 		tid = (hammer_tid_t)(int64_t)-1;
64783f2a3aaSMatthew Dillon 
648f510bf3eSYONETANI Tomokazu 		/* fpath may contain directory components */
649f510bf3eSYONETANI Tomokazu 		if ((ptr = strrchr(fpath, '/')) != NULL)
650f510bf3eSYONETANI Tomokazu 			++ptr;
651f510bf3eSYONETANI Tomokazu 		else
65283f2a3aaSMatthew Dillon 			ptr = fpath;
65383f2a3aaSMatthew Dillon 		while (*ptr && *ptr != '-' && *ptr != '.')
65483f2a3aaSMatthew Dillon 			++ptr;
65583f2a3aaSMatthew Dillon 		if (*ptr)
65683f2a3aaSMatthew Dillon 			++ptr;
65783f2a3aaSMatthew Dillon 		r = sscanf(ptr, "%4d%2d%2d-%2d%2d",
65883f2a3aaSMatthew Dillon 			   &year, &month, &day, &hour, &minute);
65983f2a3aaSMatthew Dillon 
66083f2a3aaSMatthew Dillon 		if (r >= 3) {
66183f2a3aaSMatthew Dillon 			bzero(&tm, sizeof(tm));
66283f2a3aaSMatthew Dillon 			tm.tm_isdst = -1;
66383f2a3aaSMatthew Dillon 			tm.tm_min = minute;
66483f2a3aaSMatthew Dillon 			tm.tm_hour = hour;
66583f2a3aaSMatthew Dillon 			tm.tm_mday = day;
66683f2a3aaSMatthew Dillon 			tm.tm_mon = month - 1;
66783f2a3aaSMatthew Dillon 			tm.tm_year = year - 1900;
66883f2a3aaSMatthew Dillon 			t = mktime(&tm);
66983f2a3aaSMatthew Dillon 		}
67083f2a3aaSMatthew Dillon 		bzero(linkbuf, sizeof(linkbuf));
67183f2a3aaSMatthew Dillon 		if (readlink(fpath, linkbuf, sizeof(linkbuf) - 1) > 0 &&
67283f2a3aaSMatthew Dillon 		    (ptr = strrchr(linkbuf, '@')) != NULL &&
67383f2a3aaSMatthew Dillon 		    ptr > linkbuf && ptr[-1] == '@') {
67483f2a3aaSMatthew Dillon 			tid = strtoull(ptr + 1, NULL, 16);
67583f2a3aaSMatthew Dillon 		}
67683f2a3aaSMatthew Dillon 		if (t != (time_t)-1 && tid != (hammer_tid_t)(int64_t)-1) {
67783f2a3aaSMatthew Dillon 			snap = &snapshot->snaps[snapshot->count];
67883f2a3aaSMatthew Dillon 			bzero(snap, sizeof(*snap));
67983f2a3aaSMatthew Dillon 			snap->tid = tid;
680*46137e17STomohiro Kusumi 			snap->ts = (uint64_t)t * 1000000ULL;
68183f2a3aaSMatthew Dillon 			snprintf(snap->label, sizeof(snap->label),
68283f2a3aaSMatthew Dillon 				 "migrated");
68383f2a3aaSMatthew Dillon 			++snapshot->count;
684f510bf3eSYONETANI Tomokazu 		} else {
685f510bf3eSYONETANI Tomokazu 			printf("    non-canonical snapshot softlink: %s->%s\n",
686f510bf3eSYONETANI Tomokazu 			       fpath, linkbuf);
68783f2a3aaSMatthew Dillon 		}
68883f2a3aaSMatthew Dillon 	}
68983f2a3aaSMatthew Dillon 
69083f2a3aaSMatthew Dillon 	if ((fpath == NULL && snapshot->count) ||
69183f2a3aaSMatthew Dillon 	    snapshot->count == HAMMER_SNAPS_PER_IOCTL) {
69283f2a3aaSMatthew Dillon 		printf(" (%d snapshots)", snapshot->count);
69383f2a3aaSMatthew Dillon again:
69483f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_ADD_SNAPSHOT, snapshot) < 0) {
69583f2a3aaSMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
69683f2a3aaSMatthew Dillon 			       strerror(errno));
69783f2a3aaSMatthew Dillon 		} else if (snapshot->head.error == EALREADY) {
69883f2a3aaSMatthew Dillon 			++snapshot->index;
69983f2a3aaSMatthew Dillon 			goto again;
70083f2a3aaSMatthew Dillon 		} else if (snapshot->head.error) {
701068da2c2SMatthew Dillon 			printf("    Ioctl to migrate snapshots failed: %s\n",
70283f2a3aaSMatthew Dillon 			       strerror(snapshot->head.error));
70383f2a3aaSMatthew Dillon 		}
70483f2a3aaSMatthew Dillon 		printf("index %d\n", snapshot->index);
70583f2a3aaSMatthew Dillon 		snapshot->index = 0;
70683f2a3aaSMatthew Dillon 		snapshot->count = 0;
70783f2a3aaSMatthew Dillon 		snapshot->head.error = 0;
70883f2a3aaSMatthew Dillon 	}
70983f2a3aaSMatthew Dillon }
71083f2a3aaSMatthew Dillon 
7116a6e350fSMatthew Dillon static
7126a6e350fSMatthew Dillon int
7136a6e350fSMatthew Dillon strtosecs(char *ptr)
7146a6e350fSMatthew Dillon {
7156a6e350fSMatthew Dillon 	int val;
7166a6e350fSMatthew Dillon 
7176a6e350fSMatthew Dillon 	val = strtol(ptr, &ptr, 0);
7186a6e350fSMatthew Dillon 	switch(*ptr) {
7196a6e350fSMatthew Dillon 	case 'd':
7206a6e350fSMatthew Dillon 		val *= 24;
7216a6e350fSMatthew Dillon 		/* fall through */
7226a6e350fSMatthew Dillon 	case 'h':
7236a6e350fSMatthew Dillon 		val *= 60;
7246a6e350fSMatthew Dillon 		/* fall through */
7256a6e350fSMatthew Dillon 	case 'm':
7266a6e350fSMatthew Dillon 		val *= 60;
7276a6e350fSMatthew Dillon 		/* fall through */
7286a6e350fSMatthew Dillon 	case 's':
7296a6e350fSMatthew Dillon 		break;
7306a6e350fSMatthew Dillon 	default:
7316a6e350fSMatthew Dillon 		errx(1, "illegal suffix converting %s\n", ptr);
7326a6e350fSMatthew Dillon 		break;
7336a6e350fSMatthew Dillon 	}
7346a6e350fSMatthew Dillon 	return(val);
7356a6e350fSMatthew Dillon }
7366a6e350fSMatthew Dillon 
7376a6e350fSMatthew Dillon static const char *
7386a6e350fSMatthew Dillon dividing_slash(const char *path)
7396a6e350fSMatthew Dillon {
7406a6e350fSMatthew Dillon 	int len = strlen(path);
7416a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
7426a6e350fSMatthew Dillon 		return("");
7436a6e350fSMatthew Dillon 	else
7446a6e350fSMatthew Dillon 		return("/");
7456a6e350fSMatthew Dillon }
7466a6e350fSMatthew Dillon 
7476a6e350fSMatthew Dillon /*
7486a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
7496a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
7506a6e350fSMatthew Dillon  * current time_t so we can save it later on.
7516a6e350fSMatthew Dillon  *
7526a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
7536a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
7546a6e350fSMatthew Dillon  * of how close the last operation actually was.
755bdc54107SMatthew Dillon  *
756bdc54107SMatthew Dillon  * If ForceOpt is set always return true.
7576a6e350fSMatthew Dillon  */
7586a6e350fSMatthew Dillon static int
7596a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
7606a6e350fSMatthew Dillon 	time_t *savep)
7616a6e350fSMatthew Dillon {
7626a6e350fSMatthew Dillon 	char *check_path;
7636a6e350fSMatthew Dillon 	struct tm tp1;
7646a6e350fSMatthew Dillon 	struct tm tp2;
7656a6e350fSMatthew Dillon 	FILE *fp;
7666a6e350fSMatthew Dillon 	time_t baset, lastt;
7676a6e350fSMatthew Dillon 	char buf[256];
7686a6e350fSMatthew Dillon 
7696a6e350fSMatthew Dillon 	time(savep);
7706a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
7716a6e350fSMatthew Dillon 
7726a6e350fSMatthew Dillon 	/*
773bdc54107SMatthew Dillon 	 * Force run if -F
774bdc54107SMatthew Dillon 	 */
775bdc54107SMatthew Dillon 	if (ForceOpt)
776bdc54107SMatthew Dillon 		return(1);
777bdc54107SMatthew Dillon 
778bdc54107SMatthew Dillon 	/*
7796a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
7806a6e350fSMatthew Dillon 	 */
7816a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
7826a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
7836a6e350fSMatthew Dillon 	free(check_path);
7846a6e350fSMatthew Dillon 	if (fp == NULL)
7856a6e350fSMatthew Dillon 		return(1);
7866a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
7876a6e350fSMatthew Dillon 		fclose(fp);
7886a6e350fSMatthew Dillon 		return(1);
7896a6e350fSMatthew Dillon 	}
7906a6e350fSMatthew Dillon 	fclose(fp);
7916a6e350fSMatthew Dillon 
7926a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
7936a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
7946a6e350fSMatthew Dillon 
7956a6e350fSMatthew Dillon 	/*
7966a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
7976a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
7986a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
7996a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
8006a6e350fSMatthew Dillon 	 */
8016a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
8026a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
8036a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
8046a6e350fSMatthew Dillon 	}
8056a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
8066a6e350fSMatthew Dillon 		tp1.tm_min = 0;
8076a6e350fSMatthew Dillon 		tp2.tm_min = 0;
8086a6e350fSMatthew Dillon 	}
8096a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
8106a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
8116a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
8126a6e350fSMatthew Dillon 	}
8136a6e350fSMatthew Dillon 
8146a6e350fSMatthew Dillon 	baset = mktime(&tp1);
8156a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
8166a6e350fSMatthew Dillon 
8176a6e350fSMatthew Dillon #if 0
8186a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
8196a6e350fSMatthew Dillon #endif
8206a6e350fSMatthew Dillon 
8216a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
8226a6e350fSMatthew Dillon 		return(1);
8236a6e350fSMatthew Dillon 	return(0);
8246a6e350fSMatthew Dillon }
8256a6e350fSMatthew Dillon 
8266a6e350fSMatthew Dillon /*
8276a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
8286a6e350fSMatthew Dillon  */
8296a6e350fSMatthew Dillon static void
8306a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
8316a6e350fSMatthew Dillon 			time_t savet)
8326a6e350fSMatthew Dillon {
8336a6e350fSMatthew Dillon 	char *ocheck_path;
8346a6e350fSMatthew Dillon 	char *ncheck_path;
8356a6e350fSMatthew Dillon 	FILE *fp;
8366a6e350fSMatthew Dillon 
8376a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
8386a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
8396a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
8403b9fbdeaSMatthew Dillon 	if (fp) {
8416a6e350fSMatthew Dillon 		fprintf(fp, "0x%08llx\n", (long long)savet);
8426a6e350fSMatthew Dillon 		if (fclose(fp) == 0)
8436a6e350fSMatthew Dillon 			rename(ncheck_path, ocheck_path);
8446a6e350fSMatthew Dillon 		remove(ncheck_path);
8453b9fbdeaSMatthew Dillon 	} else {
8463b9fbdeaSMatthew Dillon 		fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
8473b9fbdeaSMatthew Dillon 			ncheck_path, strerror(errno));
8483b9fbdeaSMatthew Dillon 	}
8496a6e350fSMatthew Dillon }
8506a6e350fSMatthew Dillon 
851ff1c9800SMatthew Dillon /*
852ff1c9800SMatthew Dillon  * Simply count the number of softlinks in the snapshots dir
853ff1c9800SMatthew Dillon  */
854c6c298a7SMatthew Dillon static int
85583f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path)
856c6c298a7SMatthew Dillon {
857c6c298a7SMatthew Dillon 	struct dirent *den;
858c6c298a7SMatthew Dillon 	struct stat st;
859c6c298a7SMatthew Dillon 	DIR *dir;
860c6c298a7SMatthew Dillon 	char *fpath;
861c6c298a7SMatthew Dillon 	int res = 0;
862c6c298a7SMatthew Dillon 
86383f2a3aaSMatthew Dillon 	/*
86483f2a3aaSMatthew Dillon 	 * Old-style softlink-based snapshots
86583f2a3aaSMatthew Dillon 	 */
866c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
867c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
868c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
869c6c298a7SMatthew Dillon 				continue;
870c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
871c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
872c6c298a7SMatthew Dillon 				++res;
873c6c298a7SMatthew Dillon 			free(fpath);
874c6c298a7SMatthew Dillon 		}
875c6c298a7SMatthew Dillon 		closedir(dir);
876c6c298a7SMatthew Dillon 	}
87783f2a3aaSMatthew Dillon 
87883f2a3aaSMatthew Dillon 	/*
87983f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
88083f2a3aaSMatthew Dillon 	 * count those too.
88183f2a3aaSMatthew Dillon 	 */
88283f2a3aaSMatthew Dillon 	if (new_config) {
88383f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
88483f2a3aaSMatthew Dillon 
88583f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
88683f2a3aaSMatthew Dillon 		do {
88783f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
88883f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
88983f2a3aaSMatthew Dillon 					"snapshot error");
89083f2a3aaSMatthew Dillon 				/* not reached */
89183f2a3aaSMatthew Dillon 			}
89283f2a3aaSMatthew Dillon 			res += snapshot.count;
89383f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
89483f2a3aaSMatthew Dillon 	}
895c6c298a7SMatthew Dillon 	return (res);
896c6c298a7SMatthew Dillon }
897c6c298a7SMatthew Dillon 
8986a6e350fSMatthew Dillon /*
899ff1c9800SMatthew Dillon  * Clean up expired softlinks in the snapshots dir
900ff1c9800SMatthew Dillon  */
901ff1c9800SMatthew Dillon static void
90283f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config,
90383f2a3aaSMatthew Dillon 		  const char *snapshots_path, int arg2, char *arg3)
904ff1c9800SMatthew Dillon {
905ff1c9800SMatthew Dillon 	struct dirent *den;
906ff1c9800SMatthew Dillon 	struct stat st;
907ff1c9800SMatthew Dillon 	DIR *dir;
908ff1c9800SMatthew Dillon 	char *fpath;
9095e435c92SMatthew Dillon 	int anylink = 0;
9105e435c92SMatthew Dillon 
91109e1b0d6SMatthew Dillon 	if (arg3 != NULL && strstr(arg3, "any") != NULL)
9125e435c92SMatthew Dillon 		anylink = 1;
913ff1c9800SMatthew Dillon 
914ff1c9800SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
915ff1c9800SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
916ff1c9800SMatthew Dillon 			if (den->d_name[0] == '.')
917ff1c9800SMatthew Dillon 				continue;
918ff1c9800SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
919ff1c9800SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
9209a620123STomohiro Kusumi 			    (anylink || strncmp(den->d_name, "snap-", 5) == 0)) {
921ff1c9800SMatthew Dillon 				if (check_expired(den->d_name, arg2)) {
922ff1c9800SMatthew Dillon 					if (VerboseOpt) {
923ff1c9800SMatthew Dillon 						printf("    expire %s\n",
924ff1c9800SMatthew Dillon 							fpath);
925ff1c9800SMatthew Dillon 					}
926ff1c9800SMatthew Dillon 					remove(fpath);
927ff1c9800SMatthew Dillon 				}
928ff1c9800SMatthew Dillon 			}
929ff1c9800SMatthew Dillon 			free(fpath);
930ff1c9800SMatthew Dillon 		}
931ff1c9800SMatthew Dillon 		closedir(dir);
932ff1c9800SMatthew Dillon 	}
93383f2a3aaSMatthew Dillon 
93483f2a3aaSMatthew Dillon 	/*
93583f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
93683f2a3aaSMatthew Dillon 	 * count those too.
93783f2a3aaSMatthew Dillon 	 */
93883f2a3aaSMatthew Dillon 	if (new_config) {
93983f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
94083f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot dsnapshot;
94183f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *snap;
94283f2a3aaSMatthew Dillon 		struct tm *tp;
94383f2a3aaSMatthew Dillon 		time_t t;
944ec2aa11dSMatthew Dillon 		time_t dt;
94583f2a3aaSMatthew Dillon 		char snapts[32];
946*46137e17STomohiro Kusumi 		uint32_t i;
94783f2a3aaSMatthew Dillon 
94883f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
94983f2a3aaSMatthew Dillon 		bzero(&dsnapshot, sizeof(dsnapshot));
95083f2a3aaSMatthew Dillon 		do {
95183f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
95283f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
95383f2a3aaSMatthew Dillon 					"snapshot error");
95483f2a3aaSMatthew Dillon 				/* not reached */
95583f2a3aaSMatthew Dillon 			}
95683f2a3aaSMatthew Dillon 			for (i = 0; i < snapshot.count; ++i) {
95783f2a3aaSMatthew Dillon 				snap = &snapshot.snaps[i];
958ec2aa11dSMatthew Dillon 				t = snap->ts / 1000000ULL;
959ec2aa11dSMatthew Dillon 				dt = time(NULL) - t;
960ec2aa11dSMatthew Dillon 				if ((int)dt > arg2 || snap->tid == 0) {
96183f2a3aaSMatthew Dillon 					dsnapshot.snaps[dsnapshot.count++] =
96283f2a3aaSMatthew Dillon 						*snap;
96383f2a3aaSMatthew Dillon 				}
964ec2aa11dSMatthew Dillon 				if ((int)dt > arg2 && VerboseOpt) {
96583f2a3aaSMatthew Dillon 					tp = localtime(&t);
96683f2a3aaSMatthew Dillon 					strftime(snapts, sizeof(snapts),
96783f2a3aaSMatthew Dillon 						 "%Y-%m-%d %H:%M:%S %Z", tp);
96883f2a3aaSMatthew Dillon 					printf("    expire 0x%016jx %s %s\n",
96983f2a3aaSMatthew Dillon 					       (uintmax_t)snap->tid,
97083f2a3aaSMatthew Dillon 					       snapts,
97183f2a3aaSMatthew Dillon 					       snap->label);
97283f2a3aaSMatthew Dillon 				}
973ec2aa11dSMatthew Dillon 				if (dsnapshot.count == HAMMER_SNAPS_PER_IOCTL)
974ec2aa11dSMatthew Dillon 					delete_snapshots(fd, &dsnapshot);
97583f2a3aaSMatthew Dillon 			}
97683f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
97783f2a3aaSMatthew Dillon 
978ec2aa11dSMatthew Dillon 		if (dsnapshot.count)
979ec2aa11dSMatthew Dillon 			delete_snapshots(fd, &dsnapshot);
98083f2a3aaSMatthew Dillon 	}
98183f2a3aaSMatthew Dillon }
982ec2aa11dSMatthew Dillon 
983ec2aa11dSMatthew Dillon static void
984ec2aa11dSMatthew Dillon delete_snapshots(int fd, struct hammer_ioc_snapshot *dsnapshot)
985ec2aa11dSMatthew Dillon {
986ec2aa11dSMatthew Dillon 	for (;;) {
987ec2aa11dSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_DEL_SNAPSHOT, dsnapshot) < 0) {
988ec2aa11dSMatthew Dillon 			printf("    Ioctl to delete snapshots failed: %s\n",
989ec2aa11dSMatthew Dillon 			       strerror(errno));
990ec2aa11dSMatthew Dillon 			break;
991ec2aa11dSMatthew Dillon 		}
992ec2aa11dSMatthew Dillon 		if (dsnapshot->head.error) {
993ec2aa11dSMatthew Dillon 			printf("    Ioctl to delete snapshots failed at "
994ec2aa11dSMatthew Dillon 			       "snap=%016jx: %s\n",
995ec2aa11dSMatthew Dillon 			       dsnapshot->snaps[dsnapshot->index].tid,
996ec2aa11dSMatthew Dillon 			       strerror(dsnapshot->head.error));
997ec2aa11dSMatthew Dillon 			if (++dsnapshot->index < dsnapshot->count)
998ec2aa11dSMatthew Dillon 				continue;
999ec2aa11dSMatthew Dillon 		}
1000ec2aa11dSMatthew Dillon 		break;
1001ec2aa11dSMatthew Dillon 	}
1002ec2aa11dSMatthew Dillon 	dsnapshot->index = 0;
1003ec2aa11dSMatthew Dillon 	dsnapshot->count = 0;
1004ec2aa11dSMatthew Dillon 	dsnapshot->head.error = 0;
1005ff1c9800SMatthew Dillon }
1006ff1c9800SMatthew Dillon 
1007ff1c9800SMatthew Dillon /*
1008ff1c9800SMatthew Dillon  * Take a softlink path in the form snap-yyyymmdd-hhmm and the
1009ff1c9800SMatthew Dillon  * expiration in seconds (arg2) and return non-zero if the softlink
1010ff1c9800SMatthew Dillon  * has expired.
1011ff1c9800SMatthew Dillon  */
1012ff1c9800SMatthew Dillon static int
1013ff1c9800SMatthew Dillon check_expired(const char *fpath, int arg2)
1014ff1c9800SMatthew Dillon {
1015ff1c9800SMatthew Dillon 	struct tm tm;
1016ff1c9800SMatthew Dillon 	time_t t;
1017ff1c9800SMatthew Dillon 	int year;
1018ff1c9800SMatthew Dillon 	int month;
10195e435c92SMatthew Dillon 	int day = 0;
10205e435c92SMatthew Dillon 	int hour = 0;
10215e435c92SMatthew Dillon 	int minute = 0;
1022ff1c9800SMatthew Dillon 	int r;
1023ff1c9800SMatthew Dillon 
10245e435c92SMatthew Dillon 	while (*fpath && *fpath != '-' && *fpath != '.')
10255e435c92SMatthew Dillon 		++fpath;
10265e435c92SMatthew Dillon 	if (*fpath)
10275e435c92SMatthew Dillon 		++fpath;
10285e435c92SMatthew Dillon 
10295e435c92SMatthew Dillon 	r = sscanf(fpath, "%4d%2d%2d-%2d%2d",
1030ff1c9800SMatthew Dillon 		   &year, &month, &day, &hour, &minute);
10315e435c92SMatthew Dillon 
10325e435c92SMatthew Dillon 	if (r >= 3) {
1033ff1c9800SMatthew Dillon 		bzero(&tm, sizeof(tm));
1034ff1c9800SMatthew Dillon 		tm.tm_isdst = -1;
1035ff1c9800SMatthew Dillon 		tm.tm_min = minute;
1036ff1c9800SMatthew Dillon 		tm.tm_hour = hour;
1037ff1c9800SMatthew Dillon 		tm.tm_mday = day;
1038ff1c9800SMatthew Dillon 		tm.tm_mon = month - 1;
1039ff1c9800SMatthew Dillon 		tm.tm_year = year - 1900;
10405e435c92SMatthew Dillon 		t = mktime(&tm);
10415e435c92SMatthew Dillon 		if (t == (time_t)-1)
10425e435c92SMatthew Dillon 			return(0);
10435e435c92SMatthew Dillon 		t = time(NULL) - t;
1044ff1c9800SMatthew Dillon 		if ((int)t > arg2)
1045ff1c9800SMatthew Dillon 			return(1);
1046ff1c9800SMatthew Dillon 	}
1047ff1c9800SMatthew Dillon 	return(0);
1048ff1c9800SMatthew Dillon }
1049ff1c9800SMatthew Dillon 
1050ff1c9800SMatthew Dillon /*
10516a6e350fSMatthew Dillon  * Issue a snapshot.
10526a6e350fSMatthew Dillon  */
10536a6e350fSMatthew Dillon static int
1054b5ec5ad4SMatthew Dillon create_snapshot(const char *path, const char *snapshots_path)
10556a6e350fSMatthew Dillon {
10566a6e350fSMatthew Dillon 	int r;
10576a6e350fSMatthew Dillon 
1058ff1c9800SMatthew Dillon 	runcmd(&r, "hammer snapshot %s %s", path, snapshots_path);
10596a6e350fSMatthew Dillon 	return(r);
10606a6e350fSMatthew Dillon }
10616a6e350fSMatthew Dillon 
10626a6e350fSMatthew Dillon static int
1063eb5751f6SMatthew Dillon cleanup_prune(const char *path, const char *snapshots_path,
1064c6c298a7SMatthew Dillon 		  int arg1 __unused, int arg2, int snapshots_disabled)
10656a6e350fSMatthew Dillon {
1066eb5751f6SMatthew Dillon 	const char *path_or_snapshots_path;
1067eb5751f6SMatthew Dillon 	struct softprune *base = NULL;
1068eb5751f6SMatthew Dillon 	struct hammer_ioc_prune dummy_template;
1069eb5751f6SMatthew Dillon 
1070eb5751f6SMatthew Dillon 	bzero(&dummy_template, sizeof(dummy_template));
1071eb5751f6SMatthew Dillon 	hammer_softprune_scandir(&base, &dummy_template, snapshots_path);
1072eb5751f6SMatthew Dillon 
1073eb5751f6SMatthew Dillon 	/*
1074eb5751f6SMatthew Dillon 	 * If the snapshots_path (e.g. /var/hammer/...) has no snapshots
1075eb5751f6SMatthew Dillon 	 * in it then prune will get confused and prune the filesystem
1076eb5751f6SMatthew Dillon 	 * containing the snapshots_path instead of the requested
1077eb5751f6SMatthew Dillon 	 * filesystem.  De-confuse prune.  We need a better way.
1078eb5751f6SMatthew Dillon 	 */
1079eb5751f6SMatthew Dillon 	path_or_snapshots_path = base ? snapshots_path : path;
1080eb5751f6SMatthew Dillon 
1081c6c298a7SMatthew Dillon 	/*
1082c6c298a7SMatthew Dillon 	 * If snapshots have been disabled run prune-everything instead
1083c6c298a7SMatthew Dillon 	 * of prune.
1084c6c298a7SMatthew Dillon 	 */
1085c6c298a7SMatthew Dillon 	if (snapshots_disabled && arg2) {
1086eb5751f6SMatthew Dillon 		runcmd(NULL,
1087eb5751f6SMatthew Dillon 		       "hammer -c %s/.prune.cycle -t %d prune-everything %s",
1088c6c298a7SMatthew Dillon 		       snapshots_path, arg2, path);
1089c6c298a7SMatthew Dillon 	} else if (snapshots_disabled) {
1090c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer prune-everything %s", path);
1091c6c298a7SMatthew Dillon 	} else if (arg2) {
10926a6e350fSMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
1093eb5751f6SMatthew Dillon 			snapshots_path, arg2, path_or_snapshots_path);
10946a6e350fSMatthew Dillon 	} else {
1095eb5751f6SMatthew Dillon 		runcmd(NULL, "hammer prune %s", path_or_snapshots_path);
10966a6e350fSMatthew Dillon 	}
10976a6e350fSMatthew Dillon 	return(0);
10986a6e350fSMatthew Dillon }
10996a6e350fSMatthew Dillon 
11006a6e350fSMatthew Dillon static int
11010b8bd7daSMatthew Dillon cleanup_rebalance(const char *path, const char *snapshots_path,
11020b8bd7daSMatthew Dillon 		  int arg1 __unused, int arg2)
11030b8bd7daSMatthew Dillon {
11040b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
11050b8bd7daSMatthew Dillon 		printf(".");
11060b8bd7daSMatthew Dillon 		fflush(stdout);
11070b8bd7daSMatthew Dillon 	}
11080b8bd7daSMatthew Dillon 
11090b8bd7daSMatthew Dillon 	runcmd(NULL,
11100b8bd7daSMatthew Dillon 	       "hammer -c %s/.rebalance.cycle -t %d rebalance %s",
11110b8bd7daSMatthew Dillon 	       snapshots_path, arg2, path);
11120b8bd7daSMatthew Dillon 	if (VerboseOpt == 0) {
11130b8bd7daSMatthew Dillon 		printf(".");
11140b8bd7daSMatthew Dillon 		fflush(stdout);
11150b8bd7daSMatthew Dillon 	}
11160b8bd7daSMatthew Dillon 	if (VerboseOpt == 0)
11170b8bd7daSMatthew Dillon 		printf("\n");
11180b8bd7daSMatthew Dillon 	return(0);
11190b8bd7daSMatthew Dillon }
11200b8bd7daSMatthew Dillon 
11210b8bd7daSMatthew Dillon static int
11226a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path,
11236a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
11246a6e350fSMatthew Dillon {
11256a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11266a6e350fSMatthew Dillon 		printf(".");
11276a6e350fSMatthew Dillon 		fflush(stdout);
11286a6e350fSMatthew Dillon 	}
1129797a0b63SMatthew Dillon 
1130797a0b63SMatthew Dillon 	/*
1131797a0b63SMatthew Dillon 	 * When reblocking the B-Tree always reblock everything in normal
1132797a0b63SMatthew Dillon 	 * mode.
1133797a0b63SMatthew Dillon 	 */
11346a6e350fSMatthew Dillon 	runcmd(NULL,
1135797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s",
11366a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11376a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11386a6e350fSMatthew Dillon 		printf(".");
11396a6e350fSMatthew Dillon 		fflush(stdout);
11406a6e350fSMatthew Dillon 	}
1141797a0b63SMatthew Dillon 
1142797a0b63SMatthew Dillon 	/*
1143797a0b63SMatthew Dillon 	 * When reblocking the inodes always reblock everything in normal
1144797a0b63SMatthew Dillon 	 * mode.
1145797a0b63SMatthew Dillon 	 */
11466a6e350fSMatthew Dillon 	runcmd(NULL,
1147797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s",
11486a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11496a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11506a6e350fSMatthew Dillon 		printf(".");
11516a6e350fSMatthew Dillon 		fflush(stdout);
11526a6e350fSMatthew Dillon 	}
1153797a0b63SMatthew Dillon 
1154797a0b63SMatthew Dillon 	/*
1155797a0b63SMatthew Dillon 	 * When reblocking the directories always reblock everything in normal
1156797a0b63SMatthew Dillon 	 * mode.
1157797a0b63SMatthew Dillon 	 */
1158797a0b63SMatthew Dillon 	runcmd(NULL,
1159797a0b63SMatthew Dillon 	       "hammer -c %s/.reblock-4.cycle -t %d reblock-dirs %s",
1160797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1161797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1162797a0b63SMatthew Dillon 		printf(".");
1163797a0b63SMatthew Dillon 		fflush(stdout);
1164797a0b63SMatthew Dillon 	}
1165797a0b63SMatthew Dillon 
1166797a0b63SMatthew Dillon 	/*
1167797a0b63SMatthew Dillon 	 * Do not reblock all the data in normal mode.
1168797a0b63SMatthew Dillon 	 */
11696a6e350fSMatthew Dillon 	runcmd(NULL,
11706a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
11716a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11726a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
11736a6e350fSMatthew Dillon 		printf("\n");
11746a6e350fSMatthew Dillon 	return(0);
11756a6e350fSMatthew Dillon }
11766a6e350fSMatthew Dillon 
11776a6e350fSMatthew Dillon static int
11786a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path,
11796a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
11806a6e350fSMatthew Dillon {
11816a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11826a6e350fSMatthew Dillon 		printf(".");
11836a6e350fSMatthew Dillon 		fflush(stdout);
11846a6e350fSMatthew Dillon 	}
11856a6e350fSMatthew Dillon 	runcmd(NULL,
11866a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
11876a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11886a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11896a6e350fSMatthew Dillon 		printf(".");
11906a6e350fSMatthew Dillon 		fflush(stdout);
11916a6e350fSMatthew Dillon 	}
11926a6e350fSMatthew Dillon 	runcmd(NULL,
11936a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
11946a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
11956a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
11966a6e350fSMatthew Dillon 		printf(".");
11976a6e350fSMatthew Dillon 		fflush(stdout);
11986a6e350fSMatthew Dillon 	}
11996a6e350fSMatthew Dillon 	runcmd(NULL,
1200797a0b63SMatthew Dillon 	       "hammer -c %s/.recopy-4.cycle -t %d reblock-dirs %s",
1201797a0b63SMatthew Dillon 	       snapshots_path, arg2, path);
1202797a0b63SMatthew Dillon 	if (VerboseOpt == 0) {
1203797a0b63SMatthew Dillon 		printf(".");
1204797a0b63SMatthew Dillon 		fflush(stdout);
1205797a0b63SMatthew Dillon 	}
1206797a0b63SMatthew Dillon 	runcmd(NULL,
12076a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
12086a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
12096a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
12106a6e350fSMatthew Dillon 		printf("\n");
12116a6e350fSMatthew Dillon 	return(0);
12126a6e350fSMatthew Dillon }
12136a6e350fSMatthew Dillon 
1214bb29b5d8SMatthew Dillon static int
1215bb29b5d8SMatthew Dillon cleanup_dedup(const char *path, const char *snapshots_path __unused,
1216bb29b5d8SMatthew Dillon 		  int arg1 __unused, int arg2)
1217bb29b5d8SMatthew Dillon {
1218bb29b5d8SMatthew Dillon 	if (VerboseOpt == 0) {
1219bb29b5d8SMatthew Dillon 		printf(".");
1220bb29b5d8SMatthew Dillon 		fflush(stdout);
1221bb29b5d8SMatthew Dillon 	}
1222bb29b5d8SMatthew Dillon 
1223bb29b5d8SMatthew Dillon 	runcmd(NULL, "hammer -t %d dedup %s", arg2, path);
1224bb29b5d8SMatthew Dillon 	if (VerboseOpt == 0) {
1225bb29b5d8SMatthew Dillon 		printf(".");
1226bb29b5d8SMatthew Dillon 		fflush(stdout);
1227bb29b5d8SMatthew Dillon 	}
1228bb29b5d8SMatthew Dillon 	if (VerboseOpt == 0)
1229bb29b5d8SMatthew Dillon 		printf("\n");
1230bb29b5d8SMatthew Dillon 	return(0);
1231bb29b5d8SMatthew Dillon }
1232bb29b5d8SMatthew Dillon 
12336a6e350fSMatthew Dillon static
12346a6e350fSMatthew Dillon void
12356a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...)
12366a6e350fSMatthew Dillon {
12376a6e350fSMatthew Dillon 	va_list va;
12386a6e350fSMatthew Dillon 	char *cmd;
12396a6e350fSMatthew Dillon 	char *arg;
12406a6e350fSMatthew Dillon 	char **av;
12416a6e350fSMatthew Dillon 	int n;
12426a6e350fSMatthew Dillon 	int nmax;
12436a6e350fSMatthew Dillon 	int res;
12446a6e350fSMatthew Dillon 	pid_t pid;
12456a6e350fSMatthew Dillon 
12466a6e350fSMatthew Dillon 	/*
12476a6e350fSMatthew Dillon 	 * Generate the command
12486a6e350fSMatthew Dillon 	 */
12496a6e350fSMatthew Dillon 	va_start(va, ctl);
12506a6e350fSMatthew Dillon 	vasprintf(&cmd, ctl, va);
12516a6e350fSMatthew Dillon 	va_end(va);
12526a6e350fSMatthew Dillon 	if (VerboseOpt)
12536a6e350fSMatthew Dillon 		printf("    %s\n", cmd);
12546a6e350fSMatthew Dillon 
12556a6e350fSMatthew Dillon 	/*
12566a6e350fSMatthew Dillon 	 * Break us down into arguments.  We do not just use system() here
12576a6e350fSMatthew Dillon 	 * because it blocks SIGINT and friends.
12586a6e350fSMatthew Dillon 	 */
12596a6e350fSMatthew Dillon 	n = 0;
12606a6e350fSMatthew Dillon 	nmax = 16;
12616a6e350fSMatthew Dillon 	av = malloc(sizeof(char *) * nmax);
12626a6e350fSMatthew Dillon 
12636a6e350fSMatthew Dillon 	for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
1264c453712aSMatthew Dillon 		if (n == nmax - 1) {
12656a6e350fSMatthew Dillon 			nmax += 16;
12666a6e350fSMatthew Dillon 			av = realloc(av, sizeof(char *) * nmax);
12676a6e350fSMatthew Dillon 		}
12686a6e350fSMatthew Dillon 		av[n++] = arg;
12696a6e350fSMatthew Dillon 	}
1270c453712aSMatthew Dillon 	av[n++] = NULL;
12716a6e350fSMatthew Dillon 
12726a6e350fSMatthew Dillon 	/*
12736a6e350fSMatthew Dillon 	 * Run the command.
12746a6e350fSMatthew Dillon 	 */
1275445faa69SMatthew Dillon 	RunningIoctl = 1;
12766a6e350fSMatthew Dillon 	if ((pid = fork()) == 0) {
12776a6e350fSMatthew Dillon 		if (VerboseOpt < 2) {
12786a6e350fSMatthew Dillon 			int fd = open("/dev/null", O_RDWR);
12796a6e350fSMatthew Dillon 			dup2(fd, 1);
12806a6e350fSMatthew Dillon 			close(fd);
12816a6e350fSMatthew Dillon 		}
12826a6e350fSMatthew Dillon 		execvp(av[0], av);
12836a6e350fSMatthew Dillon 		_exit(127);
12846a6e350fSMatthew Dillon 	} else if (pid < 0) {
12856a6e350fSMatthew Dillon 		res = 127;
12866a6e350fSMatthew Dillon 	} else {
12876a6e350fSMatthew Dillon 		int status;
1288445faa69SMatthew Dillon 
12896a6e350fSMatthew Dillon 		while (waitpid(pid, &status, 0) != pid)
12906a6e350fSMatthew Dillon 			;
12916a6e350fSMatthew Dillon 		res = WEXITSTATUS(status);
12926a6e350fSMatthew Dillon 	}
1293445faa69SMatthew Dillon 	RunningIoctl = 0;
1294445faa69SMatthew Dillon 	if (DidInterrupt)
1295445faa69SMatthew Dillon 		_exit(1);
12966a6e350fSMatthew Dillon 
12976a6e350fSMatthew Dillon 	free(cmd);
12986a6e350fSMatthew Dillon 	free(av);
12996a6e350fSMatthew Dillon 	if (resp)
13006a6e350fSMatthew Dillon 		*resp = res;
13016a6e350fSMatthew Dillon }
1302