xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision a360fdde)
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 
61*a360fddeSJohn Marino #include <libutil.h>
626a6e350fSMatthew Dillon #include "hammer.h"
636a6e350fSMatthew Dillon 
64c453712aSMatthew Dillon struct didpfs {
65c453712aSMatthew Dillon 	struct didpfs *next;
66c453712aSMatthew Dillon 	uuid_t		uuid;
67c453712aSMatthew Dillon };
68c453712aSMatthew Dillon 
696a6e350fSMatthew Dillon static void do_cleanup(const char *path);
7083f2a3aaSMatthew Dillon static void config_init(const char *path, struct hammer_ioc_config *config);
7183f2a3aaSMatthew Dillon static void migrate_config(FILE *fp, struct hammer_ioc_config *config);
7283f2a3aaSMatthew Dillon static void migrate_snapshots(int fd, const char *snapshots_path);
7383f2a3aaSMatthew Dillon static void migrate_one_snapshot(int fd, const char *fpath,
7483f2a3aaSMatthew Dillon 			struct hammer_ioc_snapshot *snapshot);
756a6e350fSMatthew Dillon static int strtosecs(char *ptr);
766a6e350fSMatthew Dillon static const char *dividing_slash(const char *path);
776a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1,
786a6e350fSMatthew Dillon 			time_t *savep);
796a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd,
806a6e350fSMatthew Dillon 			time_t savet);
8183f2a3aaSMatthew Dillon static int check_softlinks(int fd, int new_config, const char *snapshots_path);
8283f2a3aaSMatthew Dillon static void cleanup_softlinks(int fd, int new_config,
8383f2a3aaSMatthew Dillon 			const char *snapshots_path, int arg2, char *arg3);
84ec2aa11dSMatthew Dillon static void delete_snapshots(int fd, struct hammer_ioc_snapshot *dsnapshot);
85ff1c9800SMatthew Dillon static int check_expired(const char *fpath, int arg2);
866a6e350fSMatthew Dillon 
87b5ec5ad4SMatthew Dillon static int create_snapshot(const char *path, const char *snapshots_path);
880b8bd7daSMatthew Dillon static int cleanup_rebalance(const char *path, const char *snapshots_path,
890b8bd7daSMatthew Dillon 			int arg1, int arg2);
906a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path,
91c6c298a7SMatthew Dillon 			int arg1, int arg2, int snapshots_disabled);
926a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path,
936a6e350fSMatthew Dillon 			int arg1, int arg2);
946a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path,
956a6e350fSMatthew Dillon 			int arg1, int arg2);
96bb29b5d8SMatthew Dillon static int cleanup_dedup(const char *path, const char *snapshots_path,
97bb29b5d8SMatthew Dillon 			int arg1, int arg2);
986a6e350fSMatthew Dillon 
99b58f1e66SSascha Wildner static void runcmd(int *resp, const char *ctl, ...) __printflike(2, 3);
1006a6e350fSMatthew Dillon 
101b5ec5ad4SMatthew Dillon /*
102b5ec5ad4SMatthew Dillon  * WARNING: Do not make the SNAPSHOTS_BASE "/var/snapshots" because
103b5ec5ad4SMatthew Dillon  * it will interfere with the older HAMMER VERS < 3 snapshots directory
104b5ec5ad4SMatthew Dillon  * for the /var PFS.
105b5ec5ad4SMatthew Dillon  */
106b5ec5ad4SMatthew Dillon #define SNAPSHOTS_BASE	"/var/hammer"	/* HAMMER VERS >= 3 */
1076a6e350fSMatthew Dillon #define WS	" \t\r\n"
1086a6e350fSMatthew Dillon 
109c453712aSMatthew Dillon struct didpfs *FirstPFS;
1106a6e350fSMatthew Dillon 
1116a6e350fSMatthew Dillon void
1126a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
1136a6e350fSMatthew Dillon {
114eac446c5SMatthew Dillon 	char *fstype, *fs, *path;
115eac446c5SMatthew Dillon 	struct statfs *stfsbuf;
116eac446c5SMatthew Dillon 	int mntsize, i;
1176a6e350fSMatthew Dillon 
1186a6e350fSMatthew Dillon 	tzset();
1196a6e350fSMatthew Dillon 	if (ac == 0) {
120eac446c5SMatthew Dillon 		mntsize = getmntinfo(&stfsbuf, MNT_NOWAIT);
121eac446c5SMatthew Dillon 		if (mntsize > 0) {
122eac446c5SMatthew Dillon 			for (i=0; i < mntsize; i++) {
123eac446c5SMatthew Dillon 				/*
124eac446c5SMatthew Dillon 				 * We will cleanup in the case fstype is hammer.
125eac446c5SMatthew Dillon 				 * If we have null-mounted PFS, we check the
126eac446c5SMatthew Dillon 				 * mount source. If it looks like a PFS, we
127eac446c5SMatthew Dillon 				 * proceed to cleanup also.
128eac446c5SMatthew Dillon 				 */
129eac446c5SMatthew Dillon 				fstype = stfsbuf[i].f_fstypename;
130eac446c5SMatthew Dillon 				fs = stfsbuf[i].f_mntfromname;
131eac446c5SMatthew Dillon 				if ((strcmp(fstype, "hammer") == 0) ||
132eac446c5SMatthew Dillon 				    ((strcmp(fstype, "null") == 0) &&
133cb3c760cSMatthew Dillon 				     (strstr(fs, "/@@0x") != NULL ||
134cb3c760cSMatthew Dillon 				      strstr(fs, "/@@-1") != NULL))) {
135eac446c5SMatthew Dillon 					path = stfsbuf[i].f_mntonname;
1366a6e350fSMatthew Dillon 					do_cleanup(path);
1376a6e350fSMatthew Dillon 				}
138574066d3SMatthew Dillon 			}
139eac446c5SMatthew Dillon 		}
140eac446c5SMatthew Dillon 
1416a6e350fSMatthew Dillon 	} else {
1426a6e350fSMatthew Dillon 		while (ac) {
1436a6e350fSMatthew Dillon 			do_cleanup(*av);
1446a6e350fSMatthew Dillon 			--ac;
1456a6e350fSMatthew Dillon 			++av;
1466a6e350fSMatthew Dillon 		}
1476a6e350fSMatthew Dillon 	}
1486a6e350fSMatthew Dillon }
1496a6e350fSMatthew Dillon 
1506a6e350fSMatthew Dillon static
1516a6e350fSMatthew Dillon void
1526a6e350fSMatthew Dillon do_cleanup(const char *path)
1536a6e350fSMatthew Dillon {
1546a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
15583f2a3aaSMatthew Dillon 	struct hammer_ioc_config config;
15683f2a3aaSMatthew Dillon 	struct hammer_ioc_version version;
1576a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
15882893617SThomas Nikolajsen 	char *snapshots_path = NULL;
1596a6e350fSMatthew Dillon 	char *config_path;
1606a6e350fSMatthew Dillon 	struct stat st;
1616a6e350fSMatthew Dillon 	char *cmd;
1626a6e350fSMatthew Dillon 	char *ptr;
1636a6e350fSMatthew Dillon 	int arg1;
1646a6e350fSMatthew Dillon 	int arg2;
1655e435c92SMatthew Dillon 	char *arg3;
1666a6e350fSMatthew Dillon 	time_t savet;
1676a6e350fSMatthew Dillon 	char buf[256];
16883f2a3aaSMatthew Dillon 	char *cbase;
16983f2a3aaSMatthew Dillon 	char *cptr;
17082893617SThomas Nikolajsen 	FILE *fp = NULL;
171c453712aSMatthew Dillon 	struct didpfs *didpfs;
172c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
173c6c298a7SMatthew Dillon 	int prune_warning = 0;
17483f2a3aaSMatthew Dillon 	int new_config = 0;
175b5ec5ad4SMatthew Dillon 	int snapshots_from_pfs = 0;
1766a6e350fSMatthew Dillon 	int fd;
1776a6e350fSMatthew Dillon 	int r;
1780b8bd7daSMatthew Dillon 	int found_rebal = 0;
1796a6e350fSMatthew Dillon 
1806a6e350fSMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1816a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1826a6e350fSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
1836a6e350fSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
1846a6e350fSMatthew Dillon 	pfs.pfs_id = -1;
1856a6e350fSMatthew Dillon 
1866a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1876a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1886a6e350fSMatthew Dillon 	if (fd < 0) {
1896a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1906a6e350fSMatthew Dillon 		return;
1916a6e350fSMatthew Dillon 	}
19283f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) < 0) {
1936a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
19483f2a3aaSMatthew Dillon 		close(fd);
1956a6e350fSMatthew Dillon 		return;
1966a6e350fSMatthew Dillon 	}
1976a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1986a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
19983f2a3aaSMatthew Dillon 		close(fd);
2006a6e350fSMatthew Dillon 		return;
2016a6e350fSMatthew Dillon 	}
20283f2a3aaSMatthew Dillon 	bzero(&version, sizeof(version));
20383f2a3aaSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_VERSION, &version) < 0) {
20483f2a3aaSMatthew Dillon 		printf(" HAMMER filesystem but couldn't retrieve version!\n");
20583f2a3aaSMatthew Dillon 		close(fd);
20683f2a3aaSMatthew Dillon 		return;
20783f2a3aaSMatthew Dillon 	}
20883f2a3aaSMatthew Dillon 
20983f2a3aaSMatthew Dillon 	bzero(&config, sizeof(config));
21083f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
21183f2a3aaSMatthew Dillon 		if (ioctl(fd, HAMMERIOC_GET_CONFIG, &config) == 0 &&
21283f2a3aaSMatthew Dillon 		    config.head.error == 0) {
21383f2a3aaSMatthew Dillon 			new_config = 1;
21483f2a3aaSMatthew Dillon 		}
21583f2a3aaSMatthew Dillon 	}
2166a6e350fSMatthew Dillon 
2176a6e350fSMatthew Dillon 	/*
2186a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
2196a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
2206a6e350fSMatthew Dillon 	 */
221c453712aSMatthew Dillon 	for (didpfs = FirstPFS; didpfs; didpfs = didpfs->next) {
222c453712aSMatthew Dillon 		if (bcmp(&didpfs->uuid, &mrec_tmp.pfs.pfsd.unique_uuid, sizeof(uuid_t)) == 0) {
223bb8e52c0SThomas Nikolajsen 			printf(" PFS #%d already handled\n", pfs.pfs_id);
22483f2a3aaSMatthew Dillon 			close(fd);
2256a6e350fSMatthew Dillon 			return;
2266a6e350fSMatthew Dillon 		}
227c453712aSMatthew Dillon 	}
228c453712aSMatthew Dillon 	didpfs = malloc(sizeof(*didpfs));
229c453712aSMatthew Dillon 	didpfs->next = FirstPFS;
230c453712aSMatthew Dillon 	FirstPFS = didpfs;
231c453712aSMatthew Dillon 	didpfs->uuid = mrec_tmp.pfs.pfsd.unique_uuid;
2326a6e350fSMatthew Dillon 
2336a6e350fSMatthew Dillon 	/*
234b5ec5ad4SMatthew Dillon 	 * Calculate the old snapshots directory for HAMMER VERSION < 3
235b5ec5ad4SMatthew Dillon 	 *
236b5ec5ad4SMatthew Dillon 	 * If the directory is explicitly specified in the PFS config
237b5ec5ad4SMatthew Dillon 	 * we flag it and will not migrate it later.
238ff1c9800SMatthew Dillon 	 */
239ff1c9800SMatthew Dillon 	if (mrec_tmp.pfs.pfsd.snapshots[0] == '/') {
240ff1c9800SMatthew Dillon 		asprintf(&snapshots_path, "%s", mrec_tmp.pfs.pfsd.snapshots);
241b5ec5ad4SMatthew Dillon 		snapshots_from_pfs = 1;
242ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.snapshots[0]) {
243ff1c9800SMatthew Dillon 		printf(" WARNING: pfs-slave's snapshots dir is not absolute\n");
24483f2a3aaSMatthew Dillon 		close(fd);
245ff1c9800SMatthew Dillon 		return;
246ff1c9800SMatthew Dillon 	} else if (mrec_tmp.pfs.pfsd.mirror_flags & HAMMER_PFSD_SLAVE) {
24782893617SThomas Nikolajsen 		if (version.cur_version < 3) {
248ff1c9800SMatthew Dillon 			printf(" WARNING: must configure snapshot dir for PFS slave\n");
249ff1c9800SMatthew Dillon 			printf("\tWe suggest <fs>/var/slaves/<name> where "
250ff1c9800SMatthew Dillon 			       "<fs> is the base HAMMER fs\n");
251bb8e52c0SThomas Nikolajsen 			printf("\tcontaining the slave\n");
25283f2a3aaSMatthew Dillon 			close(fd);
253ff1c9800SMatthew Dillon 			return;
25482893617SThomas Nikolajsen 		}
255ff1c9800SMatthew Dillon 	} else {
256ff1c9800SMatthew Dillon 		asprintf(&snapshots_path,
257ff1c9800SMatthew Dillon 			 "%s%ssnapshots", path, dividing_slash(path));
258ff1c9800SMatthew Dillon 	}
259ff1c9800SMatthew Dillon 
260ff1c9800SMatthew Dillon 	/*
261b5ec5ad4SMatthew Dillon 	 * Check for old-style config file
2626a6e350fSMatthew Dillon 	 */
26382893617SThomas Nikolajsen 	if (snapshots_path) {
2646a6e350fSMatthew Dillon 		asprintf(&config_path, "%s/config", snapshots_path);
2656a6e350fSMatthew Dillon 		fp = fopen(config_path, "r");
26682893617SThomas Nikolajsen 	}
26783f2a3aaSMatthew Dillon 
26883f2a3aaSMatthew Dillon 	/*
26983f2a3aaSMatthew Dillon 	 * Handle upgrades to hammer version 3, move the config
27083f2a3aaSMatthew Dillon 	 * file into meta-data.
27183f2a3aaSMatthew Dillon 	 *
27283f2a3aaSMatthew Dillon 	 * For the old config read the file into the config structure,
27383f2a3aaSMatthew Dillon 	 * we will parse it out of the config structure regardless.
27483f2a3aaSMatthew Dillon 	 */
27583f2a3aaSMatthew Dillon 	if (version.cur_version >= 3) {
27683f2a3aaSMatthew Dillon 		if (fp) {
27783f2a3aaSMatthew Dillon 			printf("(migrating) ");
27883f2a3aaSMatthew Dillon 			fflush(stdout);
27983f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
28083f2a3aaSMatthew Dillon 			migrate_snapshots(fd, snapshots_path);
28183f2a3aaSMatthew Dillon 			fclose(fp);
28283f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
28383f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
28483f2a3aaSMatthew Dillon 				close(fd);
2856a6e350fSMatthew Dillon 				return;
2866a6e350fSMatthew Dillon 			}
28783f2a3aaSMatthew Dillon 			remove(config_path);
28883f2a3aaSMatthew Dillon 		} else if (new_config == 0) {
28983f2a3aaSMatthew Dillon 			config_init(path, &config);
29083f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_SET_CONFIG, &config) < 0) {
29183f2a3aaSMatthew Dillon 				printf(" cannot init meta-data config!\n");
29283f2a3aaSMatthew Dillon 				close(fd);
29383f2a3aaSMatthew Dillon 				return;
29483f2a3aaSMatthew Dillon 			}
29583f2a3aaSMatthew Dillon 		}
29683f2a3aaSMatthew Dillon 		new_config = 1;
29783f2a3aaSMatthew Dillon 	} else {
298b5ec5ad4SMatthew Dillon 		/*
299b5ec5ad4SMatthew Dillon 		 * Create missing snapshots directory for HAMMER VERSION < 3
300b5ec5ad4SMatthew Dillon 		 */
301b5ec5ad4SMatthew Dillon 		if (stat(snapshots_path, &st) < 0) {
302b5ec5ad4SMatthew Dillon 			if (mkdir(snapshots_path, 0755) != 0) {
303b5ec5ad4SMatthew Dillon 				free(snapshots_path);
304b5ec5ad4SMatthew Dillon 				printf(" unable to create snapshot dir \"%s\": %s\n",
305b5ec5ad4SMatthew Dillon 					snapshots_path, strerror(errno));
306b5ec5ad4SMatthew Dillon 				close(fd);
307b5ec5ad4SMatthew Dillon 				return;
308b5ec5ad4SMatthew Dillon 			}
309b5ec5ad4SMatthew Dillon 		}
310b5ec5ad4SMatthew Dillon 
311b5ec5ad4SMatthew Dillon 		/*
312b5ec5ad4SMatthew Dillon 		 *  Create missing config file for HAMMER VERSION < 3
313b5ec5ad4SMatthew Dillon 		 */
31483f2a3aaSMatthew Dillon 		if (fp == NULL) {
31583f2a3aaSMatthew Dillon 			config_init(path, &config);
31683f2a3aaSMatthew Dillon 			fp = fopen(config_path, "w");
31783f2a3aaSMatthew Dillon 			if (fp) {
31883f2a3aaSMatthew Dillon 				fwrite(config.config.text, 1,
31983f2a3aaSMatthew Dillon 					strlen(config.config.text), fp);
32083f2a3aaSMatthew Dillon 				fclose(fp);
32183f2a3aaSMatthew Dillon 			}
32283f2a3aaSMatthew Dillon 		} else {
32383f2a3aaSMatthew Dillon 			migrate_config(fp, &config);
32483f2a3aaSMatthew Dillon 			fclose(fp);
32583f2a3aaSMatthew Dillon 		}
32683f2a3aaSMatthew Dillon 	}
3276a6e350fSMatthew Dillon 
328b5ec5ad4SMatthew Dillon 	/*
329b5ec5ad4SMatthew Dillon 	 * If snapshots_from_pfs is not set we calculate the new snapshots
330b5ec5ad4SMatthew Dillon 	 * directory default (in /var) for HAMMER VERSION >= 3 and migrate
331b5ec5ad4SMatthew Dillon 	 * the old snapshots directory over.
332b5ec5ad4SMatthew Dillon 	 *
333b5ec5ad4SMatthew Dillon 	 * People who have set an explicit snapshots directory will have
334b5ec5ad4SMatthew Dillon 	 * to migrate the data manually into /var/hammer, or not bother at
335b5ec5ad4SMatthew Dillon 	 * all.  People running slaves may wish to migrate it and then
336b5ec5ad4SMatthew Dillon 	 * clear the snapshots specification in the PFS config for the
337b5ec5ad4SMatthew Dillon 	 * slave.
338b5ec5ad4SMatthew Dillon 	 */
339b5ec5ad4SMatthew Dillon 	if (new_config && snapshots_from_pfs == 0) {
340b5ec5ad4SMatthew Dillon 		char *npath;
341b5ec5ad4SMatthew Dillon 
342b5ec5ad4SMatthew Dillon 		assert(path[0] == '/');
343b5ec5ad4SMatthew Dillon 		if (strcmp(path, "/") == 0)
344b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/root", SNAPSHOTS_BASE);
345b5ec5ad4SMatthew Dillon 		else
346b5ec5ad4SMatthew Dillon 			asprintf(&npath, "%s/%s", SNAPSHOTS_BASE, path + 1);
34782893617SThomas Nikolajsen 		if (snapshots_path) {
348b5ec5ad4SMatthew Dillon 			if (stat(npath, &st) < 0 && errno == ENOENT) {
349b5ec5ad4SMatthew Dillon 				if (stat(snapshots_path, &st) < 0 && errno == ENOENT) {
350b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Creating snapshots\n"
351b5ec5ad4SMatthew Dillon 					       "\tCreating snapshots in %s\n",
352b5ec5ad4SMatthew Dillon 					       npath);
353b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
354b5ec5ad4SMatthew Dillon 				} else {
355b5ec5ad4SMatthew Dillon 					printf(" HAMMER UPGRADE: Moving snapshots\n"
356b5ec5ad4SMatthew Dillon 					       "\tMoving snapshots from %s to %s\n",
357b5ec5ad4SMatthew Dillon 					       snapshots_path, npath);
358b5ec5ad4SMatthew Dillon 					runcmd(&r, "mkdir -p %s", npath);
359b5ec5ad4SMatthew Dillon 					runcmd(&r, "cpdup %s %s", snapshots_path, npath);
360b5ec5ad4SMatthew Dillon 					if (r != 0) {
361b5ec5ad4SMatthew Dillon 				    printf("Unable to move snapshots directory!\n");
362b5ec5ad4SMatthew Dillon 				    printf("Please fix this critical error.\n");
363b5ec5ad4SMatthew Dillon 				    printf("Aborting cleanup of %s\n", path);
364b5ec5ad4SMatthew Dillon 						close(fd);
365b5ec5ad4SMatthew Dillon 						return;
366b5ec5ad4SMatthew Dillon 					}
367b5ec5ad4SMatthew Dillon 					runcmd(&r, "rm -rf %s", snapshots_path);
368b5ec5ad4SMatthew Dillon 				}
369b5ec5ad4SMatthew Dillon 			}
370b5ec5ad4SMatthew Dillon 			free(snapshots_path);
37182893617SThomas Nikolajsen 		} else if (stat(npath, &st) < 0 && errno == ENOENT) {
37282893617SThomas Nikolajsen 			runcmd(&r, "mkdir -p %s", npath);
37382893617SThomas Nikolajsen 		}
374b5ec5ad4SMatthew Dillon 		snapshots_path = npath;
375b5ec5ad4SMatthew Dillon 	}
376b5ec5ad4SMatthew Dillon 
377b5ec5ad4SMatthew Dillon 	/*
378b5ec5ad4SMatthew Dillon 	 * Lock the PFS.  fd is the base directory of the mounted PFS.
379b5ec5ad4SMatthew Dillon 	 */
38083f2a3aaSMatthew Dillon 	if (flock(fd, LOCK_EX|LOCK_NB) == -1) {
3815bd5f172SSimon Schubert 		if (errno == EWOULDBLOCK)
3825bd5f172SSimon Schubert 			printf(" PFS #%d locked by other process\n", pfs.pfs_id);
3835bd5f172SSimon Schubert 		else
3845bd5f172SSimon Schubert 			printf(" can not lock %s: %s\n", config_path, strerror(errno));
38583f2a3aaSMatthew Dillon 		close(fd);
3865bd5f172SSimon Schubert 		return;
3875bd5f172SSimon Schubert 	}
3885bd5f172SSimon Schubert 
389ff1c9800SMatthew Dillon 	printf(" handle PFS #%d using %s\n", pfs.pfs_id, snapshots_path);
3906a6e350fSMatthew Dillon 
391*a360fddeSJohn Marino 	struct pidfh	*pfh = NULL;
392*a360fddeSJohn Marino 	static char	pidfile[PIDFILE_BUFSIZE];
393*a360fddeSJohn Marino 
394*a360fddeSJohn Marino 	snprintf (pidfile, PIDFILE_BUFSIZE, "%s/hammer.cleanup.%d",
395*a360fddeSJohn Marino 		pidfile_loc, getpid());
396*a360fddeSJohn Marino 	pfh = pidfile_open(pidfile, 0644, NULL);
397*a360fddeSJohn Marino 	if (pfh == NULL) {
398*a360fddeSJohn Marino 		warn ("Unable to open or create %s", pidfile);
399*a360fddeSJohn Marino 	}
400*a360fddeSJohn Marino 	pidfile_write(pfh);
401*a360fddeSJohn Marino 
4026a6e350fSMatthew Dillon 	/*
4036a6e350fSMatthew Dillon 	 * Process the config file
4046a6e350fSMatthew Dillon 	 */
40583f2a3aaSMatthew Dillon 	cbase = config.config.text;
40683f2a3aaSMatthew Dillon 
40783f2a3aaSMatthew Dillon 	while ((cptr = strchr(cbase, '\n')) != NULL) {
40883f2a3aaSMatthew Dillon 		bcopy(cbase, buf, cptr - cbase);
40983f2a3aaSMatthew Dillon 		buf[cptr - cbase] = 0;
41083f2a3aaSMatthew Dillon 		cbase = cptr + 1;
41183f2a3aaSMatthew Dillon 
4126a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
413743332abSMatthew Dillon 		if (cmd == NULL || cmd[0] == '#')
414743332abSMatthew Dillon 			continue;
415743332abSMatthew Dillon 
4166a6e350fSMatthew Dillon 		arg1 = 0;
4176a6e350fSMatthew Dillon 		arg2 = 0;
4185e435c92SMatthew Dillon 		arg3 = NULL;
4196a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
4206a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
4215e435c92SMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL) {
4226a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
4235e435c92SMatthew Dillon 				arg3 = strtok(NULL, WS);
4245e435c92SMatthew Dillon 			}
4256a6e350fSMatthew Dillon 		}
4266a6e350fSMatthew Dillon 
4276a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
4286a6e350fSMatthew Dillon 		fflush(stdout);
4296a6e350fSMatthew Dillon 
4306a6e350fSMatthew Dillon 		r = 1;
4316a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
4325e435c92SMatthew Dillon 			if (arg1 == 0) {
43383f2a3aaSMatthew Dillon 				if (arg2 &&
43483f2a3aaSMatthew Dillon 				    check_softlinks(fd, new_config,
43583f2a3aaSMatthew Dillon 						    snapshots_path)) {
4365e435c92SMatthew Dillon 					printf("only removing old snapshots\n");
4375e435c92SMatthew Dillon 					prune_warning = 1;
43883f2a3aaSMatthew Dillon 					cleanup_softlinks(fd, new_config,
43983f2a3aaSMatthew Dillon 							  snapshots_path,
4405e435c92SMatthew Dillon 							  arg2, arg3);
4415e435c92SMatthew Dillon 				} else {
4425e435c92SMatthew Dillon 					printf("disabled\n");
4435e435c92SMatthew Dillon 					snapshots_disabled = 1;
4445e435c92SMatthew Dillon 				}
4455e435c92SMatthew Dillon 			} else
4466a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4476a6e350fSMatthew Dillon 				printf("run\n");
44883f2a3aaSMatthew Dillon 				cleanup_softlinks(fd, new_config,
44983f2a3aaSMatthew Dillon 						  snapshots_path,
4505e435c92SMatthew Dillon 						  arg2, arg3);
451b5ec5ad4SMatthew Dillon 				r = create_snapshot(path, snapshots_path);
4526a6e350fSMatthew Dillon 			} else {
4536a6e350fSMatthew Dillon 				printf("skip\n");
4546a6e350fSMatthew Dillon 			}
4555e435c92SMatthew Dillon 		} else if (arg1 == 0) {
4565e435c92SMatthew Dillon 			/*
4575e435c92SMatthew Dillon 			 * The commands following this check can't handle
4585e435c92SMatthew Dillon 			 * a period of 0, so call the feature disabled and
4595e435c92SMatthew Dillon 			 * ignore the directive.
4605e435c92SMatthew Dillon 			 */
4615e435c92SMatthew Dillon 			printf("disabled\n");
4626a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
4636a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4645e435c92SMatthew Dillon 				if (prune_warning) {
4655e435c92SMatthew Dillon 					printf("run - WARNING snapshot "
4665e435c92SMatthew Dillon 					       "softlinks present "
467226f3799SThomas Nikolajsen 					       "but snapshots disabled\n");
4685e435c92SMatthew Dillon 				} else {
469c6c298a7SMatthew Dillon 					printf("run\n");
4705e435c92SMatthew Dillon 				}
4716a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
472c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
4736a6e350fSMatthew Dillon 			} else {
4746a6e350fSMatthew Dillon 				printf("skip\n");
4756a6e350fSMatthew Dillon 			}
4760b8bd7daSMatthew Dillon 		} else if (strcmp(cmd, "rebalance") == 0) {
4770b8bd7daSMatthew Dillon 			found_rebal = 1;
4780b8bd7daSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4790b8bd7daSMatthew Dillon 				printf("run");
4800b8bd7daSMatthew Dillon 				fflush(stdout);
4810b8bd7daSMatthew Dillon 				if (VerboseOpt)
4820b8bd7daSMatthew Dillon 					printf("\n");
4830b8bd7daSMatthew Dillon 				r = cleanup_rebalance(path, snapshots_path,
4840b8bd7daSMatthew Dillon 						arg1, arg2);
4850b8bd7daSMatthew Dillon 			} else {
4860b8bd7daSMatthew Dillon 				printf("skip\n");
4870b8bd7daSMatthew Dillon 			}
4886a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
4896a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
4906a6e350fSMatthew Dillon 				printf("run");
4916a6e350fSMatthew Dillon 				fflush(stdout);
4926a6e350fSMatthew Dillon 				if (VerboseOpt)
4936a6e350fSMatthew Dillon 					printf("\n");
4946a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
4956a6e350fSMatthew Dillon 						arg1, arg2);
4966a6e350fSMatthew Dillon 			} else {
4976a6e350fSMatthew Dillon 				printf("skip\n");
4986a6e350fSMatthew Dillon 			}
4996a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
5006a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
5016a6e350fSMatthew Dillon 				printf("run");
5026a6e350fSMatthew Dillon 				fflush(stdout);
5036a6e350fSMatthew Dillon 				if (VerboseOpt)
5046a6e350fSMatthew Dillon 					printf("\n");
5056a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
5066a6e350fSMatthew Dillon 					       arg1, arg2);
5076a6e350fSMatthew Dillon 			} else {
5086a6e350fSMatthew Dillon 				printf("skip\n");
5096a6e350fSMatthew Dillon 			}
510bb29b5d8SMatthew Dillon 		} else if (strcmp(cmd, "dedup") == 0) {
511bb29b5d8SMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
512bb29b5d8SMatthew Dillon 				printf("run");
513bb29b5d8SMatthew Dillon 				fflush(stdout);
514bb29b5d8SMatthew Dillon 				if (VerboseOpt)
515bb29b5d8SMatthew Dillon 					printf("\n");
516bb29b5d8SMatthew Dillon 				r = cleanup_dedup(path, snapshots_path,
517bb29b5d8SMatthew Dillon 						arg1, arg2);
518bb29b5d8SMatthew Dillon 			} else {
519bb29b5d8SMatthew Dillon 				printf("skip\n");
520bb29b5d8SMatthew Dillon 			}
5216a6e350fSMatthew Dillon 		} else {
5226a6e350fSMatthew Dillon 			printf("unknown directive\n");
5236a6e350fSMatthew Dillon 			r = 1;
5246a6e350fSMatthew Dillon 		}
5256a6e350fSMatthew Dillon 		if (r == 0)
5266a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
5276a6e350fSMatthew Dillon 	}
5280b8bd7daSMatthew Dillon 
5290b8bd7daSMatthew Dillon 	/*
53083f2a3aaSMatthew Dillon 	 * Add new rebalance feature if the config doesn't have it.
531b5ec5ad4SMatthew Dillon 	 * (old style config only).
5320b8bd7daSMatthew Dillon 	 */
53383f2a3aaSMatthew Dillon 	if (new_config == 0 && found_rebal == 0) {
5340b8bd7daSMatthew Dillon 		if ((fp = fopen(config_path, "r+")) != NULL) {
5350b8bd7daSMatthew Dillon 			fseek(fp, 0L, 2);
5360b8bd7daSMatthew Dillon 			fprintf(fp, "rebalance 1d 5m\n");
5370b8bd7daSMatthew Dillon 			fclose(fp);
5380b8bd7daSMatthew Dillon 		}
5390b8bd7daSMatthew Dillon 	}
54083f2a3aaSMatthew Dillon 
54183f2a3aaSMatthew Dillon 	/*
54283f2a3aaSMatthew Dillon 	 * Cleanup, and delay a little
54383f2a3aaSMatthew Dillon 	 */
54483f2a3aaSMatthew Dillon 	close(fd);
5456a6e350fSMatthew Dillon 	usleep(1000);
546*a360fddeSJohn Marino 	pidfile_close(pfh);
547*a360fddeSJohn Marino 	pidfile_remove(pfh);
5486a6e350fSMatthew Dillon }
5496a6e350fSMatthew Dillon 
55083f2a3aaSMatthew Dillon /*
55183f2a3aaSMatthew Dillon  * Initialize new config data (new or old style)
55283f2a3aaSMatthew Dillon  */
55383f2a3aaSMatthew Dillon static void
55483f2a3aaSMatthew Dillon config_init(const char *path, struct hammer_ioc_config *config)
55583f2a3aaSMatthew Dillon {
55683f2a3aaSMatthew Dillon 	const char *snapshots;
55783f2a3aaSMatthew Dillon 
55883f2a3aaSMatthew Dillon 	if (strcmp(path, "/tmp") == 0 ||
55983f2a3aaSMatthew Dillon 	    strcmp(path, "/var/tmp") == 0 ||
56083f2a3aaSMatthew Dillon 	    strcmp(path, "/usr/obj") == 0) {
56183f2a3aaSMatthew Dillon 		snapshots = "snapshots 0d 0d\n";
56283f2a3aaSMatthew Dillon 	} else {
56383f2a3aaSMatthew Dillon 		snapshots = "snapshots 1d 60d\n";
56483f2a3aaSMatthew Dillon 	}
56583f2a3aaSMatthew Dillon 	bzero(config->config.text, sizeof(config->config.text));
56683f2a3aaSMatthew Dillon 	snprintf(config->config.text, sizeof(config->config.text) - 1, "%s%s",
56783f2a3aaSMatthew Dillon 		snapshots,
56883f2a3aaSMatthew Dillon 		"prune     1d 5m\n"
56983f2a3aaSMatthew Dillon 		"rebalance 1d 5m\n"
570bb29b5d8SMatthew Dillon 		"#dedup	   1d 5m\n"
57183f2a3aaSMatthew Dillon 		"reblock   1d 5m\n"
572f6532f03SThomas Nikolajsen 		"recopy    30d 10m\n");
57383f2a3aaSMatthew Dillon }
57483f2a3aaSMatthew Dillon 
57583f2a3aaSMatthew Dillon /*
57683f2a3aaSMatthew Dillon  * Migrate configuration data from the old snapshots/config
57716265794SThomas Nikolajsen  * file to the new meta-data format.
57883f2a3aaSMatthew Dillon  */
57983f2a3aaSMatthew Dillon static void
58083f2a3aaSMatthew Dillon migrate_config(FILE *fp, struct hammer_ioc_config *config)
58183f2a3aaSMatthew Dillon {
58283f2a3aaSMatthew Dillon 	int n;
58383f2a3aaSMatthew Dillon 
58483f2a3aaSMatthew Dillon 	n = fread(config->config.text, 1, sizeof(config->config.text) - 1, fp);
58583f2a3aaSMatthew Dillon 	if (n >= 0)
58683f2a3aaSMatthew Dillon 		bzero(config->config.text + n, sizeof(config->config.text) - n);
58783f2a3aaSMatthew Dillon }
58883f2a3aaSMatthew Dillon 
58983f2a3aaSMatthew Dillon /*
59083f2a3aaSMatthew Dillon  * Migrate snapshot softlinks in the snapshots directory to the
59183f2a3aaSMatthew Dillon  * new meta-data format.  The softlinks are left intact, but
59283f2a3aaSMatthew Dillon  * this way the pruning code won't lose track of them if you
59383f2a3aaSMatthew Dillon  * happen to blow away the snapshots directory.
59483f2a3aaSMatthew Dillon  */
59583f2a3aaSMatthew Dillon static void
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);
61183f2a3aaSMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode)) {
61283f2a3aaSMatthew Dillon 				migrate_one_snapshot(fd, fpath, &snapshot);
61383f2a3aaSMatthew Dillon 			}
61483f2a3aaSMatthew Dillon 			free(fpath);
61583f2a3aaSMatthew Dillon 		}
61683f2a3aaSMatthew Dillon 		closedir(dir);
61783f2a3aaSMatthew Dillon 	}
61883f2a3aaSMatthew Dillon 	migrate_one_snapshot(fd, NULL, &snapshot);
61983f2a3aaSMatthew Dillon 
62083f2a3aaSMatthew Dillon }
62183f2a3aaSMatthew Dillon 
62283f2a3aaSMatthew Dillon /*
62383f2a3aaSMatthew Dillon  * Migrate a single snapshot.  If fpath is NULL the ioctl is flushed,
62483f2a3aaSMatthew Dillon  * otherwise it is flushed when it fills up.
62583f2a3aaSMatthew Dillon  */
62683f2a3aaSMatthew Dillon static void
62783f2a3aaSMatthew Dillon migrate_one_snapshot(int fd, const char *fpath,
62883f2a3aaSMatthew Dillon 		     struct hammer_ioc_snapshot *snapshot)
62983f2a3aaSMatthew Dillon {
63083f2a3aaSMatthew Dillon 	if (fpath) {
63183f2a3aaSMatthew Dillon 		struct hammer_snapshot_data *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 &&
67283f2a3aaSMatthew Dillon 		    ptr > linkbuf && ptr[-1] == '@') {
67383f2a3aaSMatthew Dillon 			tid = strtoull(ptr + 1, NULL, 16);
67483f2a3aaSMatthew Dillon 		}
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;
67983f2a3aaSMatthew Dillon 			snap->ts = (u_int64_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
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:
7306a6e350fSMatthew Dillon 		errx(1, "illegal suffix converting %s\n", ptr);
7316a6e350fSMatthew Dillon 		break;
7326a6e350fSMatthew Dillon 	}
7336a6e350fSMatthew Dillon 	return(val);
7346a6e350fSMatthew Dillon }
7356a6e350fSMatthew Dillon 
7366a6e350fSMatthew Dillon static const char *
7376a6e350fSMatthew Dillon dividing_slash(const char *path)
7386a6e350fSMatthew Dillon {
7396a6e350fSMatthew Dillon 	int len = strlen(path);
7406a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
7416a6e350fSMatthew Dillon 		return("");
7426a6e350fSMatthew Dillon 	else
7436a6e350fSMatthew Dillon 		return("/");
7446a6e350fSMatthew Dillon }
7456a6e350fSMatthew Dillon 
7466a6e350fSMatthew Dillon /*
7476a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
7486a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
7496a6e350fSMatthew Dillon  * current time_t so we can save it later on.
7506a6e350fSMatthew Dillon  *
7516a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
7526a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
7536a6e350fSMatthew Dillon  * of how close the last operation actually was.
754bdc54107SMatthew Dillon  *
755bdc54107SMatthew Dillon  * If ForceOpt is set always return true.
7566a6e350fSMatthew Dillon  */
7576a6e350fSMatthew Dillon static int
7586a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
7596a6e350fSMatthew Dillon 	time_t *savep)
7606a6e350fSMatthew Dillon {
7616a6e350fSMatthew Dillon 	char *check_path;
7626a6e350fSMatthew Dillon 	struct tm tp1;
7636a6e350fSMatthew Dillon 	struct tm tp2;
7646a6e350fSMatthew Dillon 	FILE *fp;
7656a6e350fSMatthew Dillon 	time_t baset, lastt;
7666a6e350fSMatthew Dillon 	char buf[256];
7676a6e350fSMatthew Dillon 
7686a6e350fSMatthew Dillon 	time(savep);
7696a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
7706a6e350fSMatthew Dillon 
7716a6e350fSMatthew Dillon 	/*
772bdc54107SMatthew Dillon 	 * Force run if -F
773bdc54107SMatthew Dillon 	 */
774bdc54107SMatthew Dillon 	if (ForceOpt)
775bdc54107SMatthew Dillon 		return(1);
776bdc54107SMatthew Dillon 
777bdc54107SMatthew Dillon 	/*
7786a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
7796a6e350fSMatthew Dillon 	 */
7806a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
7816a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
7826a6e350fSMatthew Dillon 	free(check_path);
7836a6e350fSMatthew Dillon 	if (fp == NULL)
7846a6e350fSMatthew Dillon 		return(1);
7856a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
7866a6e350fSMatthew Dillon 		fclose(fp);
7876a6e350fSMatthew Dillon 		return(1);
7886a6e350fSMatthew Dillon 	}
7896a6e350fSMatthew Dillon 	fclose(fp);
7906a6e350fSMatthew Dillon 
7916a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
7926a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
7936a6e350fSMatthew Dillon 
7946a6e350fSMatthew Dillon 	/*
7956a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
7966a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
7976a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
7986a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
7996a6e350fSMatthew Dillon 	 */
8006a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
8016a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
8026a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
8036a6e350fSMatthew Dillon 	}
8046a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
8056a6e350fSMatthew Dillon 		tp1.tm_min = 0;
8066a6e350fSMatthew Dillon 		tp2.tm_min = 0;
8076a6e350fSMatthew Dillon 	}
8086a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
8096a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
8106a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
8116a6e350fSMatthew Dillon 	}
8126a6e350fSMatthew Dillon 
8136a6e350fSMatthew Dillon 	baset = mktime(&tp1);
8146a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
8156a6e350fSMatthew Dillon 
8166a6e350fSMatthew Dillon #if 0
8176a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
8186a6e350fSMatthew Dillon #endif
8196a6e350fSMatthew Dillon 
8206a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
8216a6e350fSMatthew Dillon 		return(1);
8226a6e350fSMatthew Dillon 	return(0);
8236a6e350fSMatthew Dillon }
8246a6e350fSMatthew Dillon 
8256a6e350fSMatthew Dillon /*
8266a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
8276a6e350fSMatthew Dillon  */
8286a6e350fSMatthew Dillon static void
8296a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
8306a6e350fSMatthew Dillon 			time_t savet)
8316a6e350fSMatthew Dillon {
8326a6e350fSMatthew Dillon 	char *ocheck_path;
8336a6e350fSMatthew Dillon 	char *ncheck_path;
8346a6e350fSMatthew Dillon 	FILE *fp;
8356a6e350fSMatthew Dillon 
8366a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
8376a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
8386a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
8393b9fbdeaSMatthew Dillon 	if (fp) {
8406a6e350fSMatthew Dillon 		fprintf(fp, "0x%08llx\n", (long long)savet);
8416a6e350fSMatthew Dillon 		if (fclose(fp) == 0)
8426a6e350fSMatthew Dillon 			rename(ncheck_path, ocheck_path);
8436a6e350fSMatthew Dillon 		remove(ncheck_path);
8443b9fbdeaSMatthew Dillon 	} else {
8453b9fbdeaSMatthew Dillon 		fprintf(stderr, "hammer: Unable to create period-file %s: %s\n",
8463b9fbdeaSMatthew Dillon 			ncheck_path, strerror(errno));
8473b9fbdeaSMatthew Dillon 	}
8486a6e350fSMatthew Dillon }
8496a6e350fSMatthew Dillon 
850ff1c9800SMatthew Dillon /*
851ff1c9800SMatthew Dillon  * Simply count the number of softlinks in the snapshots dir
852ff1c9800SMatthew Dillon  */
853c6c298a7SMatthew Dillon static int
85483f2a3aaSMatthew Dillon check_softlinks(int fd, int new_config, const char *snapshots_path)
855c6c298a7SMatthew Dillon {
856c6c298a7SMatthew Dillon 	struct dirent *den;
857c6c298a7SMatthew Dillon 	struct stat st;
858c6c298a7SMatthew Dillon 	DIR *dir;
859c6c298a7SMatthew Dillon 	char *fpath;
860c6c298a7SMatthew Dillon 	int res = 0;
861c6c298a7SMatthew Dillon 
86283f2a3aaSMatthew Dillon 	/*
86383f2a3aaSMatthew Dillon 	 * Old-style softlink-based snapshots
86483f2a3aaSMatthew Dillon 	 */
865c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
866c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
867c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
868c6c298a7SMatthew Dillon 				continue;
869c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
870c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
871c6c298a7SMatthew Dillon 				++res;
872c6c298a7SMatthew Dillon 			free(fpath);
873c6c298a7SMatthew Dillon 		}
874c6c298a7SMatthew Dillon 		closedir(dir);
875c6c298a7SMatthew Dillon 	}
87683f2a3aaSMatthew Dillon 
87783f2a3aaSMatthew Dillon 	/*
87883f2a3aaSMatthew Dillon 	 * New-style snapshots are stored as filesystem meta-data,
87983f2a3aaSMatthew Dillon 	 * count those too.
88083f2a3aaSMatthew Dillon 	 */
88183f2a3aaSMatthew Dillon 	if (new_config) {
88283f2a3aaSMatthew Dillon 		struct hammer_ioc_snapshot snapshot;
88383f2a3aaSMatthew Dillon 
88483f2a3aaSMatthew Dillon 		bzero(&snapshot, sizeof(snapshot));
88583f2a3aaSMatthew Dillon 		do {
88683f2a3aaSMatthew Dillon 			if (ioctl(fd, HAMMERIOC_GET_SNAPSHOT, &snapshot) < 0) {
88783f2a3aaSMatthew Dillon 				err(2, "hammer cleanup: check_softlink "
88883f2a3aaSMatthew Dillon 					"snapshot error");
88983f2a3aaSMatthew Dillon 				/* not reached */
89083f2a3aaSMatthew Dillon 			}
89183f2a3aaSMatthew Dillon 			res += snapshot.count;
89283f2a3aaSMatthew Dillon 		} while (snapshot.head.error == 0 && snapshot.count);
89383f2a3aaSMatthew Dillon 	}
894c6c298a7SMatthew Dillon 	return (res);
895c6c298a7SMatthew Dillon }
896c6c298a7SMatthew Dillon 
8976a6e350fSMatthew Dillon /*
898ff1c9800SMatthew Dillon  * Clean up expired softlinks in the snapshots dir
899ff1c9800SMatthew Dillon  */
900ff1c9800SMatthew Dillon static void
90183f2a3aaSMatthew Dillon cleanup_softlinks(int fd, int new_config,
90283f2a3aaSMatthew Dillon 		  const char *snapshots_path, int arg2, char *arg3)
903ff1c9800SMatthew Dillon {
904ff1c9800SMatthew Dillon 	struct dirent *den;
905ff1c9800SMatthew Dillon 	struct stat st;
906ff1c9800SMatthew Dillon 	DIR *dir;
907ff1c9800SMatthew Dillon 	char *fpath;
9085e435c92SMatthew Dillon 	int anylink = 0;
9095e435c92SMatthew Dillon 
91009e1b0d6SMatthew Dillon 	if (arg3 != NULL && strstr(arg3, "any") != NULL)
9115e435c92SMatthew Dillon 		anylink = 1;
912ff1c9800SMatthew Dillon 
913ff1c9800SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
914ff1c9800SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
915ff1c9800SMatthew Dillon 			if (den->d_name[0] == '.')
916ff1c9800SMatthew Dillon 				continue;
917ff1c9800SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
918ff1c9800SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode) &&
9195e435c92SMatthew Dillon 			    (anylink || strncmp(den->d_name, "snap-", 5) == 0)
9205e435c92SMatthew Dillon 			) {
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];
94683f2a3aaSMatthew Dillon 		u_int32_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