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