xref: /dragonfly/sbin/hammer/cmd_cleanup.c (revision c6c298a7)
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  *
34*c6c298a7SMatthew Dillon  * $DragonFly: src/sbin/hammer/cmd_cleanup.c,v 1.2 2008/09/20 06:46:22 dillon Exp $
356a6e350fSMatthew Dillon  */
366a6e350fSMatthew Dillon /*
376a6e350fSMatthew Dillon  * Clean up a specific HAMMER filesystem or all HAMMER filesystems.
386a6e350fSMatthew Dillon  *
396a6e350fSMatthew Dillon  * Each filesystem is expected to have a <mount>/snapshots directory.
406a6e350fSMatthew Dillon  * No cleanup will be performed on any filesystem that does not.  If
416a6e350fSMatthew Dillon  * no filesystems are specified the 'df' program is run and any HAMMER
426a6e350fSMatthew Dillon  * or null-mounted hammer PFS's are extracted.
436a6e350fSMatthew Dillon  *
446a6e350fSMatthew Dillon  * The snapshots directory may contain a config file called 'config'.  If
456a6e350fSMatthew Dillon  * no config file is present one will be created with the following
466a6e350fSMatthew Dillon  * defaults:
476a6e350fSMatthew Dillon  *
486a6e350fSMatthew Dillon  *	snapshots 1d 60d	(0d 60d for /tmp, /var/tmp, /usr/obj)
496a6e350fSMatthew Dillon  *	prune     1d 5m
506a6e350fSMatthew Dillon  *	reblock   1d 5m
516a6e350fSMatthew Dillon  *	recopy    30d 5m
526a6e350fSMatthew Dillon  *
536a6e350fSMatthew Dillon  * All hammer commands create and maintain cycle files in the snapshots
546a6e350fSMatthew Dillon  * directory.
556a6e350fSMatthew Dillon  */
566a6e350fSMatthew Dillon 
576a6e350fSMatthew Dillon #include "hammer.h"
586a6e350fSMatthew Dillon 
596a6e350fSMatthew Dillon static void do_cleanup(const char *path);
606a6e350fSMatthew Dillon static int strtosecs(char *ptr);
616a6e350fSMatthew Dillon static const char *dividing_slash(const char *path);
626a6e350fSMatthew Dillon static int check_period(const char *snapshots_path, const char *cmd, int arg1,
636a6e350fSMatthew Dillon 			time_t *savep);
646a6e350fSMatthew Dillon static void save_period(const char *snapshots_path, const char *cmd,
656a6e350fSMatthew Dillon 			time_t savet);
66*c6c298a7SMatthew Dillon static int check_softlinks(const char *snapshots_path);
676a6e350fSMatthew Dillon 
686a6e350fSMatthew Dillon static int cleanup_snapshots(const char *path, const char *snapshots_path,
696a6e350fSMatthew Dillon 			      int arg1, int arg2);
706a6e350fSMatthew Dillon static int cleanup_prune(const char *path, const char *snapshots_path,
71*c6c298a7SMatthew Dillon 			      int arg1, int arg2, int snapshots_disabled);
726a6e350fSMatthew Dillon static int cleanup_reblock(const char *path, const char *snapshots_path,
736a6e350fSMatthew Dillon 			      int arg1, int arg2);
746a6e350fSMatthew Dillon static int cleanup_recopy(const char *path, const char *snapshots_path,
756a6e350fSMatthew Dillon 			      int arg1, int arg2);
766a6e350fSMatthew Dillon 
776a6e350fSMatthew Dillon static void runcmd(int *resp, const char *ctl, ...);
786a6e350fSMatthew Dillon 
796a6e350fSMatthew Dillon #define WS	" \t\r\n"
806a6e350fSMatthew Dillon #define MAXPFS	65536
816a6e350fSMatthew Dillon #define DIDBITS	(sizeof(int) * 8)
826a6e350fSMatthew Dillon 
836a6e350fSMatthew Dillon static int DidPFS[MAXPFS/DIDBITS];
846a6e350fSMatthew Dillon 
856a6e350fSMatthew Dillon void
866a6e350fSMatthew Dillon hammer_cmd_cleanup(char **av, int ac)
876a6e350fSMatthew Dillon {
886a6e350fSMatthew Dillon 	FILE *fp;
896a6e350fSMatthew Dillon 	char *ptr;
906a6e350fSMatthew Dillon 	char *path;
916a6e350fSMatthew Dillon 	char buf[256];
926a6e350fSMatthew Dillon 
936a6e350fSMatthew Dillon 	tzset();
946a6e350fSMatthew Dillon 	if (ac == 0) {
956a6e350fSMatthew Dillon 		fp = popen("df -t hammer,null", "r");
966a6e350fSMatthew Dillon 		if (fp == NULL)
976a6e350fSMatthew Dillon 			errx(1, "hammer cleanup: 'df' failed");
986a6e350fSMatthew Dillon 		while (fgets(buf, sizeof(buf), fp) != NULL) {
996a6e350fSMatthew Dillon 			ptr = strtok(buf, WS);
1006a6e350fSMatthew Dillon 			if (ptr && strcmp(ptr, "Filesystem") == 0)
1016a6e350fSMatthew Dillon 				continue;
1026a6e350fSMatthew Dillon 			if (ptr)
1036a6e350fSMatthew Dillon 				ptr = strtok(NULL, WS);
1046a6e350fSMatthew Dillon 			if (ptr)
1056a6e350fSMatthew Dillon 				ptr = strtok(NULL, WS);
1066a6e350fSMatthew Dillon 			if (ptr)
1076a6e350fSMatthew Dillon 				ptr = strtok(NULL, WS);
1086a6e350fSMatthew Dillon 			if (ptr)
1096a6e350fSMatthew Dillon 				ptr = strtok(NULL, WS);
1106a6e350fSMatthew Dillon 			if (ptr) {
1116a6e350fSMatthew Dillon 				path = strtok(NULL, WS);
1126a6e350fSMatthew Dillon 				if (path)
1136a6e350fSMatthew Dillon 					do_cleanup(path);
1146a6e350fSMatthew Dillon 			}
1156a6e350fSMatthew Dillon 		}
1166a6e350fSMatthew Dillon 		fclose(fp);
1176a6e350fSMatthew Dillon 	} else {
1186a6e350fSMatthew Dillon 		while (ac) {
1196a6e350fSMatthew Dillon 			do_cleanup(*av);
1206a6e350fSMatthew Dillon 			--ac;
1216a6e350fSMatthew Dillon 			++av;
1226a6e350fSMatthew Dillon 		}
1236a6e350fSMatthew Dillon 	}
1246a6e350fSMatthew Dillon }
1256a6e350fSMatthew Dillon 
1266a6e350fSMatthew Dillon static
1276a6e350fSMatthew Dillon void
1286a6e350fSMatthew Dillon do_cleanup(const char *path)
1296a6e350fSMatthew Dillon {
1306a6e350fSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1316a6e350fSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
1326a6e350fSMatthew Dillon 	char *snapshots_path;
1336a6e350fSMatthew Dillon 	char *config_path;
1346a6e350fSMatthew Dillon 	struct stat st;
1356a6e350fSMatthew Dillon 	char *cmd;
1366a6e350fSMatthew Dillon 	char *ptr;
1376a6e350fSMatthew Dillon 	int arg1;
1386a6e350fSMatthew Dillon 	int arg2;
1396a6e350fSMatthew Dillon 	time_t savet;
1406a6e350fSMatthew Dillon 	char buf[256];
1416a6e350fSMatthew Dillon 	FILE *fp;
142*c6c298a7SMatthew Dillon 	int snapshots_disabled = 0;
143*c6c298a7SMatthew Dillon 	int prune_warning = 0;
1446a6e350fSMatthew Dillon 	int fd;
1456a6e350fSMatthew Dillon 	int r;
1466a6e350fSMatthew Dillon 
1476a6e350fSMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1486a6e350fSMatthew Dillon 	bzero(&mrec_tmp, sizeof(mrec_tmp));
1496a6e350fSMatthew Dillon 	pfs.ondisk = &mrec_tmp.pfs.pfsd;
1506a6e350fSMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp.pfs.pfsd);
1516a6e350fSMatthew Dillon 	pfs.pfs_id = -1;
1526a6e350fSMatthew Dillon 
1536a6e350fSMatthew Dillon 	printf("cleanup %-20s -", path);
1546a6e350fSMatthew Dillon 	fd = open(path, O_RDONLY);
1556a6e350fSMatthew Dillon 	if (fd < 0) {
1566a6e350fSMatthew Dillon 		printf(" unable to access directory: %s\n", strerror(errno));
1576a6e350fSMatthew Dillon 		return;
1586a6e350fSMatthew Dillon 	}
1596a6e350fSMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1606a6e350fSMatthew Dillon 		printf(" not a HAMMER filesystem: %s\n", strerror(errno));
1616a6e350fSMatthew Dillon 		return;
1626a6e350fSMatthew Dillon 	}
1636a6e350fSMatthew Dillon 	close(fd);
1646a6e350fSMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
1656a6e350fSMatthew Dillon 		printf(" unrecognized HAMMER version\n");
1666a6e350fSMatthew Dillon 		return;
1676a6e350fSMatthew Dillon 	}
1686a6e350fSMatthew Dillon 
1696a6e350fSMatthew Dillon 	/*
1706a6e350fSMatthew Dillon 	 * Make sure we have not already handled this PFS.  Several nullfs
1716a6e350fSMatthew Dillon 	 * mounts might alias the same PFS.
1726a6e350fSMatthew Dillon 	 */
1736a6e350fSMatthew Dillon 	if (pfs.pfs_id < 0 || pfs.pfs_id >= MAXPFS) {
1746a6e350fSMatthew Dillon 		printf(" pfs_id %d illegal\n", pfs.pfs_id);
1756a6e350fSMatthew Dillon 		return;
1766a6e350fSMatthew Dillon 	}
1776a6e350fSMatthew Dillon 
1786a6e350fSMatthew Dillon 	if (DidPFS[pfs.pfs_id / DIDBITS] & (1 << (pfs.pfs_id % DIDBITS))) {
1796a6e350fSMatthew Dillon 		printf(" pfs_id %d already handled\n", pfs.pfs_id);
1806a6e350fSMatthew Dillon 		return;
1816a6e350fSMatthew Dillon 	}
1826a6e350fSMatthew Dillon 	DidPFS[pfs.pfs_id / DIDBITS] |= (1 << (pfs.pfs_id % DIDBITS));
1836a6e350fSMatthew Dillon 
1846a6e350fSMatthew Dillon 	/*
1856a6e350fSMatthew Dillon 	 * Create a snapshot directory if necessary, and a config file if
1866a6e350fSMatthew Dillon 	 * necessary.
1876a6e350fSMatthew Dillon 	 */
1886a6e350fSMatthew Dillon 	asprintf(&snapshots_path, "%s%ssnapshots", path, dividing_slash(path));
1896a6e350fSMatthew Dillon 	if (stat(snapshots_path, &st) < 0) {
1906a6e350fSMatthew Dillon 		if (mkdir(snapshots_path, 0755) != 0) {
1916a6e350fSMatthew Dillon 			free(snapshots_path);
1926a6e350fSMatthew Dillon 			printf(" unable to create snapshot dir: %s\n",
1936a6e350fSMatthew Dillon 				strerror(errno));
1946a6e350fSMatthew Dillon 			return;
1956a6e350fSMatthew Dillon 		}
1966a6e350fSMatthew Dillon 	}
1976a6e350fSMatthew Dillon 	asprintf(&config_path, "%s/config", snapshots_path);
1986a6e350fSMatthew Dillon 	if ((fp = fopen(config_path, "r")) == NULL) {
1996a6e350fSMatthew Dillon 		fp = fopen(config_path, "w");
2006a6e350fSMatthew Dillon 		if (fp == NULL) {
2016a6e350fSMatthew Dillon 			printf(" cannot create %s: %s\n",
2026a6e350fSMatthew Dillon 				config_path, strerror(errno));
2036a6e350fSMatthew Dillon 			return;
2046a6e350fSMatthew Dillon 		}
2056a6e350fSMatthew Dillon 		if (strcmp(path, "/tmp") == 0 ||
2066a6e350fSMatthew Dillon 		    strcmp(path, "/var/tmp") == 0 ||
2076a6e350fSMatthew Dillon 		    strcmp(path, "/usr/obj") == 0) {
2086a6e350fSMatthew Dillon 			fprintf(fp, "snapshots 0d 60d\n");
2096a6e350fSMatthew Dillon 		} else {
2106a6e350fSMatthew Dillon 			fprintf(fp, "snapshots 1d 60d\n");
2116a6e350fSMatthew Dillon 		}
2126a6e350fSMatthew Dillon 		fprintf(fp,
2136a6e350fSMatthew Dillon 			"prune     1d 5m\n"
2146a6e350fSMatthew Dillon 			"reblock   1d 5m\n"
2156a6e350fSMatthew Dillon 			"recopy    30d 10m\n");
2166a6e350fSMatthew Dillon 		fclose(fp);
2176a6e350fSMatthew Dillon 		fp = fopen(config_path, "r");
2186a6e350fSMatthew Dillon 	}
2196a6e350fSMatthew Dillon 	if (fp == NULL) {
2206a6e350fSMatthew Dillon 		printf(" cannot access %s: %s\n",
2216a6e350fSMatthew Dillon 		       config_path, strerror(errno));
2226a6e350fSMatthew Dillon 		return;
2236a6e350fSMatthew Dillon 	}
2246a6e350fSMatthew Dillon 
2256a6e350fSMatthew Dillon 	printf(" processing PFS #%d\n", pfs.pfs_id);
2266a6e350fSMatthew Dillon 
2276a6e350fSMatthew Dillon 	/*
2286a6e350fSMatthew Dillon 	 * Process the config file
2296a6e350fSMatthew Dillon 	 */
2306a6e350fSMatthew Dillon 	while (fgets(buf, sizeof(buf), fp) != NULL) {
2316a6e350fSMatthew Dillon 		cmd = strtok(buf, WS);
2326a6e350fSMatthew Dillon 		arg1 = 0;
2336a6e350fSMatthew Dillon 		arg2 = 0;
2346a6e350fSMatthew Dillon 		if ((ptr = strtok(NULL, WS)) != NULL) {
2356a6e350fSMatthew Dillon 			arg1 = strtosecs(ptr);
2366a6e350fSMatthew Dillon 			if ((ptr = strtok(NULL, WS)) != NULL)
2376a6e350fSMatthew Dillon 				arg2 = strtosecs(ptr);
2386a6e350fSMatthew Dillon 		}
2396a6e350fSMatthew Dillon 
2406a6e350fSMatthew Dillon 		printf("%20s - ", cmd);
2416a6e350fSMatthew Dillon 		fflush(stdout);
2426a6e350fSMatthew Dillon 
2436a6e350fSMatthew Dillon 		if (arg1 == 0) {
2446a6e350fSMatthew Dillon 			printf("disabled\n");
245*c6c298a7SMatthew Dillon 			if (strcmp(cmd, "snapshots") == 0) {
246*c6c298a7SMatthew Dillon 				if (check_softlinks(snapshots_path))
247*c6c298a7SMatthew Dillon 					prune_warning = 1;
248*c6c298a7SMatthew Dillon 				else
249*c6c298a7SMatthew Dillon 					snapshots_disabled = 1;
250*c6c298a7SMatthew Dillon 			}
2516a6e350fSMatthew Dillon 			continue;
2526a6e350fSMatthew Dillon 		}
2536a6e350fSMatthew Dillon 
2546a6e350fSMatthew Dillon 		r = 1;
2556a6e350fSMatthew Dillon 		if (strcmp(cmd, "snapshots") == 0) {
2566a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
2576a6e350fSMatthew Dillon 				printf("run\n");
2586a6e350fSMatthew Dillon 				r = cleanup_snapshots(path, snapshots_path,
2596a6e350fSMatthew Dillon 						  arg1, arg2);
2606a6e350fSMatthew Dillon 			} else {
2616a6e350fSMatthew Dillon 				printf("skip\n");
2626a6e350fSMatthew Dillon 			}
2636a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "prune") == 0) {
2646a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
265*c6c298a7SMatthew Dillon 				if (prune_warning)
266*c6c298a7SMatthew Dillon 					printf("run - WARNING snapshot softlinks present but snapshots disabled\n");
267*c6c298a7SMatthew Dillon 				else
268*c6c298a7SMatthew Dillon 					printf("run\n");
2696a6e350fSMatthew Dillon 				r = cleanup_prune(path, snapshots_path,
270*c6c298a7SMatthew Dillon 					      arg1, arg2, snapshots_disabled);
2716a6e350fSMatthew Dillon 			} else {
2726a6e350fSMatthew Dillon 				printf("skip\n");
2736a6e350fSMatthew Dillon 			}
2746a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "reblock") == 0) {
2756a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
2766a6e350fSMatthew Dillon 				printf("run");
2776a6e350fSMatthew Dillon 				fflush(stdout);
2786a6e350fSMatthew Dillon 				if (VerboseOpt)
2796a6e350fSMatthew Dillon 					printf("\n");
2806a6e350fSMatthew Dillon 				r = cleanup_reblock(path, snapshots_path,
2816a6e350fSMatthew Dillon 						arg1, arg2);
2826a6e350fSMatthew Dillon 			} else {
2836a6e350fSMatthew Dillon 				printf("skip\n");
2846a6e350fSMatthew Dillon 			}
2856a6e350fSMatthew Dillon 		} else if (strcmp(cmd, "recopy") == 0) {
2866a6e350fSMatthew Dillon 			if (check_period(snapshots_path, cmd, arg1, &savet)) {
2876a6e350fSMatthew Dillon 				printf("run");
2886a6e350fSMatthew Dillon 				fflush(stdout);
2896a6e350fSMatthew Dillon 				if (VerboseOpt)
2906a6e350fSMatthew Dillon 					printf("\n");
2916a6e350fSMatthew Dillon 				r = cleanup_recopy(path, snapshots_path,
2926a6e350fSMatthew Dillon 					       arg1, arg2);
2936a6e350fSMatthew Dillon 			} else {
2946a6e350fSMatthew Dillon 				printf("skip\n");
2956a6e350fSMatthew Dillon 			}
2966a6e350fSMatthew Dillon 		} else {
2976a6e350fSMatthew Dillon 			printf("unknown directive\n");
2986a6e350fSMatthew Dillon 			r = 1;
2996a6e350fSMatthew Dillon 		}
3006a6e350fSMatthew Dillon 		if (r == 0)
3016a6e350fSMatthew Dillon 			save_period(snapshots_path, cmd, savet);
3026a6e350fSMatthew Dillon 	}
3036a6e350fSMatthew Dillon 	fclose(fp);
3046a6e350fSMatthew Dillon 	usleep(1000);
3056a6e350fSMatthew Dillon }
3066a6e350fSMatthew Dillon 
3076a6e350fSMatthew Dillon static
3086a6e350fSMatthew Dillon int
3096a6e350fSMatthew Dillon strtosecs(char *ptr)
3106a6e350fSMatthew Dillon {
3116a6e350fSMatthew Dillon 	int val;
3126a6e350fSMatthew Dillon 
3136a6e350fSMatthew Dillon 	val = strtol(ptr, &ptr, 0);
3146a6e350fSMatthew Dillon 	switch(*ptr) {
3156a6e350fSMatthew Dillon 	case 'd':
3166a6e350fSMatthew Dillon 		val *= 24;
3176a6e350fSMatthew Dillon 		/* fall through */
3186a6e350fSMatthew Dillon 	case 'h':
3196a6e350fSMatthew Dillon 		val *= 60;
3206a6e350fSMatthew Dillon 		/* fall through */
3216a6e350fSMatthew Dillon 	case 'm':
3226a6e350fSMatthew Dillon 		val *= 60;
3236a6e350fSMatthew Dillon 		/* fall through */
3246a6e350fSMatthew Dillon 	case 's':
3256a6e350fSMatthew Dillon 		break;
3266a6e350fSMatthew Dillon 	default:
3276a6e350fSMatthew Dillon 		errx(1, "illegal suffix converting %s\n", ptr);
3286a6e350fSMatthew Dillon 		break;
3296a6e350fSMatthew Dillon 	}
3306a6e350fSMatthew Dillon 	return(val);
3316a6e350fSMatthew Dillon }
3326a6e350fSMatthew Dillon 
3336a6e350fSMatthew Dillon static const char *
3346a6e350fSMatthew Dillon dividing_slash(const char *path)
3356a6e350fSMatthew Dillon {
3366a6e350fSMatthew Dillon 	int len = strlen(path);
3376a6e350fSMatthew Dillon 	if (len && path[len-1] == '/')
3386a6e350fSMatthew Dillon 		return("");
3396a6e350fSMatthew Dillon 	else
3406a6e350fSMatthew Dillon 		return("/");
3416a6e350fSMatthew Dillon }
3426a6e350fSMatthew Dillon 
3436a6e350fSMatthew Dillon /*
3446a6e350fSMatthew Dillon  * Check whether the desired period has elapsed since the last successful
3456a6e350fSMatthew Dillon  * run.  The run may take a while and cross a boundary so we remember the
3466a6e350fSMatthew Dillon  * current time_t so we can save it later on.
3476a6e350fSMatthew Dillon  *
3486a6e350fSMatthew Dillon  * Periods in minutes, hours, or days are assumed to have been crossed
3496a6e350fSMatthew Dillon  * if the local time crosses a minute, hour, or day boundary regardless
3506a6e350fSMatthew Dillon  * of how close the last operation actually was.
3516a6e350fSMatthew Dillon  */
3526a6e350fSMatthew Dillon static int
3536a6e350fSMatthew Dillon check_period(const char *snapshots_path, const char *cmd, int arg1,
3546a6e350fSMatthew Dillon 	time_t *savep)
3556a6e350fSMatthew Dillon {
3566a6e350fSMatthew Dillon 	char *check_path;
3576a6e350fSMatthew Dillon 	struct tm tp1;
3586a6e350fSMatthew Dillon 	struct tm tp2;
3596a6e350fSMatthew Dillon 	FILE *fp;
3606a6e350fSMatthew Dillon 	time_t baset, lastt;
3616a6e350fSMatthew Dillon 	char buf[256];
3626a6e350fSMatthew Dillon 
3636a6e350fSMatthew Dillon 	time(savep);
3646a6e350fSMatthew Dillon 	localtime_r(savep, &tp1);
3656a6e350fSMatthew Dillon 
3666a6e350fSMatthew Dillon 	/*
3676a6e350fSMatthew Dillon 	 * Retrieve the start time of the last successful operation.
3686a6e350fSMatthew Dillon 	 */
3696a6e350fSMatthew Dillon 	asprintf(&check_path, "%s/.%s.period", snapshots_path, cmd);
3706a6e350fSMatthew Dillon 	fp = fopen(check_path, "r");
3716a6e350fSMatthew Dillon 	free(check_path);
3726a6e350fSMatthew Dillon 	if (fp == NULL)
3736a6e350fSMatthew Dillon 		return(1);
3746a6e350fSMatthew Dillon 	if (fgets(buf, sizeof(buf), fp) == NULL) {
3756a6e350fSMatthew Dillon 		fclose(fp);
3766a6e350fSMatthew Dillon 		return(1);
3776a6e350fSMatthew Dillon 	}
3786a6e350fSMatthew Dillon 	fclose(fp);
3796a6e350fSMatthew Dillon 
3806a6e350fSMatthew Dillon 	lastt = strtol(buf, NULL, 0);
3816a6e350fSMatthew Dillon 	localtime_r(&lastt, &tp2);
3826a6e350fSMatthew Dillon 
3836a6e350fSMatthew Dillon 	/*
3846a6e350fSMatthew Dillon 	 * Normalize the times.  e.g. if asked to do something on a 1-day
3856a6e350fSMatthew Dillon 	 * interval the operation will be performed as soon as the day
3866a6e350fSMatthew Dillon 	 * turns over relative to the previous operation, even if the previous
3876a6e350fSMatthew Dillon 	 * operation ran a few seconds ago just before midnight.
3886a6e350fSMatthew Dillon 	 */
3896a6e350fSMatthew Dillon 	if (arg1 % 60 == 0) {
3906a6e350fSMatthew Dillon 		tp1.tm_sec = 0;
3916a6e350fSMatthew Dillon 		tp2.tm_sec = 0;
3926a6e350fSMatthew Dillon 	}
3936a6e350fSMatthew Dillon 	if (arg1 % (60 * 60) == 0) {
3946a6e350fSMatthew Dillon 		tp1.tm_min = 0;
3956a6e350fSMatthew Dillon 		tp2.tm_min = 0;
3966a6e350fSMatthew Dillon 	}
3976a6e350fSMatthew Dillon 	if (arg1 % (24 * 60 * 60) == 0) {
3986a6e350fSMatthew Dillon 		tp1.tm_hour = 0;
3996a6e350fSMatthew Dillon 		tp2.tm_hour = 0;
4006a6e350fSMatthew Dillon 	}
4016a6e350fSMatthew Dillon 
4026a6e350fSMatthew Dillon 	baset = mktime(&tp1);
4036a6e350fSMatthew Dillon 	lastt = mktime(&tp2);
4046a6e350fSMatthew Dillon 
4056a6e350fSMatthew Dillon #if 0
4066a6e350fSMatthew Dillon 	printf("%lld vs %lld\n", (long long)(baset - lastt), (long long)arg1);
4076a6e350fSMatthew Dillon #endif
4086a6e350fSMatthew Dillon 
4096a6e350fSMatthew Dillon 	if ((int)(baset - lastt) >= arg1)
4106a6e350fSMatthew Dillon 		return(1);
4116a6e350fSMatthew Dillon 	return(0);
4126a6e350fSMatthew Dillon }
4136a6e350fSMatthew Dillon 
4146a6e350fSMatthew Dillon /*
4156a6e350fSMatthew Dillon  * Store the start time of the last successful operation.
4166a6e350fSMatthew Dillon  */
4176a6e350fSMatthew Dillon static void
4186a6e350fSMatthew Dillon save_period(const char *snapshots_path, const char *cmd,
4196a6e350fSMatthew Dillon 			time_t savet)
4206a6e350fSMatthew Dillon {
4216a6e350fSMatthew Dillon 	char *ocheck_path;
4226a6e350fSMatthew Dillon 	char *ncheck_path;
4236a6e350fSMatthew Dillon 	FILE *fp;
4246a6e350fSMatthew Dillon 
4256a6e350fSMatthew Dillon 	asprintf(&ocheck_path, "%s/.%s.period", snapshots_path, cmd);
4266a6e350fSMatthew Dillon 	asprintf(&ncheck_path, "%s/.%s.period.new", snapshots_path, cmd);
4276a6e350fSMatthew Dillon 	fp = fopen(ncheck_path, "w");
4286a6e350fSMatthew Dillon 	fprintf(fp, "0x%08llx\n", (long long)savet);
4296a6e350fSMatthew Dillon 	if (fclose(fp) == 0)
4306a6e350fSMatthew Dillon 		rename(ncheck_path, ocheck_path);
4316a6e350fSMatthew Dillon 	remove(ncheck_path);
4326a6e350fSMatthew Dillon }
4336a6e350fSMatthew Dillon 
434*c6c298a7SMatthew Dillon static int
435*c6c298a7SMatthew Dillon check_softlinks(const char *snapshots_path)
436*c6c298a7SMatthew Dillon {
437*c6c298a7SMatthew Dillon 	struct dirent *den;
438*c6c298a7SMatthew Dillon 	struct stat st;
439*c6c298a7SMatthew Dillon 	DIR *dir;
440*c6c298a7SMatthew Dillon 	char *fpath;
441*c6c298a7SMatthew Dillon 	int res = 0;
442*c6c298a7SMatthew Dillon 
443*c6c298a7SMatthew Dillon 	/*
444*c6c298a7SMatthew Dillon 	 * Force snapshots_disabled to 0 if the snapshots directory
445*c6c298a7SMatthew Dillon 	 * contains softlinks.
446*c6c298a7SMatthew Dillon 	 */
447*c6c298a7SMatthew Dillon 	if ((dir = opendir(snapshots_path)) != NULL) {
448*c6c298a7SMatthew Dillon 		while ((den = readdir(dir)) != NULL) {
449*c6c298a7SMatthew Dillon 			if (den->d_name[0] == '.')
450*c6c298a7SMatthew Dillon 				continue;
451*c6c298a7SMatthew Dillon 			asprintf(&fpath, "%s/%s", snapshots_path, den->d_name);
452*c6c298a7SMatthew Dillon 			if (lstat(fpath, &st) == 0 && S_ISLNK(st.st_mode))
453*c6c298a7SMatthew Dillon 				++res;
454*c6c298a7SMatthew Dillon 			free(fpath);
455*c6c298a7SMatthew Dillon 		}
456*c6c298a7SMatthew Dillon 		closedir(dir);
457*c6c298a7SMatthew Dillon 	}
458*c6c298a7SMatthew Dillon 	return(res);
459*c6c298a7SMatthew Dillon }
460*c6c298a7SMatthew Dillon 
4616a6e350fSMatthew Dillon /*
4626a6e350fSMatthew Dillon  * Issue a snapshot.
4636a6e350fSMatthew Dillon  */
4646a6e350fSMatthew Dillon static int
4656a6e350fSMatthew Dillon cleanup_snapshots(const char *path __unused, const char *snapshots_path,
4666a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2 __unused)
4676a6e350fSMatthew Dillon {
4686a6e350fSMatthew Dillon 	int r;
4696a6e350fSMatthew Dillon 
4706a6e350fSMatthew Dillon 	runcmd(&r, "hammer snapshot %s", snapshots_path);
4716a6e350fSMatthew Dillon 	return(r);
4726a6e350fSMatthew Dillon }
4736a6e350fSMatthew Dillon 
4746a6e350fSMatthew Dillon static int
4756a6e350fSMatthew Dillon cleanup_prune(const char *path __unused, const char *snapshots_path,
476*c6c298a7SMatthew Dillon 		  int arg1 __unused, int arg2, int snapshots_disabled)
4776a6e350fSMatthew Dillon {
478*c6c298a7SMatthew Dillon 	/*
479*c6c298a7SMatthew Dillon 	 * If snapshots have been disabled run prune-everything instead
480*c6c298a7SMatthew Dillon 	 * of prune.
481*c6c298a7SMatthew Dillon 	 */
482*c6c298a7SMatthew Dillon 	if (snapshots_disabled && arg2) {
483*c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune-everything %s",
484*c6c298a7SMatthew Dillon 			snapshots_path, arg2, path);
485*c6c298a7SMatthew Dillon 	} else if (snapshots_disabled) {
486*c6c298a7SMatthew Dillon 		runcmd(NULL, "hammer prune-everything %s", path);
487*c6c298a7SMatthew Dillon 	} else if (arg2) {
4886a6e350fSMatthew Dillon 		runcmd(NULL, "hammer -c %s/.prune.cycle -t %d prune %s",
4896a6e350fSMatthew Dillon 			snapshots_path, arg2, snapshots_path);
4906a6e350fSMatthew Dillon 	} else {
4916a6e350fSMatthew Dillon 		runcmd(NULL, "hammer prune %s", snapshots_path);
4926a6e350fSMatthew Dillon 	}
4936a6e350fSMatthew Dillon 	return(0);
4946a6e350fSMatthew Dillon }
4956a6e350fSMatthew Dillon 
4966a6e350fSMatthew Dillon static int
4976a6e350fSMatthew Dillon cleanup_reblock(const char *path, const char *snapshots_path,
4986a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
4996a6e350fSMatthew Dillon {
5006a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
5016a6e350fSMatthew Dillon 		printf(".");
5026a6e350fSMatthew Dillon 		fflush(stdout);
5036a6e350fSMatthew Dillon 	}
5046a6e350fSMatthew Dillon 	runcmd(NULL,
5056a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-1.cycle -t %d reblock-btree %s 95",
5066a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
5076a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
5086a6e350fSMatthew Dillon 		printf(".");
5096a6e350fSMatthew Dillon 		fflush(stdout);
5106a6e350fSMatthew Dillon 	}
5116a6e350fSMatthew Dillon 	runcmd(NULL,
5126a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-2.cycle -t %d reblock-inodes %s 95",
5136a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
5146a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
5156a6e350fSMatthew Dillon 		printf(".");
5166a6e350fSMatthew Dillon 		fflush(stdout);
5176a6e350fSMatthew Dillon 	}
5186a6e350fSMatthew Dillon 	runcmd(NULL,
5196a6e350fSMatthew Dillon 	       "hammer -c %s/.reblock-3.cycle -t %d reblock-data %s 95",
5206a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
5216a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
5226a6e350fSMatthew Dillon 		printf("\n");
5236a6e350fSMatthew Dillon 	return(0);
5246a6e350fSMatthew Dillon }
5256a6e350fSMatthew Dillon 
5266a6e350fSMatthew Dillon static int
5276a6e350fSMatthew Dillon cleanup_recopy(const char *path, const char *snapshots_path,
5286a6e350fSMatthew Dillon 		  int arg1 __unused, int arg2)
5296a6e350fSMatthew Dillon {
5306a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
5316a6e350fSMatthew Dillon 		printf(".");
5326a6e350fSMatthew Dillon 		fflush(stdout);
5336a6e350fSMatthew Dillon 	}
5346a6e350fSMatthew Dillon 	runcmd(NULL,
5356a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-1.cycle -t %d reblock-btree %s",
5366a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
5376a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
5386a6e350fSMatthew Dillon 		printf(".");
5396a6e350fSMatthew Dillon 		fflush(stdout);
5406a6e350fSMatthew Dillon 	}
5416a6e350fSMatthew Dillon 	runcmd(NULL,
5426a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-2.cycle -t %d reblock-inodes %s",
5436a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
5446a6e350fSMatthew Dillon 	if (VerboseOpt == 0) {
5456a6e350fSMatthew Dillon 		printf(".");
5466a6e350fSMatthew Dillon 		fflush(stdout);
5476a6e350fSMatthew Dillon 	}
5486a6e350fSMatthew Dillon 	runcmd(NULL,
5496a6e350fSMatthew Dillon 	       "hammer -c %s/.recopy-3.cycle -t %d reblock-data %s",
5506a6e350fSMatthew Dillon 	       snapshots_path, arg2, path);
5516a6e350fSMatthew Dillon 	if (VerboseOpt == 0)
5526a6e350fSMatthew Dillon 		printf("\n");
5536a6e350fSMatthew Dillon 	return(0);
5546a6e350fSMatthew Dillon }
5556a6e350fSMatthew Dillon 
5566a6e350fSMatthew Dillon static
5576a6e350fSMatthew Dillon void
5586a6e350fSMatthew Dillon runcmd(int *resp, const char *ctl, ...)
5596a6e350fSMatthew Dillon {
5606a6e350fSMatthew Dillon 	va_list va;
5616a6e350fSMatthew Dillon 	char *cmd;
5626a6e350fSMatthew Dillon 	char *arg;
5636a6e350fSMatthew Dillon 	char **av;
5646a6e350fSMatthew Dillon 	int n;
5656a6e350fSMatthew Dillon 	int nmax;
5666a6e350fSMatthew Dillon 	int res;
5676a6e350fSMatthew Dillon 	pid_t pid;
5686a6e350fSMatthew Dillon 
5696a6e350fSMatthew Dillon 	/*
5706a6e350fSMatthew Dillon 	 * Generate the command
5716a6e350fSMatthew Dillon 	 */
5726a6e350fSMatthew Dillon 	va_start(va, ctl);
5736a6e350fSMatthew Dillon 	vasprintf(&cmd, ctl, va);
5746a6e350fSMatthew Dillon 	va_end(va);
5756a6e350fSMatthew Dillon 	if (VerboseOpt)
5766a6e350fSMatthew Dillon 		printf("    %s\n", cmd);
5776a6e350fSMatthew Dillon 
5786a6e350fSMatthew Dillon 	/*
5796a6e350fSMatthew Dillon 	 * Break us down into arguments.  We do not just use system() here
5806a6e350fSMatthew Dillon 	 * because it blocks SIGINT and friends.
5816a6e350fSMatthew Dillon 	 */
5826a6e350fSMatthew Dillon 	n = 0;
5836a6e350fSMatthew Dillon 	nmax = 16;
5846a6e350fSMatthew Dillon 	av = malloc(sizeof(char *) * nmax);
5856a6e350fSMatthew Dillon 
5866a6e350fSMatthew Dillon 	for (arg = strtok(cmd, WS); arg; arg = strtok(NULL, WS)) {
5876a6e350fSMatthew Dillon 		if (n == nmax) {
5886a6e350fSMatthew Dillon 			nmax += 16;
5896a6e350fSMatthew Dillon 			av = realloc(av, sizeof(char *) * nmax);
5906a6e350fSMatthew Dillon 		}
5916a6e350fSMatthew Dillon 		av[n++] = arg;
5926a6e350fSMatthew Dillon 	}
5936a6e350fSMatthew Dillon 
5946a6e350fSMatthew Dillon 	/*
5956a6e350fSMatthew Dillon 	 * Run the command.
5966a6e350fSMatthew Dillon 	 */
5976a6e350fSMatthew Dillon 	if ((pid = fork()) == 0) {
5986a6e350fSMatthew Dillon 		if (VerboseOpt < 2) {
5996a6e350fSMatthew Dillon 			int fd = open("/dev/null", O_RDWR);
6006a6e350fSMatthew Dillon 			dup2(fd, 1);
6016a6e350fSMatthew Dillon 			close(fd);
6026a6e350fSMatthew Dillon 		}
6036a6e350fSMatthew Dillon 		execvp(av[0], av);
6046a6e350fSMatthew Dillon 		_exit(127);
6056a6e350fSMatthew Dillon 	} else if (pid < 0) {
6066a6e350fSMatthew Dillon 		res = 127;
6076a6e350fSMatthew Dillon 	} else {
6086a6e350fSMatthew Dillon 		int status;
6096a6e350fSMatthew Dillon 		while (waitpid(pid, &status, 0) != pid)
6106a6e350fSMatthew Dillon 			;
6116a6e350fSMatthew Dillon 		res = WEXITSTATUS(status);
6126a6e350fSMatthew Dillon 	}
6136a6e350fSMatthew Dillon 
6146a6e350fSMatthew Dillon 	free(cmd);
6156a6e350fSMatthew Dillon 	free(av);
6166a6e350fSMatthew Dillon 	if (resp)
6176a6e350fSMatthew Dillon 		*resp = res;
6186a6e350fSMatthew Dillon }
6196a6e350fSMatthew Dillon 
6206a6e350fSMatthew Dillon 
621