Merge from vendor branch OPENSSH:
[dragonfly.git] / sbin / jscan / jscan.c
1 /*
2  * Copyright (c) 2003,2004 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/jscan/jscan.c,v 1.5 2005/09/06 06:42:44 dillon Exp $
35  */
36
37 #include "jscan.h"
38
39 static void usage(const char *av0);
40
41 int jmodes;
42 int fsync_opt;
43 off_t record_size = 100 * 1024 * 1024;
44 off_t trans_count;
45 static enum jdirection jdirection = JD_FORWARDS;
46
47 static void jscan_do_output(struct jfile *, const char *, int64_t);
48 static void jscan_do_mirror(struct jfile *, const char *, int64_t);
49 static void jscan_do_record(struct jfile *, const char *, int64_t);
50 static void fork_subprocess(struct jfile *,
51                 void (*)(struct jfile *, const char *, int64_t),
52                 const char *, const char *, int64_t);
53
54 int
55 main(int ac, char **av)
56 {
57     const char *input_prefix = NULL;
58     char *output_transid_file = NULL;
59     char *mirror_transid_file = NULL;
60     const char *mirror_directory = ".";
61     char *record_prefix = NULL;
62     char *record_transid_file = NULL;
63     enum jdirection direction;
64     char *ptr;
65     int64_t mirror_transid;
66     int64_t output_transid;
67     int64_t record_transid;
68     int64_t transid;
69     int input_fd;
70     struct stat st;
71     struct jfile *jf;
72     struct jdata *jd;
73     int bytes;
74     int error;
75     int ch;
76
77     while ((ch = getopt(ac, av, "2dm:o:s:uw:D:O:W:F")) != -1) {
78         switch(ch) {
79         case '2':
80             jmodes |= JMODEF_INPUT_FULL;
81             break;
82         case 'c':
83             trans_count = strtoll(optarg, &ptr, 0);
84             switch(*ptr) {
85             case 't':
86                 record_size *= 1024;
87                 /* fall through */
88             case 'g':
89                 record_size *= 1024;
90                 /* fall through */
91             case 'm':
92                 record_size *= 1024;
93                 /* fall through */
94             case 'k':
95                 record_size *= 1024;
96                 break;
97             case 0:
98                 break;
99             default:
100                 fprintf(stderr, "Bad suffix for value specified with -c, use 'k', 'm', 'g', 't', or nothing\n");
101                 usage(av[0]);
102             }
103             break;
104         case 'd':
105             jmodes |= JMODEF_DEBUG;
106             break;
107         case 'm':
108             jmodes |= JMODEF_MIRROR;
109             if (strcmp(optarg, "none") != 0)
110                 mirror_transid_file = optarg;
111             break;
112         case 'O':
113             jmodes |= JMODEF_OUTPUT_FULL;
114             /* fall through */
115         case 'o':
116             jmodes |= JMODEF_OUTPUT;
117             if (strcmp(optarg, "none") != 0)
118                 output_transid_file = optarg;
119             break;
120         case 's':
121             record_size = strtoll(optarg, &ptr, 0);
122             switch(*ptr) {
123             case 't':
124                 record_size *= 1024;
125                 /* fall through */
126             case 'g':
127                 record_size *= 1024;
128                 /* fall through */
129             case 'm':
130                 record_size *= 1024;
131                 /* fall through */
132             case 'k':
133                 record_size *= 1024;
134                 break;
135             case 0:
136                 break;
137             default:
138                 fprintf(stderr, "Bad suffix for value specified with -s, use 'k', 'm', 'g', 't', or nothing\n");
139                 usage(av[0]);
140             }
141             break;
142         case 'u':
143             jdirection = JD_BACKWARDS;
144             break;
145         case 'W':
146             jmodes |= JMODEF_RECORD_TMP;
147             /* fall through */
148         case 'w':
149             jmodes |= JMODEF_RECORD;
150             record_prefix = optarg;
151             asprintf(&record_transid_file, "%s.transid", record_prefix);
152             break;
153         case 'D':
154             mirror_directory = optarg;
155             break;
156         case 'F':
157             ++fsync_opt;
158             break;
159         default:
160             fprintf(stderr, "unknown option: -%c\n", optopt);
161             usage(av[0]);
162         }
163     }
164
165     /*
166      * Sanity checks
167      */
168     if ((jmodes & JMODEF_COMMAND_MASK) == 0)
169         usage(av[0]);
170     if (optind > ac + 1)  {
171         fprintf(stderr, "Only one input file or prefix may be specified,\n"
172                         "or zero if stdin is to be the input.\n");
173         usage(av[0]);
174     }
175     if (jdirection == JD_BACKWARDS && (jmodes & (JMODEF_RECORD|JMODEF_OUTPUT))) {
176         fprintf(stderr, "Undo mode is only good in mirroring mode and "
177                         "cannot be mixed with other modes.\n");
178         exit(1);
179     }
180
181     /*
182      * STEP1 - OPEN INPUT
183      *
184      * The input will either be a pipe, a regular file, or a journaling 
185      * file prefix.
186      */
187     jf = NULL;
188     if (optind == ac) {
189         input_prefix = "<stdin>";
190         input_fd = 0;
191         if (fstat(0, &st) < 0 || !S_ISREG(st.st_mode)) {
192             jmodes |= JMODEF_INPUT_PIPE;
193             if (jdirection == JD_BACKWARDS) {
194                 fprintf(stderr, "Cannot scan journals on pipes backwards\n");
195                 usage(av[0]);
196             }
197         }
198         jf = jopen_fd(input_fd, jdirection);
199     } else if (stat(av[optind], &st) == 0 && S_ISREG(st.st_mode)) {
200         input_prefix = av[optind];
201         if ((input_fd = open(av[optind], O_RDONLY)) != NULL) {
202             jf = jopen_fd(input_fd, jdirection);
203         } else {
204             jf = NULL;
205         }
206     } else {
207         input_prefix = av[optind];
208         jf = jopen_prefix(input_prefix, jdirection, 0);
209         jmodes |= JMODEF_INPUT_PREFIX;
210     }
211     if (jf == NULL) {
212         fprintf(stderr, "Unable to open input %s: %s\n", 
213                 input_prefix, strerror(errno));
214         exit(1);
215     }
216
217     /*
218      * STEP 1 - SYNCHRONIZING THE INPUT STREAM
219      *
220      * Figure out the starting point for our various output modes.  Figure
221      * out the earliest transaction id and try to seek to that point,
222      * otherwise we might have to scan through terrabytes of data.
223      *
224      * Invalid transid's will be set to 0, but it should also be noted
225      * that 0 is also a valid transid.
226      */
227     get_transid_from_file(output_transid_file, &output_transid,
228                           JMODEF_OUTPUT_TRANSID_GOOD);
229     get_transid_from_file(mirror_transid_file, &mirror_transid, 
230                           JMODEF_MIRROR_TRANSID_GOOD);
231     get_transid_from_file(record_transid_file, &record_transid, 
232                           JMODEF_RECORD_TRANSID_GOOD);
233     transid = LLONG_MAX;
234     if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && output_transid < transid)
235         transid = output_transid;
236     if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && mirror_transid < transid)
237         transid = mirror_transid;
238     if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && record_transid < transid)
239         transid = record_transid;
240     if ((jmodes & JMODEF_TRANSID_GOOD_MASK) == 0)
241         transid = 0;
242
243     /*
244      * Now it gets more difficult.  If we are recording then the input
245      * could be representative of continuing data and not have any
246      * prior, older data that the output or mirror modes might need.  Those
247      * modes must work off the recording data even as we write to it.
248      * In that case we fork and have the sub-processes work off the
249      * record output.
250      *
251      * Then we take the input and start recording.
252      */
253     if (jmodes & JMODEF_RECORD) {
254         if (jrecord_init(record_prefix) < 0) {
255             fprintf(stderr, "Unable to initialize file set for: %s\n", 
256                     record_prefix);
257             exit(1);
258         }
259         if (jmodes & JMODEF_MIRROR) {
260             fork_subprocess(jf, jscan_do_mirror, record_prefix, 
261                             mirror_directory, mirror_transid);
262             /* XXX ack stream for temporary record file removal */
263         }
264         if (jmodes & JMODEF_OUTPUT) {
265             fork_subprocess(jf, jscan_do_output, record_prefix,
266                             NULL, output_transid);
267             /* XXX ack stream for temporary record file removal */
268         }
269         jscan_do_record(jf, record_prefix, record_transid);
270         exit(0);
271     }
272
273     /*
274      * If the input is a prefix set we can just pass it to the appropriate
275      * jscan_do_*() function.  If we are doing both output and mirroring
276      * we fork the mirror and do the output in the foreground since that
277      * is going to stdout.
278      */
279     if (jmodes & JMODEF_INPUT_PREFIX) {
280         if ((jmodes & JMODEF_OUTPUT) && (jmodes & JMODEF_MIRROR)) {
281             fork_subprocess(jf, jscan_do_mirror, input_prefix, 
282                             mirror_directory, mirror_transid);
283             jscan_do_output(jf, NULL, output_transid);
284         } else if (jmodes & JMODEF_OUTPUT) {
285             jscan_do_output(jf, NULL, output_transid);
286         } else if (jmodes & JMODEF_MIRROR) {
287             jscan_do_mirror(jf, mirror_directory, mirror_transid);
288         }
289         exit(0);
290     }
291
292     /*
293      * The input is not a prefix set and we are not recording, which means
294      * we have to transfer the data on the input pipe to the output and
295      * mirroring code on the fly.  This also means that we must keep track
296      * of meta-data records in-memory.  However, if the input is a regular
297      * file we *CAN* try to optimize where we start reading.
298      *
299      * NOTE: If the mirroring code encounters a transaction record that is
300      * not marked begin, and it does not have the begin record, it will
301      * attempt to locate the begin record if the input is not a pipe, then
302      * seek back.
303      */
304     if ((jmodes & JMODEF_TRANSID_GOOD_MASK) && !(jmodes & JMODEF_INPUT_PIPE))
305         jseek(jf, transid, jdirection);
306     jmodes |= JMODEF_MEMORY_TRACKING;
307
308     while ((error = jread(jf, &jd, jdirection)) == 0) {
309         if (jmodes & JMODEF_DEBUG)
310             dump_debug(jf, jd, transid);
311         if (jmodes & JMODEF_OUTPUT)
312             dump_output(jf, jd, output_transid);
313         if (jmodes & JMODEF_MIRROR)
314             dump_mirror(jf, jd, mirror_transid);
315         jfree(jf, jd);
316     }
317     jclose(jf);
318     exit(error ? 1 : 0);
319 }
320
321 static
322 void
323 fork_subprocess(struct jfile *jftoclose,
324                 void (*func)(struct jfile *, const char *, int64_t),
325                 const char *input_prefix, const char *info, int64_t transid)
326 {
327     pid_t pid;
328     struct jfile *jf;
329
330     if ((pid = fork()) == 0) {
331         jmodes &= ~JMODEF_DEBUG;
332         jclose(jftoclose);
333         jf = jopen_prefix(input_prefix, jdirection, 0);
334         jmodes |= JMODEF_INPUT_PREFIX;
335         func(jf, info, transid);
336         jclose(jf);
337         exit(0);
338     } else if (pid < 0) {
339         fprintf(stderr, "fork(): %s\n", strerror(errno));
340         exit(1);
341     }
342 }
343
344 static
345 void
346 jscan_do_output(struct jfile *jf, const char *info, int64_t transid)
347 {
348     struct jdata *jd;
349     int error;
350
351     if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
352         jseek(jf, transid, jdirection);
353     while ((error = jread(jf, &jd, jdirection)) == 0) {
354         if (jmodes & JMODEF_DEBUG)
355             dump_debug(jf, jd, transid);
356         dump_output(jf, jd, transid);
357         jfree(jf, jd);
358     }
359 }
360
361 static
362 void
363 jscan_do_mirror(struct jfile *jf, const char *info, int64_t transid)
364 {
365     struct jdata *jd;
366     int error;
367
368     if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
369         jseek(jf, transid, jdirection);
370     while ((error = jread(jf, &jd, jdirection)) == 0) {
371         if (jmodes & JMODEF_DEBUG)
372             dump_debug(jf, jd, transid);
373         dump_mirror(jf, jd, transid);
374         jfree(jf, jd);
375     }
376 }
377
378 static
379 void
380 jscan_do_record(struct jfile *jf, const char *info, int64_t transid)
381 {
382     struct jdata *jd;
383     int error;
384
385     if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
386         jseek(jf, transid, jdirection);
387     while ((error = jread(jf, &jd, jdirection)) == 0) {
388         if (jmodes & JMODEF_DEBUG)
389             dump_debug(jf, jd, transid);
390         dump_record(jf, jd, transid);
391         jfree(jf, jd);
392     }
393 }
394
395 static void
396 usage(const char *av0)
397 {
398     fprintf(stderr, 
399         "%s [-2duF] [-D dir] [-m mirror_transid_file/none]\n"
400         "\t[-o/O output_trnasid_file/none]\n"
401         "\t[-s size[kmgt]] -w/W record_prefix] [input_file/input_prefix]\n",
402         av0);
403     exit(1);
404 }
405