2 * Copyright (c) 2003,2004 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sbin/jscan/jscan.c,v 1.5 2005/09/06 06:42:44 dillon Exp $
39 static void usage(const char *av0);
43 off_t record_size = 100 * 1024 * 1024;
45 static enum jdirection jdirection = JD_FORWARDS;
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);
55 main(int ac, char **av)
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;
65 int64_t mirror_transid;
66 int64_t output_transid;
67 int64_t record_transid;
77 while ((ch = getopt(ac, av, "2dm:o:s:uw:D:O:W:F")) != -1) {
80 jmodes |= JMODEF_INPUT_FULL;
83 trans_count = strtoll(optarg, &ptr, 0);
100 fprintf(stderr, "Bad suffix for value specified with -c, use 'k', 'm', 'g', 't', or nothing\n");
105 jmodes |= JMODEF_DEBUG;
108 jmodes |= JMODEF_MIRROR;
109 if (strcmp(optarg, "none") != 0)
110 mirror_transid_file = optarg;
113 jmodes |= JMODEF_OUTPUT_FULL;
116 jmodes |= JMODEF_OUTPUT;
117 if (strcmp(optarg, "none") != 0)
118 output_transid_file = optarg;
121 record_size = strtoll(optarg, &ptr, 0);
138 fprintf(stderr, "Bad suffix for value specified with -s, use 'k', 'm', 'g', 't', or nothing\n");
143 jdirection = JD_BACKWARDS;
146 jmodes |= JMODEF_RECORD_TMP;
149 jmodes |= JMODEF_RECORD;
150 record_prefix = optarg;
151 asprintf(&record_transid_file, "%s.transid", record_prefix);
154 mirror_directory = optarg;
160 fprintf(stderr, "unknown option: -%c\n", optopt);
168 if ((jmodes & JMODEF_COMMAND_MASK) == 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");
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");
184 * The input will either be a pipe, a regular file, or a journaling
189 input_prefix = "<stdin>";
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");
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);
207 input_prefix = av[optind];
208 jf = jopen_prefix(input_prefix, jdirection, 0);
209 jmodes |= JMODEF_INPUT_PREFIX;
212 fprintf(stderr, "Unable to open input %s: %s\n",
213 input_prefix, strerror(errno));
218 * STEP 1 - SYNCHRONIZING THE INPUT STREAM
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.
224 * Invalid transid's will be set to 0, but it should also be noted
225 * that 0 is also a valid transid.
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);
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)
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
251 * Then we take the input and start recording.
253 if (jmodes & JMODEF_RECORD) {
254 if (jrecord_init(record_prefix) < 0) {
255 fprintf(stderr, "Unable to initialize file set for: %s\n",
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 */
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 */
269 jscan_do_record(jf, record_prefix, record_transid);
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.
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);
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.
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
304 if ((jmodes & JMODEF_TRANSID_GOOD_MASK) && !(jmodes & JMODEF_INPUT_PIPE))
305 jseek(jf, transid, jdirection);
306 jmodes |= JMODEF_MEMORY_TRACKING;
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);
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)
330 if ((pid = fork()) == 0) {
331 jmodes &= ~JMODEF_DEBUG;
333 jf = jopen_prefix(input_prefix, jdirection, 0);
334 jmodes |= JMODEF_INPUT_PREFIX;
335 func(jf, info, transid);
338 } else if (pid < 0) {
339 fprintf(stderr, "fork(): %s\n", strerror(errno));
346 jscan_do_output(struct jfile *jf, const char *info, int64_t transid)
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);
363 jscan_do_mirror(struct jfile *jf, const char *info, int64_t transid)
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);
380 jscan_do_record(struct jfile *jf, const char *info, int64_t transid)
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);
396 usage(const char *av0)
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",