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