xref: /dflybsd-src/sbin/hammer/hammer.c (revision c4bf625e67439f34b29bfd33c4e2555ffea63ce9)
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sbin/hammer/hammer.c,v 1.13 2008/03/25 03:57:58 dillon Exp $
35  */
36 
37 #include "hammer.h"
38 #include <math.h>
39 
40 static void hammer_parsetime(u_int64_t *tidp, const char *timestr);
41 static void hammer_waitsync(int dosleep);
42 static void hammer_parsedevs(const char *blkdevs);
43 static void usage(int exit_code);
44 
45 int RecurseOpt;
46 int VerboseOpt;
47 int NoSyncOpt;
48 const char *LinkPath;
49 
50 int
51 main(int ac, char **av)
52 {
53 	struct timeval tv;
54 	u_int64_t tid;
55 	int ch;
56 	u_int32_t status;
57 	char *blkdevs = NULL;
58 
59 	while ((ch = getopt(ac, av, "hf:rs:vx")) != -1) {
60 		switch(ch) {
61 		case 'h':
62 			usage(0);
63 			/* not reached */
64 		case 'r':
65 			RecurseOpt = 1;
66 			break;
67 		case 'f':
68 			blkdevs = optarg;
69 			break;
70 		case 's':
71 			LinkPath = optarg;
72 			break;
73 		case 'v':
74 			++VerboseOpt;
75 			break;
76 		case 'x':
77 			++NoSyncOpt;
78 			break;
79 		default:
80 			usage(1);
81 			/* not reached */
82 		}
83 	}
84 	ac -= optind;
85 	av += optind;
86 	if (ac < 1) {
87 		usage(1);
88 		/* not reached */
89 	}
90 
91 	if (strcmp(av[0], "now") == 0) {
92 		hammer_waitsync(1);
93 		tid = (hammer_tid_t)time(NULL) * 1000000000LLU;
94 		printf("0x%08x\n", (int)(tid / 1000000000LL));
95 		exit(0);
96 	}
97 	if (strcmp(av[0], "now64") == 0) {
98 		hammer_waitsync(0);
99 		gettimeofday(&tv, NULL);
100 		tid = (hammer_tid_t)tv.tv_sec * 1000000000LLU +
101 			tv.tv_usec * 1000LLU;
102 		printf("0x%016llx\n", tid);
103 		exit(0);
104 	}
105 	if (strcmp(av[0], "stamp") == 0) {
106 		if (av[1] == NULL)
107 			usage(1);
108 		hammer_parsetime(&tid, av[1]);
109 		printf("0x%08x\n", (int)(tid / 1000000000LL));
110 		exit(0);
111 	}
112 	if (strcmp(av[0], "stamp64") == 0) {
113 		if (av[1] == NULL)
114 			usage(1);
115 		hammer_parsetime(&tid, av[1]);
116 		printf("0x%016llx\n", tid);
117 		exit(0);
118 	}
119 	if (strcmp(av[0], "namekey") == 0) {
120 		int64_t key;
121 
122 		if (av[1] == NULL)
123 			usage(1);
124 		key = (int64_t)(crc32(av[1], strlen(av[1])) & 0x7FFFFFFF) << 32;
125 		if (key == 0)
126 			key |= 0x100000000LL;
127 		printf("0x%016llx\n", key);
128 		exit(0);
129 	}
130 	if (strcmp(av[0], "namekey32") == 0) {
131 		int32_t key;
132 
133 		if (av[1] == NULL)
134 			usage(1);
135 		key = crc32(av[1], strlen(av[1])) & 0x7FFFFFFF;
136 		if (key == 0)
137 			++key;
138 		printf("0x%08x\n", key);
139 		exit(0);
140 	}
141 	if (strcmp(av[0], "prune") == 0) {
142 		hammer_cmd_prune(av + 1, ac - 1);
143 		exit(0);
144 	}
145 
146 	if (strncmp(av[0], "history", 7) == 0) {
147 		hammer_cmd_history(av[0] + 7, av + 1, ac - 1);
148 		exit(0);
149 	}
150 	if (strcmp(av[0], "reblock") == 0) {
151 		hammer_cmd_reblock(av + 1, ac - 1);
152 		exit(0);
153 	}
154 
155 	uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
156 	if (status != uuid_s_ok) {
157 		errx(1, "uuids file does not have the DragonFly "
158 			"HAMMER filesystem type");
159 	}
160 
161 	if (strcmp(av[0], "show") == 0) {
162 		hammer_off_t node_offset = (hammer_off_t)-1;
163 
164 		hammer_parsedevs(blkdevs);
165 		if (ac > 1)
166 			sscanf(av[1], "%llx", &node_offset);
167 		hammer_cmd_show(node_offset, 0, NULL, NULL);
168 		exit(0);
169 	}
170 	if (strcmp(av[0], "blockmap") == 0) {
171 		hammer_parsedevs(blkdevs);
172 		hammer_cmd_blockmap();
173 		exit(0);
174 	}
175 	usage(1);
176 	/* not reached */
177 	return(0);
178 }
179 
180 /*
181  * Parse a timestamp for the mount point
182  *
183  * yyyymmddhhmmss
184  * -N[s/h/d/m/y]
185  */
186 static
187 void
188 hammer_parsetime(u_int64_t *tidp, const char *timestr)
189 {
190 	struct timeval tv;
191 	struct tm tm;
192 	int32_t n;
193 	char c;
194 
195 	gettimeofday(&tv, NULL);
196 
197 	if (*timestr == 0)
198 		usage(1);
199 
200 	if (isalpha(timestr[strlen(timestr)-1])) {
201 		if (sscanf(timestr, "%d%c", &n, &c) != 2)
202 			usage(1);
203 		switch(c) {
204 		case 'Y':
205 			n *= 365;
206 			goto days;
207 		case 'M':
208 			n *= 30;
209 			/* fall through */
210 		case 'D':
211 		days:
212 			n *= 24;
213 			/* fall through */
214 		case 'h':
215 			n *= 60;
216 			/* fall through */
217 		case 'm':
218 			n *= 60;
219 			/* fall through */
220 		case 's':
221 			tv.tv_sec -= n;
222 			break;
223 		default:
224 			usage(1);
225 		}
226 	} else {
227 		double seconds = 0;
228 
229 		localtime_r(&tv.tv_sec, &tm);
230 		seconds = (double)tm.tm_sec;
231 		tm.tm_year += 1900;
232 		tm.tm_mon += 1;
233 		n = sscanf(timestr, "%4d%2d%2d:%2d%2d%lf",
234 			   &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
235 			   &tm.tm_hour, &tm.tm_min, &seconds);
236 		tm.tm_mon -= 1;
237 		tm.tm_year -= 1900;
238 		/* if [:hhmmss] is omitted, assume :000000.0 */
239 		if (n < 4)
240 			tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
241 		else
242 			tm.tm_sec = (int)seconds;
243 		tv.tv_sec = mktime(&tm);
244 		tv.tv_usec = (int)((seconds - floor(seconds)) * 1000000.0);
245 	}
246 	*tidp = (u_int64_t)tv.tv_sec * 1000000000LLU +
247 		tv.tv_usec * 1000LLU;
248 }
249 
250 /*
251  * If the TID is within 60 seconds of the current time we sync().  If
252  * dosleep is non-zero and the TID is within 1 second of the current time
253  * we wait for the second-hand to turn over.
254  *
255  * The NoSyncOpt prevents both the sync() call and any sleeps from occuring.
256  */
257 static
258 void
259 hammer_waitsync(int dosleep)
260 {
261 	time_t t1, t2;
262 
263 	if (NoSyncOpt == 0) {
264 		sync();
265 		t1 = t2 = time(NULL);
266 		while (dosleep && t1 == t2) {
267 			usleep(100000);
268 			t2 = time(NULL);
269 		}
270 	}
271 }
272 
273 static
274 void
275 hammer_parsedevs(const char *blkdevs)
276 {
277 	char *copy;
278 	char *volname;
279 
280 	if (blkdevs == NULL) {
281 		errx(1, "A -f blkdevs specification is required "
282 			"for this command");
283 	}
284 
285 	copy = strdup(blkdevs);
286 	while ((volname = copy) != NULL) {
287 		if ((copy = strchr(copy, ':')) != NULL)
288 			*copy++ = 0;
289 		setup_volume(-1, volname, 0, O_RDONLY);
290 	}
291 }
292 
293 static
294 void
295 usage(int exit_code)
296 {
297 	fprintf(stderr,
298 		"hammer -h\n"
299 		"hammer [-x] now[64]\n"
300 		"hammer stamp[64] <time>\n"
301 		"hammer [-s linkpath] prune <filesystem> [using <configfile>]\n"
302 		"hammer [-s linkpath] prune <filesystem> from <modulo_time> to "
303 				"<modulo_time> every <modulo_time>\n"
304 		"hammer prune <filesystem> everything\n"
305 		"hammer reblock <filesystem> [compact%%] (default 90%%)\n"
306 		"hammer history[@offset[,len]] <file-1>...<file-N>\n"
307 		"hammer -f blkdevs [-r] show\n"
308 		"hammer -f blkdevs blockmap\n"
309 	);
310 	fprintf(stderr, "time: +n[s/m/h/D/M/Y]\n"
311 			"time: yyyymmdd[:hhmmss]\n"
312 			"modulo_time: n{s,m,h,d,M,y}\n");
313 	exit(exit_code);
314 }
315 
316