xref: /dragonfly/sbin/hammer/hammer.c (revision 62f7f702)
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.14 2008/05/04 19:18:17 dillon Exp $
35  */
36 
37 #include "hammer.h"
38 #include <signal.h>
39 #include <math.h>
40 
41 static void hammer_parsetime(u_int64_t *tidp, const char *timestr);
42 static void hammer_waitsync(int dosleep);
43 static void hammer_parsedevs(const char *blkdevs);
44 static void sigalrm(int signo);
45 static void usage(int exit_code);
46 
47 int RecurseOpt;
48 int VerboseOpt;
49 int NoSyncOpt;
50 const char *LinkPath;
51 
52 int
53 main(int ac, char **av)
54 {
55 	struct timeval tv;
56 	u_int64_t tid;
57 	int ch;
58 	int timeout = 0;
59 	u_int32_t status;
60 	char *blkdevs = NULL;
61 
62 	while ((ch = getopt(ac, av, "hf:rs:t:vx")) != -1) {
63 		switch(ch) {
64 		case 'h':
65 			usage(0);
66 			/* not reached */
67 		case 'r':
68 			RecurseOpt = 1;
69 			break;
70 		case 'f':
71 			blkdevs = optarg;
72 			break;
73 		case 's':
74 			LinkPath = optarg;
75 			break;
76 		case 't':
77 			timeout = strtol(optarg, NULL, 0);
78 			break;
79 		case 'v':
80 			++VerboseOpt;
81 			break;
82 		case 'x':
83 			++NoSyncOpt;
84 			break;
85 		default:
86 			usage(1);
87 			/* not reached */
88 		}
89 	}
90 	ac -= optind;
91 	av += optind;
92 	if (ac < 1) {
93 		usage(1);
94 		/* not reached */
95 	}
96 
97 	if (timeout > 0) {
98 		signal(SIGALRM, sigalrm);
99 		alarm(timeout);
100 	}
101 
102 	if (strcmp(av[0], "now") == 0) {
103 		hammer_waitsync(1);
104 		tid = (hammer_tid_t)time(NULL) * 1000000000LLU;
105 		printf("0x%08x\n", (int)(tid / 1000000000LL));
106 		exit(0);
107 	}
108 	if (strcmp(av[0], "now64") == 0) {
109 		hammer_waitsync(0);
110 		gettimeofday(&tv, NULL);
111 		tid = (hammer_tid_t)tv.tv_sec * 1000000000LLU +
112 			tv.tv_usec * 1000LLU;
113 		printf("0x%016llx\n", tid);
114 		exit(0);
115 	}
116 	if (strcmp(av[0], "stamp") == 0) {
117 		if (av[1] == NULL)
118 			usage(1);
119 		hammer_parsetime(&tid, av[1]);
120 		printf("0x%08x\n", (int)(tid / 1000000000LL));
121 		exit(0);
122 	}
123 	if (strcmp(av[0], "stamp64") == 0) {
124 		if (av[1] == NULL)
125 			usage(1);
126 		hammer_parsetime(&tid, av[1]);
127 		printf("0x%016llx\n", tid);
128 		exit(0);
129 	}
130 	if (strcmp(av[0], "namekey") == 0) {
131 		int64_t key;
132 
133 		if (av[1] == NULL)
134 			usage(1);
135 		key = (int64_t)(crc32(av[1], strlen(av[1])) & 0x7FFFFFFF) << 32;
136 		if (key == 0)
137 			key |= 0x100000000LL;
138 		printf("0x%016llx\n", key);
139 		exit(0);
140 	}
141 	if (strcmp(av[0], "namekey32") == 0) {
142 		int32_t key;
143 
144 		if (av[1] == NULL)
145 			usage(1);
146 		key = crc32(av[1], strlen(av[1])) & 0x7FFFFFFF;
147 		if (key == 0)
148 			++key;
149 		printf("0x%08x\n", key);
150 		exit(0);
151 	}
152 	if (strcmp(av[0], "prune") == 0) {
153 		hammer_cmd_prune(av + 1, ac - 1);
154 		exit(0);
155 	}
156 
157 	if (strncmp(av[0], "history", 7) == 0) {
158 		hammer_cmd_history(av[0] + 7, av + 1, ac - 1);
159 		exit(0);
160 	}
161 	if (strcmp(av[0], "reblock") == 0) {
162 		hammer_cmd_reblock(av + 1, ac - 1);
163 		exit(0);
164 	}
165 
166 	uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
167 	if (status != uuid_s_ok) {
168 		errx(1, "uuids file does not have the DragonFly "
169 			"HAMMER filesystem type");
170 	}
171 
172 	if (strcmp(av[0], "show") == 0) {
173 		hammer_off_t node_offset = (hammer_off_t)-1;
174 
175 		hammer_parsedevs(blkdevs);
176 		if (ac > 1)
177 			sscanf(av[1], "%llx", &node_offset);
178 		hammer_cmd_show(node_offset, 0, NULL, NULL);
179 		exit(0);
180 	}
181 	if (strcmp(av[0], "blockmap") == 0) {
182 		hammer_parsedevs(blkdevs);
183 		hammer_cmd_blockmap();
184 		exit(0);
185 	}
186 	usage(1);
187 	/* not reached */
188 	return(0);
189 }
190 
191 /*
192  * Parse a timestamp for the mount point
193  *
194  * yyyymmddhhmmss
195  * -N[s/h/d/m/y]
196  */
197 static
198 void
199 hammer_parsetime(u_int64_t *tidp, const char *timestr)
200 {
201 	struct timeval tv;
202 	struct tm tm;
203 	int32_t n;
204 	char c;
205 
206 	gettimeofday(&tv, NULL);
207 
208 	if (*timestr == 0)
209 		usage(1);
210 
211 	if (isalpha(timestr[strlen(timestr)-1])) {
212 		if (sscanf(timestr, "%d%c", &n, &c) != 2)
213 			usage(1);
214 		switch(c) {
215 		case 'Y':
216 			n *= 365;
217 			goto days;
218 		case 'M':
219 			n *= 30;
220 			/* fall through */
221 		case 'D':
222 		days:
223 			n *= 24;
224 			/* fall through */
225 		case 'h':
226 			n *= 60;
227 			/* fall through */
228 		case 'm':
229 			n *= 60;
230 			/* fall through */
231 		case 's':
232 			tv.tv_sec -= n;
233 			break;
234 		default:
235 			usage(1);
236 		}
237 	} else {
238 		double seconds = 0;
239 
240 		localtime_r(&tv.tv_sec, &tm);
241 		seconds = (double)tm.tm_sec;
242 		tm.tm_year += 1900;
243 		tm.tm_mon += 1;
244 		n = sscanf(timestr, "%4d%2d%2d:%2d%2d%lf",
245 			   &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
246 			   &tm.tm_hour, &tm.tm_min, &seconds);
247 		tm.tm_mon -= 1;
248 		tm.tm_year -= 1900;
249 		/* if [:hhmmss] is omitted, assume :000000.0 */
250 		if (n < 4)
251 			tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
252 		else
253 			tm.tm_sec = (int)seconds;
254 		tv.tv_sec = mktime(&tm);
255 		tv.tv_usec = (int)((seconds - floor(seconds)) * 1000000.0);
256 	}
257 	*tidp = (u_int64_t)tv.tv_sec * 1000000000LLU +
258 		tv.tv_usec * 1000LLU;
259 }
260 
261 /*
262  * If the TID is within 60 seconds of the current time we sync().  If
263  * dosleep is non-zero and the TID is within 1 second of the current time
264  * we wait for the second-hand to turn over.
265  *
266  * The NoSyncOpt prevents both the sync() call and any sleeps from occuring.
267  */
268 static
269 void
270 hammer_waitsync(int dosleep)
271 {
272 	time_t t1, t2;
273 
274 	if (NoSyncOpt == 0) {
275 		sync();
276 		t1 = t2 = time(NULL);
277 		while (dosleep && t1 == t2) {
278 			usleep(100000);
279 			t2 = time(NULL);
280 		}
281 	}
282 }
283 
284 static
285 void
286 hammer_parsedevs(const char *blkdevs)
287 {
288 	char *copy;
289 	char *volname;
290 
291 	if (blkdevs == NULL) {
292 		errx(1, "A -f blkdevs specification is required "
293 			"for this command");
294 	}
295 
296 	copy = strdup(blkdevs);
297 	while ((volname = copy) != NULL) {
298 		if ((copy = strchr(copy, ':')) != NULL)
299 			*copy++ = 0;
300 		setup_volume(-1, volname, 0, O_RDONLY);
301 	}
302 }
303 
304 static
305 void
306 sigalrm(int signo __unused)
307 {
308 	/* do nothing (interrupts HAMMER ioctl) */
309 }
310 
311 static
312 void
313 usage(int exit_code)
314 {
315 	fprintf(stderr,
316 		"hammer -h\n"
317 		"hammer [-x] now[64]\n"
318 		"hammer stamp[64] <time>\n"
319 		"hammer [-s linkpath] prune <filesystem> [using <configfile>]\n"
320 		"hammer [-s linkpath] prune <filesystem> from <modulo_time> to "
321 				"<modulo_time> every <modulo_time>\n"
322 		"hammer prune <filesystem> everything\n"
323 		"hammer reblock <filesystem> [compact%%] (default 90%%)\n"
324 		"hammer history[@offset[,len]] <file-1>...<file-N>\n"
325 		"hammer -f blkdevs [-r] show\n"
326 		"hammer -f blkdevs blockmap\n"
327 	);
328 	fprintf(stderr, "time: +n[s/m/h/D/M/Y]\n"
329 			"time: yyyymmdd[:hhmmss]\n"
330 			"modulo_time: n{s,m,h,d,M,y}\n");
331 	exit(exit_code);
332 }
333 
334