xref: /dflybsd-src/sbin/jscan/jscan.c (revision 86d7f5d305c6adaa56ff4582ece9859d73106103)
1*86d7f5d3SJohn Marino /*
2*86d7f5d3SJohn Marino  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
3*86d7f5d3SJohn Marino  *
4*86d7f5d3SJohn Marino  * This code is derived from software contributed to The DragonFly Project
5*86d7f5d3SJohn Marino  * by Matthew Dillon <dillon@backplane.com>
6*86d7f5d3SJohn Marino  *
7*86d7f5d3SJohn Marino  * Redistribution and use in source and binary forms, with or without
8*86d7f5d3SJohn Marino  * modification, are permitted provided that the following conditions
9*86d7f5d3SJohn Marino  * are met:
10*86d7f5d3SJohn Marino  *
11*86d7f5d3SJohn Marino  * 1. Redistributions of source code must retain the above copyright
12*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer.
13*86d7f5d3SJohn Marino  * 2. Redistributions in binary form must reproduce the above copyright
14*86d7f5d3SJohn Marino  *    notice, this list of conditions and the following disclaimer in
15*86d7f5d3SJohn Marino  *    the documentation and/or other materials provided with the
16*86d7f5d3SJohn Marino  *    distribution.
17*86d7f5d3SJohn Marino  * 3. Neither the name of The DragonFly Project nor the names of its
18*86d7f5d3SJohn Marino  *    contributors may be used to endorse or promote products derived
19*86d7f5d3SJohn Marino  *    from this software without specific, prior written permission.
20*86d7f5d3SJohn Marino  *
21*86d7f5d3SJohn Marino  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22*86d7f5d3SJohn Marino  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23*86d7f5d3SJohn Marino  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24*86d7f5d3SJohn Marino  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25*86d7f5d3SJohn Marino  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26*86d7f5d3SJohn Marino  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27*86d7f5d3SJohn Marino  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28*86d7f5d3SJohn Marino  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29*86d7f5d3SJohn Marino  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30*86d7f5d3SJohn Marino  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31*86d7f5d3SJohn Marino  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32*86d7f5d3SJohn Marino  * SUCH DAMAGE.
33*86d7f5d3SJohn Marino  *
34*86d7f5d3SJohn Marino  * $DragonFly: src/sbin/jscan/jscan.c,v 1.13 2008/06/05 18:06:30 swildner Exp $
35*86d7f5d3SJohn Marino  */
36*86d7f5d3SJohn Marino 
37*86d7f5d3SJohn Marino #include "jscan.h"
38*86d7f5d3SJohn Marino 
39*86d7f5d3SJohn Marino static int donecheck(enum jdirection direction, struct jdata *jd,
40*86d7f5d3SJohn Marino 		     int64_t transid);
41*86d7f5d3SJohn Marino static void usage(const char *av0);
42*86d7f5d3SJohn Marino 
43*86d7f5d3SJohn Marino int jmodes;
44*86d7f5d3SJohn Marino int fsync_opt;
45*86d7f5d3SJohn Marino int verbose_opt;
46*86d7f5d3SJohn Marino off_t prefix_file_size = 100 * 1024 * 1024;
47*86d7f5d3SJohn Marino off_t trans_count;
48*86d7f5d3SJohn Marino static enum jdirection jdirection = JD_FORWARDS;
49*86d7f5d3SJohn Marino 
50*86d7f5d3SJohn Marino static void jscan_do_output(struct jfile *, const char *,
51*86d7f5d3SJohn Marino 			    const char *, int64_t);
52*86d7f5d3SJohn Marino static void jscan_do_mirror(struct jfile *, const char *,
53*86d7f5d3SJohn Marino 			    const char *, int64_t);
54*86d7f5d3SJohn Marino static void jscan_do_record(struct jfile *, const char *,
55*86d7f5d3SJohn Marino 			    const char *, int64_t);
56*86d7f5d3SJohn Marino static void jscan_do_debug(struct jfile *, const char *,
57*86d7f5d3SJohn Marino 			    const char *, int64_t);
58*86d7f5d3SJohn Marino static void fork_subprocess(struct jfile *,
59*86d7f5d3SJohn Marino 			    void (*)(struct jfile *, const char *,
60*86d7f5d3SJohn Marino 				     const char *, int64_t),
61*86d7f5d3SJohn Marino 			    const char *,
62*86d7f5d3SJohn Marino 			    const char *, const char *, int64_t);
63*86d7f5d3SJohn Marino 
64*86d7f5d3SJohn Marino int
main(int ac,char ** av)65*86d7f5d3SJohn Marino main(int ac, char **av)
66*86d7f5d3SJohn Marino {
67*86d7f5d3SJohn Marino     const char *input_prefix = NULL;
68*86d7f5d3SJohn Marino     char *output_transid_file = NULL;
69*86d7f5d3SJohn Marino     char *mirror_transid_file = NULL;
70*86d7f5d3SJohn Marino     const char *mirror_directory = ".";
71*86d7f5d3SJohn Marino     char *record_prefix = NULL;
72*86d7f5d3SJohn Marino     char *record_transid_file = NULL;
73*86d7f5d3SJohn Marino     struct jsession jsdebug;
74*86d7f5d3SJohn Marino     struct jsession jsoutput;
75*86d7f5d3SJohn Marino     struct jsession jsmirror;
76*86d7f5d3SJohn Marino     char *ptr;
77*86d7f5d3SJohn Marino     int64_t mirror_transid;
78*86d7f5d3SJohn Marino     int64_t output_transid;
79*86d7f5d3SJohn Marino     int64_t record_transid;
80*86d7f5d3SJohn Marino     int64_t transid;
81*86d7f5d3SJohn Marino     int input_fd;
82*86d7f5d3SJohn Marino     struct stat st;
83*86d7f5d3SJohn Marino     struct jfile *jf;
84*86d7f5d3SJohn Marino     struct jdata *jd;
85*86d7f5d3SJohn Marino     int ch;
86*86d7f5d3SJohn Marino 
87*86d7f5d3SJohn Marino     while ((ch = getopt(ac, av, "2c:dfm:o:s:uvw:D:O:W:F")) != -1) {
88*86d7f5d3SJohn Marino 	switch(ch) {
89*86d7f5d3SJohn Marino 	case '2':
90*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_INPUT_FULL;
91*86d7f5d3SJohn Marino 	    break;
92*86d7f5d3SJohn Marino 	case 'c':
93*86d7f5d3SJohn Marino 	    trans_count = strtoll(optarg, &ptr, 0);
94*86d7f5d3SJohn Marino 	    switch(*ptr) {
95*86d7f5d3SJohn Marino 	    case 't':
96*86d7f5d3SJohn Marino 		trans_count *= 1024;
97*86d7f5d3SJohn Marino 		/* fall through */
98*86d7f5d3SJohn Marino 	    case 'g':
99*86d7f5d3SJohn Marino 		trans_count *= 1024;
100*86d7f5d3SJohn Marino 		/* fall through */
101*86d7f5d3SJohn Marino 	    case 'm':
102*86d7f5d3SJohn Marino 		trans_count *= 1024;
103*86d7f5d3SJohn Marino 		/* fall through */
104*86d7f5d3SJohn Marino 	    case 'k':
105*86d7f5d3SJohn Marino 		trans_count *= 1024;
106*86d7f5d3SJohn Marino 		break;
107*86d7f5d3SJohn Marino 	    case 0:
108*86d7f5d3SJohn Marino 		break;
109*86d7f5d3SJohn Marino 	    default:
110*86d7f5d3SJohn Marino 		fprintf(stderr, "Bad suffix for value specified with -c, use 'k', 'm', 'g', 't', or nothing\n");
111*86d7f5d3SJohn Marino 		usage(av[0]);
112*86d7f5d3SJohn Marino 	    }
113*86d7f5d3SJohn Marino 	    break;
114*86d7f5d3SJohn Marino 	case 'd':
115*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_DEBUG;
116*86d7f5d3SJohn Marino 	    break;
117*86d7f5d3SJohn Marino 	case 'f':
118*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_LOOP_FOREVER;
119*86d7f5d3SJohn Marino 	    break;
120*86d7f5d3SJohn Marino 	case 'v':
121*86d7f5d3SJohn Marino 	    ++verbose_opt;
122*86d7f5d3SJohn Marino 	    break;
123*86d7f5d3SJohn Marino 	case 'm':
124*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_MIRROR;
125*86d7f5d3SJohn Marino 	    if (strcmp(optarg, "none") != 0)
126*86d7f5d3SJohn Marino 		mirror_transid_file = optarg;
127*86d7f5d3SJohn Marino 	    break;
128*86d7f5d3SJohn Marino 	case 'O':
129*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_OUTPUT_FULL;
130*86d7f5d3SJohn Marino 	    /* fall through */
131*86d7f5d3SJohn Marino 	case 'o':
132*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_OUTPUT;
133*86d7f5d3SJohn Marino 	    if (strcmp(optarg, "none") != 0)
134*86d7f5d3SJohn Marino 		output_transid_file = optarg;
135*86d7f5d3SJohn Marino 	    break;
136*86d7f5d3SJohn Marino 	case 's':
137*86d7f5d3SJohn Marino 	    prefix_file_size = strtoll(optarg, &ptr, 0);
138*86d7f5d3SJohn Marino 	    switch(*ptr) {
139*86d7f5d3SJohn Marino 	    case 't':
140*86d7f5d3SJohn Marino 		prefix_file_size *= 1024;
141*86d7f5d3SJohn Marino 		/* fall through */
142*86d7f5d3SJohn Marino 	    case 'g':
143*86d7f5d3SJohn Marino 		prefix_file_size *= 1024;
144*86d7f5d3SJohn Marino 		/* fall through */
145*86d7f5d3SJohn Marino 	    case 'm':
146*86d7f5d3SJohn Marino 		prefix_file_size *= 1024;
147*86d7f5d3SJohn Marino 		/* fall through */
148*86d7f5d3SJohn Marino 	    case 'k':
149*86d7f5d3SJohn Marino 		prefix_file_size *= 1024;
150*86d7f5d3SJohn Marino 		break;
151*86d7f5d3SJohn Marino 	    case 0:
152*86d7f5d3SJohn Marino 		break;
153*86d7f5d3SJohn Marino 	    default:
154*86d7f5d3SJohn Marino 		fprintf(stderr, "Bad suffix for value specified with -s, use 'k', 'm', 'g', 't', or nothing\n");
155*86d7f5d3SJohn Marino 		usage(av[0]);
156*86d7f5d3SJohn Marino 	    }
157*86d7f5d3SJohn Marino 	    break;
158*86d7f5d3SJohn Marino 	case 'u':
159*86d7f5d3SJohn Marino 	    jdirection = JD_BACKWARDS;
160*86d7f5d3SJohn Marino 	    break;
161*86d7f5d3SJohn Marino 	case 'W':
162*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_RECORD_TMP;
163*86d7f5d3SJohn Marino 	    /* fall through */
164*86d7f5d3SJohn Marino 	case 'w':
165*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_RECORD;
166*86d7f5d3SJohn Marino 	    record_prefix = optarg;
167*86d7f5d3SJohn Marino 	    asprintf(&record_transid_file, "%s.transid", record_prefix);
168*86d7f5d3SJohn Marino 	    break;
169*86d7f5d3SJohn Marino 	case 'D':
170*86d7f5d3SJohn Marino 	    mirror_directory = optarg;
171*86d7f5d3SJohn Marino 	    break;
172*86d7f5d3SJohn Marino 	case 'F':
173*86d7f5d3SJohn Marino 	    ++fsync_opt;
174*86d7f5d3SJohn Marino 	    break;
175*86d7f5d3SJohn Marino 	default:
176*86d7f5d3SJohn Marino 	    fprintf(stderr, "unknown option: -%c\n", optopt);
177*86d7f5d3SJohn Marino 	    usage(av[0]);
178*86d7f5d3SJohn Marino 	}
179*86d7f5d3SJohn Marino     }
180*86d7f5d3SJohn Marino 
181*86d7f5d3SJohn Marino     /*
182*86d7f5d3SJohn Marino      * Sanity checks
183*86d7f5d3SJohn Marino      */
184*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_COMMAND_MASK) == 0)
185*86d7f5d3SJohn Marino 	usage(av[0]);
186*86d7f5d3SJohn Marino     if (optind > ac + 1)  {
187*86d7f5d3SJohn Marino 	fprintf(stderr, "Only one input file or prefix may be specified,\n"
188*86d7f5d3SJohn Marino 			"or zero if stdin is to be the input.\n");
189*86d7f5d3SJohn Marino 	usage(av[0]);
190*86d7f5d3SJohn Marino     }
191*86d7f5d3SJohn Marino     if (strcmp(mirror_directory, ".") != 0) {
192*86d7f5d3SJohn Marino 	struct stat sb;
193*86d7f5d3SJohn Marino 	if (stat(mirror_directory, &sb) != 0) {
194*86d7f5d3SJohn Marino 	    perror ("Could not stat mirror directory");
195*86d7f5d3SJohn Marino 	    usage(av[0]);
196*86d7f5d3SJohn Marino 	}
197*86d7f5d3SJohn Marino 	if (!S_ISDIR(sb.st_mode))
198*86d7f5d3SJohn Marino 	{
199*86d7f5d3SJohn Marino 	    fprintf (stderr, "Mirror directory '%s' is not a directory\n", mirror_directory);
200*86d7f5d3SJohn Marino 	    usage(av[0]);
201*86d7f5d3SJohn Marino 	}
202*86d7f5d3SJohn Marino     }
203*86d7f5d3SJohn Marino     if (jdirection == JD_BACKWARDS && (jmodes & (JMODEF_RECORD|JMODEF_OUTPUT))) {
204*86d7f5d3SJohn Marino 	fprintf(stderr, "Undo mode is only good in mirroring mode and "
205*86d7f5d3SJohn Marino 			"cannot be mixed with other modes.\n");
206*86d7f5d3SJohn Marino 	exit(1);
207*86d7f5d3SJohn Marino     }
208*86d7f5d3SJohn Marino 
209*86d7f5d3SJohn Marino     /*
210*86d7f5d3SJohn Marino      * STEP1 - OPEN INPUT
211*86d7f5d3SJohn Marino      *
212*86d7f5d3SJohn Marino      * The input will either be a pipe, a regular file, or a journaling
213*86d7f5d3SJohn Marino      * file prefix.
214*86d7f5d3SJohn Marino      */
215*86d7f5d3SJohn Marino     jf = NULL;
216*86d7f5d3SJohn Marino     if (optind == ac) {
217*86d7f5d3SJohn Marino 	input_prefix = "<stdin>";
218*86d7f5d3SJohn Marino 	input_fd = 0;
219*86d7f5d3SJohn Marino 	if (fstat(0, &st) < 0 || !S_ISREG(st.st_mode)) {
220*86d7f5d3SJohn Marino 	    jmodes |= JMODEF_INPUT_PIPE;
221*86d7f5d3SJohn Marino 	    if (jdirection == JD_BACKWARDS) {
222*86d7f5d3SJohn Marino 		fprintf(stderr, "Cannot scan journals on pipes backwards\n");
223*86d7f5d3SJohn Marino 		usage(av[0]);
224*86d7f5d3SJohn Marino 	    }
225*86d7f5d3SJohn Marino 	}
226*86d7f5d3SJohn Marino 	jf = jopen_fd(input_fd);
227*86d7f5d3SJohn Marino     } else if (stat(av[optind], &st) == 0 && S_ISREG(st.st_mode)) {
228*86d7f5d3SJohn Marino 	input_prefix = av[optind];
229*86d7f5d3SJohn Marino 	if ((input_fd = open(av[optind], O_RDONLY)) != 0) {
230*86d7f5d3SJohn Marino 	    jf = jopen_fd(input_fd);
231*86d7f5d3SJohn Marino 	} else {
232*86d7f5d3SJohn Marino 	    jf = NULL;
233*86d7f5d3SJohn Marino 	}
234*86d7f5d3SJohn Marino     } else {
235*86d7f5d3SJohn Marino 	input_prefix = av[optind];
236*86d7f5d3SJohn Marino 	jf = jopen_prefix(input_prefix, 0);
237*86d7f5d3SJohn Marino 	jmodes |= JMODEF_INPUT_PREFIX;
238*86d7f5d3SJohn Marino     }
239*86d7f5d3SJohn Marino     if (jf == NULL) {
240*86d7f5d3SJohn Marino 	fprintf(stderr, "Unable to open input %s: %s\n",
241*86d7f5d3SJohn Marino 		input_prefix, strerror(errno));
242*86d7f5d3SJohn Marino 	exit(1);
243*86d7f5d3SJohn Marino     }
244*86d7f5d3SJohn Marino 
245*86d7f5d3SJohn Marino     /*
246*86d7f5d3SJohn Marino      * STEP 1 - SYNCHRONIZING THE INPUT STREAM
247*86d7f5d3SJohn Marino      *
248*86d7f5d3SJohn Marino      * Figure out the starting point for our various output modes.  Figure
249*86d7f5d3SJohn Marino      * out the earliest transaction id and try to seek to that point,
250*86d7f5d3SJohn Marino      * otherwise we might have to scan through terrabytes of data.
251*86d7f5d3SJohn Marino      *
252*86d7f5d3SJohn Marino      * Invalid transid's will be set to 0, but it should also be noted
253*86d7f5d3SJohn Marino      * that 0 is also a valid transid.
254*86d7f5d3SJohn Marino      */
255*86d7f5d3SJohn Marino     get_transid_from_file(output_transid_file, &output_transid,
256*86d7f5d3SJohn Marino 			  JMODEF_OUTPUT_TRANSID_GOOD);
257*86d7f5d3SJohn Marino     get_transid_from_file(mirror_transid_file, &mirror_transid,
258*86d7f5d3SJohn Marino 			  JMODEF_MIRROR_TRANSID_GOOD);
259*86d7f5d3SJohn Marino     get_transid_from_file(record_transid_file, &record_transid,
260*86d7f5d3SJohn Marino 			  JMODEF_RECORD_TRANSID_GOOD);
261*86d7f5d3SJohn Marino     transid = LLONG_MAX;
262*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && output_transid < transid)
263*86d7f5d3SJohn Marino 	transid = output_transid;
264*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && mirror_transid < transid)
265*86d7f5d3SJohn Marino 	transid = mirror_transid;
266*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && record_transid < transid)
267*86d7f5d3SJohn Marino 	transid = record_transid;
268*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_TRANSID_GOOD_MASK) == 0)
269*86d7f5d3SJohn Marino 	transid = 0;
270*86d7f5d3SJohn Marino     if (verbose_opt) {
271*86d7f5d3SJohn Marino 	if (jmodes & JMODEF_OUTPUT) {
272*86d7f5d3SJohn Marino 	    fprintf(stderr, "Starting transid for OUTPUT: %016jx\n",
273*86d7f5d3SJohn Marino 		    (uintmax_t)output_transid);
274*86d7f5d3SJohn Marino 	}
275*86d7f5d3SJohn Marino 	if (jmodes & JMODEF_MIRROR) {
276*86d7f5d3SJohn Marino 	    fprintf(stderr, "Starting transid for MIRROR: %016jx\n",
277*86d7f5d3SJohn Marino 		    (uintmax_t)mirror_transid);
278*86d7f5d3SJohn Marino 	}
279*86d7f5d3SJohn Marino 	if (jmodes & JMODEF_RECORD) {
280*86d7f5d3SJohn Marino 	    fprintf(stderr, "Starting transid for RECORD: %016jx\n",
281*86d7f5d3SJohn Marino 		    (uintmax_t)record_transid);
282*86d7f5d3SJohn Marino 	}
283*86d7f5d3SJohn Marino     }
284*86d7f5d3SJohn Marino 
285*86d7f5d3SJohn Marino     if (strcmp(mirror_directory, ".") != 0) {
286*86d7f5d3SJohn Marino 	if (chdir (mirror_directory) != 0) {
287*86d7f5d3SJohn Marino 	    perror ("Could not enter mirror directory");
288*86d7f5d3SJohn Marino 	    exit (1);
289*86d7f5d3SJohn Marino 	}
290*86d7f5d3SJohn Marino     }
291*86d7f5d3SJohn Marino 
292*86d7f5d3SJohn Marino     /*
293*86d7f5d3SJohn Marino      * Now it gets more difficult.  If we are recording then the input
294*86d7f5d3SJohn Marino      * could be representative of continuing data and not have any
295*86d7f5d3SJohn Marino      * prior, older data that the output or mirror modes might need.  Those
296*86d7f5d3SJohn Marino      * modes must work off the recording data even as we write to it.
297*86d7f5d3SJohn Marino      * In that case we fork and have the sub-processes work off the
298*86d7f5d3SJohn Marino      * record output.
299*86d7f5d3SJohn Marino      *
300*86d7f5d3SJohn Marino      * Then we take the input and start recording.
301*86d7f5d3SJohn Marino      */
302*86d7f5d3SJohn Marino     if (jmodes & JMODEF_RECORD) {
303*86d7f5d3SJohn Marino 	if (jrecord_init(record_prefix) < 0) {
304*86d7f5d3SJohn Marino 	    fprintf(stderr, "Unable to initialize file set for: %s\n",
305*86d7f5d3SJohn Marino 		    record_prefix);
306*86d7f5d3SJohn Marino 	    exit(1);
307*86d7f5d3SJohn Marino 	}
308*86d7f5d3SJohn Marino 	if (jmodes & JMODEF_MIRROR) {
309*86d7f5d3SJohn Marino 	    fork_subprocess(jf, jscan_do_mirror, record_prefix,
310*86d7f5d3SJohn Marino 			    mirror_transid_file,
311*86d7f5d3SJohn Marino 			    mirror_directory, mirror_transid);
312*86d7f5d3SJohn Marino 	    /* XXX ack stream for temporary record file removal */
313*86d7f5d3SJohn Marino 	}
314*86d7f5d3SJohn Marino 	if (jmodes & JMODEF_OUTPUT) {
315*86d7f5d3SJohn Marino 	    fork_subprocess(jf, jscan_do_output, record_prefix,
316*86d7f5d3SJohn Marino 			    record_transid_file,
317*86d7f5d3SJohn Marino 			    NULL, output_transid);
318*86d7f5d3SJohn Marino 	    /* XXX ack stream for temporary record file removal */
319*86d7f5d3SJohn Marino 	}
320*86d7f5d3SJohn Marino 	jscan_do_record(jf, record_transid_file, record_prefix, record_transid);
321*86d7f5d3SJohn Marino 	exit(0);
322*86d7f5d3SJohn Marino     }
323*86d7f5d3SJohn Marino 
324*86d7f5d3SJohn Marino     /*
325*86d7f5d3SJohn Marino      * If the input is a prefix set we can just pass it to the appropriate
326*86d7f5d3SJohn Marino      * jscan_do_*() function.  If we are doing both output and mirroring
327*86d7f5d3SJohn Marino      * we fork the mirror and do the output in the foreground since that
328*86d7f5d3SJohn Marino      * is going to stdout.
329*86d7f5d3SJohn Marino      */
330*86d7f5d3SJohn Marino     if (jmodes & JMODEF_INPUT_PREFIX) {
331*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_OUTPUT) && (jmodes & JMODEF_MIRROR)) {
332*86d7f5d3SJohn Marino 	    fork_subprocess(jf, jscan_do_mirror, input_prefix,
333*86d7f5d3SJohn Marino 			    mirror_transid_file,
334*86d7f5d3SJohn Marino 			    mirror_directory, mirror_transid);
335*86d7f5d3SJohn Marino 	    jscan_do_output(jf, output_transid_file, NULL, output_transid);
336*86d7f5d3SJohn Marino 	} else if (jmodes & JMODEF_OUTPUT) {
337*86d7f5d3SJohn Marino 	    jscan_do_output(jf, output_transid_file, NULL, output_transid);
338*86d7f5d3SJohn Marino 	} else if (jmodes & JMODEF_MIRROR) {
339*86d7f5d3SJohn Marino 	    jscan_do_mirror(jf, mirror_transid_file, mirror_directory,
340*86d7f5d3SJohn Marino 			    mirror_transid);
341*86d7f5d3SJohn Marino 	} else if (jmodes & JMODEF_DEBUG) {
342*86d7f5d3SJohn Marino 	    jscan_do_debug(jf, NULL, NULL, 0);
343*86d7f5d3SJohn Marino 	}
344*86d7f5d3SJohn Marino 	exit(0);
345*86d7f5d3SJohn Marino     }
346*86d7f5d3SJohn Marino 
347*86d7f5d3SJohn Marino     /*
348*86d7f5d3SJohn Marino      * The input is not a prefix set and we are not recording, which means
349*86d7f5d3SJohn Marino      * we have to transfer the data on the input pipe to the output and
350*86d7f5d3SJohn Marino      * mirroring code on the fly.  This also means that we must keep track
351*86d7f5d3SJohn Marino      * of meta-data records in-memory.  However, if the input is a regular
352*86d7f5d3SJohn Marino      * file we *CAN* try to optimize where we start reading.
353*86d7f5d3SJohn Marino      *
354*86d7f5d3SJohn Marino      * NOTE: If the mirroring code encounters a transaction record that is
355*86d7f5d3SJohn Marino      * not marked begin, and it does not have the begin record, it will
356*86d7f5d3SJohn Marino      * attempt to locate the begin record if the input is not a pipe, then
357*86d7f5d3SJohn Marino      * seek back.
358*86d7f5d3SJohn Marino      */
359*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_TRANSID_GOOD_MASK) && !(jmodes & JMODEF_INPUT_PIPE))
360*86d7f5d3SJohn Marino 	jd = jseek(jf, transid, jdirection);
361*86d7f5d3SJohn Marino     else
362*86d7f5d3SJohn Marino 	jd = jread(jf, NULL, jdirection);
363*86d7f5d3SJohn Marino     jmodes |= JMODEF_MEMORY_TRACKING;
364*86d7f5d3SJohn Marino 
365*86d7f5d3SJohn Marino     jsession_init(&jsdebug, jf, jdirection,
366*86d7f5d3SJohn Marino 		  NULL, 0);
367*86d7f5d3SJohn Marino     jsession_init(&jsoutput, jf, jdirection,
368*86d7f5d3SJohn Marino 		  output_transid_file, output_transid);
369*86d7f5d3SJohn Marino     jsession_init(&jsmirror, jf, jdirection,
370*86d7f5d3SJohn Marino 		  mirror_transid_file, mirror_transid);
371*86d7f5d3SJohn Marino     jsmirror.ss_mirror_directory = mirror_directory;
372*86d7f5d3SJohn Marino 
373*86d7f5d3SJohn Marino     while (jd != NULL) {
374*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
375*86d7f5d3SJohn Marino 	    dump_debug(&jsdebug, jd);
376*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_OUTPUT) && jsession_check(&jsoutput, jd))
377*86d7f5d3SJohn Marino 	    dump_output(&jsoutput, jd);
378*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_MIRROR) && jsession_check(&jsmirror, jd))
379*86d7f5d3SJohn Marino 	    dump_mirror(&jsmirror, jd);
380*86d7f5d3SJohn Marino 	if (donecheck(jdirection, jd, transid)) {
381*86d7f5d3SJohn Marino 	    jfree(jf, jd);
382*86d7f5d3SJohn Marino 	    break;
383*86d7f5d3SJohn Marino 	}
384*86d7f5d3SJohn Marino 	jd = jread(jf, jd, jdirection);
385*86d7f5d3SJohn Marino     }
386*86d7f5d3SJohn Marino     jclose(jf);
387*86d7f5d3SJohn Marino     jsession_term(&jsdebug);
388*86d7f5d3SJohn Marino     jsession_term(&jsoutput);
389*86d7f5d3SJohn Marino     jsession_term(&jsmirror);
390*86d7f5d3SJohn Marino     return(0);
391*86d7f5d3SJohn Marino }
392*86d7f5d3SJohn Marino 
393*86d7f5d3SJohn Marino /*
394*86d7f5d3SJohn Marino  * Returns one if we need to break out of our scanning loop, zero otherwise.
395*86d7f5d3SJohn Marino  */
396*86d7f5d3SJohn Marino static int
donecheck(enum jdirection direction,struct jdata * jd,int64_t transid)397*86d7f5d3SJohn Marino donecheck(enum jdirection direction, struct jdata *jd, int64_t transid)
398*86d7f5d3SJohn Marino {
399*86d7f5d3SJohn Marino     if (direction == JD_FORWARDS) {
400*86d7f5d3SJohn Marino 	if (jd->jd_transid > transid && trans_count && --trans_count == 0)
401*86d7f5d3SJohn Marino 	    return(1);
402*86d7f5d3SJohn Marino     } else {
403*86d7f5d3SJohn Marino 	if (jd->jd_transid <= transid && trans_count && --trans_count == 0)
404*86d7f5d3SJohn Marino 	    return(1);
405*86d7f5d3SJohn Marino     }
406*86d7f5d3SJohn Marino     return(0);
407*86d7f5d3SJohn Marino }
408*86d7f5d3SJohn Marino 
409*86d7f5d3SJohn Marino /*
410*86d7f5d3SJohn Marino  * When we have multiple commands and are writing to a prefix set, we can
411*86d7f5d3SJohn Marino  * 'background' the output and/or mirroring command and have the background
412*86d7f5d3SJohn Marino  * processes feed off the prefix set the foreground process is writing to.
413*86d7f5d3SJohn Marino  */
414*86d7f5d3SJohn Marino static void
fork_subprocess(struct jfile * jftoclose,void (* func)(struct jfile *,const char *,const char *,int64_t),const char * input_prefix,const char * transid_file,const char * info,int64_t transid)415*86d7f5d3SJohn Marino fork_subprocess(struct jfile *jftoclose,
416*86d7f5d3SJohn Marino 	void (*func)(struct jfile *, const char *, const char *, int64_t),
417*86d7f5d3SJohn Marino 	const char *input_prefix, const char *transid_file, const char *info,
418*86d7f5d3SJohn Marino 	int64_t transid)
419*86d7f5d3SJohn Marino {
420*86d7f5d3SJohn Marino     pid_t pid;
421*86d7f5d3SJohn Marino     struct jfile *jf;
422*86d7f5d3SJohn Marino 
423*86d7f5d3SJohn Marino     if ((pid = fork()) == 0) {
424*86d7f5d3SJohn Marino 	jmodes &= ~(JMODEF_DEBUG | JMODEF_INPUT_PIPE);
425*86d7f5d3SJohn Marino 	jmodes |= JMODEF_LOOP_FOREVER;	/* keep checking for new input */
426*86d7f5d3SJohn Marino 	jclose(jftoclose);
427*86d7f5d3SJohn Marino 	jf = jopen_prefix(input_prefix, 0);
428*86d7f5d3SJohn Marino 	jmodes |= JMODEF_INPUT_PREFIX;
429*86d7f5d3SJohn Marino 	func(jf, transid_file, info, transid);
430*86d7f5d3SJohn Marino 	jclose(jf);
431*86d7f5d3SJohn Marino 	exit(0);
432*86d7f5d3SJohn Marino     } else if (pid < 0) {
433*86d7f5d3SJohn Marino 	fprintf(stderr, "fork(): %s\n", strerror(errno));
434*86d7f5d3SJohn Marino 	exit(1);
435*86d7f5d3SJohn Marino     }
436*86d7f5d3SJohn Marino }
437*86d7f5d3SJohn Marino 
438*86d7f5d3SJohn Marino static void
jscan_do_output(struct jfile * jf,const char * output_transid_file,const char * dummy __unused,int64_t transid)439*86d7f5d3SJohn Marino jscan_do_output(struct jfile *jf, const char *output_transid_file, const char *dummy __unused, int64_t transid)
440*86d7f5d3SJohn Marino {
441*86d7f5d3SJohn Marino     struct jdata *jd;
442*86d7f5d3SJohn Marino     struct jsession jsdebug;
443*86d7f5d3SJohn Marino     struct jsession jsoutput;
444*86d7f5d3SJohn Marino 
445*86d7f5d3SJohn Marino     jsession_init(&jsdebug, jf, jdirection,
446*86d7f5d3SJohn Marino 		  NULL, 0);
447*86d7f5d3SJohn Marino     jsession_init(&jsoutput, jf, jdirection,
448*86d7f5d3SJohn Marino 		  output_transid_file, transid);
449*86d7f5d3SJohn Marino 
450*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
451*86d7f5d3SJohn Marino 	jd = jseek(jf, transid, jdirection);
452*86d7f5d3SJohn Marino     else
453*86d7f5d3SJohn Marino 	jd = jread(jf, NULL, jdirection);
454*86d7f5d3SJohn Marino     while (jd != NULL) {
455*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
456*86d7f5d3SJohn Marino 	    dump_debug(&jsdebug, jd);
457*86d7f5d3SJohn Marino 	if (jsession_check(&jsoutput, jd))
458*86d7f5d3SJohn Marino 	    dump_output(&jsoutput, jd);
459*86d7f5d3SJohn Marino 	if (donecheck(jdirection, jd, transid)) {
460*86d7f5d3SJohn Marino 	    jfree(jf, jd);
461*86d7f5d3SJohn Marino 	    break;
462*86d7f5d3SJohn Marino 	}
463*86d7f5d3SJohn Marino 	jd = jread(jf, jd, jdirection);
464*86d7f5d3SJohn Marino     }
465*86d7f5d3SJohn Marino     jsession_term(&jsdebug);
466*86d7f5d3SJohn Marino     jsession_term(&jsoutput);
467*86d7f5d3SJohn Marino }
468*86d7f5d3SJohn Marino 
469*86d7f5d3SJohn Marino static void
jscan_do_mirror(struct jfile * jf,const char * mirror_transid_file,const char * mirror_directory,int64_t transid)470*86d7f5d3SJohn Marino jscan_do_mirror(struct jfile *jf, const char *mirror_transid_file, const char *mirror_directory, int64_t transid)
471*86d7f5d3SJohn Marino {
472*86d7f5d3SJohn Marino     struct jsession jsdebug;
473*86d7f5d3SJohn Marino     struct jsession jsmirror;
474*86d7f5d3SJohn Marino     struct jdata *jd;
475*86d7f5d3SJohn Marino 
476*86d7f5d3SJohn Marino     jsession_init(&jsdebug, jf, jdirection,
477*86d7f5d3SJohn Marino 		  NULL, 0);
478*86d7f5d3SJohn Marino     jsession_init(&jsmirror, jf, jdirection,
479*86d7f5d3SJohn Marino 		  mirror_transid_file, transid);
480*86d7f5d3SJohn Marino     jsmirror.ss_mirror_directory = mirror_directory;
481*86d7f5d3SJohn Marino 
482*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
483*86d7f5d3SJohn Marino 	jd = jseek(jf, transid, jdirection);
484*86d7f5d3SJohn Marino     else
485*86d7f5d3SJohn Marino 	jd = jread(jf, NULL, jdirection);
486*86d7f5d3SJohn Marino     while (jd != NULL) {
487*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
488*86d7f5d3SJohn Marino 	    dump_debug(&jsdebug, jd);
489*86d7f5d3SJohn Marino 	if (jsession_check(&jsmirror, jd))
490*86d7f5d3SJohn Marino 	    dump_mirror(&jsmirror, jd);
491*86d7f5d3SJohn Marino 	if (donecheck(jdirection, jd, transid)) {
492*86d7f5d3SJohn Marino 	    jfree(jf, jd);
493*86d7f5d3SJohn Marino 	    break;
494*86d7f5d3SJohn Marino 	}
495*86d7f5d3SJohn Marino 	jd = jread(jf, jd, jdirection);
496*86d7f5d3SJohn Marino     }
497*86d7f5d3SJohn Marino     jsession_term(&jsdebug);
498*86d7f5d3SJohn Marino     jsession_term(&jsmirror);
499*86d7f5d3SJohn Marino }
500*86d7f5d3SJohn Marino 
501*86d7f5d3SJohn Marino static void
jscan_do_record(struct jfile * jfin,const char * record_transid_file,const char * prefix,int64_t transid)502*86d7f5d3SJohn Marino jscan_do_record(struct jfile *jfin, const char *record_transid_file, const char *prefix, int64_t transid)
503*86d7f5d3SJohn Marino {
504*86d7f5d3SJohn Marino     struct jsession jsdebug;
505*86d7f5d3SJohn Marino     struct jsession jsrecord;
506*86d7f5d3SJohn Marino     struct jdata *jd;
507*86d7f5d3SJohn Marino 
508*86d7f5d3SJohn Marino     jsession_init(&jsdebug, jfin, jdirection,
509*86d7f5d3SJohn Marino 		  NULL, 0);
510*86d7f5d3SJohn Marino     jsession_init(&jsrecord, jfin, jdirection,
511*86d7f5d3SJohn Marino 		  record_transid_file, transid);
512*86d7f5d3SJohn Marino 
513*86d7f5d3SJohn Marino     assert(jdirection == JD_FORWARDS);
514*86d7f5d3SJohn Marino     jsrecord.ss_jfout = jopen_prefix(prefix, 1);
515*86d7f5d3SJohn Marino     if (jsrecord.ss_jfout == NULL) {
516*86d7f5d3SJohn Marino 	fprintf(stderr, "Unable to open prefix set for writing: %s\n", prefix);
517*86d7f5d3SJohn Marino 	exit(1);
518*86d7f5d3SJohn Marino     }
519*86d7f5d3SJohn Marino     if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
520*86d7f5d3SJohn Marino 	jd = jseek(jfin, transid, jdirection);
521*86d7f5d3SJohn Marino     else
522*86d7f5d3SJohn Marino 	jd = jread(jfin, NULL, jdirection);
523*86d7f5d3SJohn Marino     while (jd != NULL) {
524*86d7f5d3SJohn Marino 	if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
525*86d7f5d3SJohn Marino 	    dump_debug(&jsdebug, jd);
526*86d7f5d3SJohn Marino 	if (jsession_check(&jsrecord, jd))
527*86d7f5d3SJohn Marino 	    dump_record(&jsrecord, jd);
528*86d7f5d3SJohn Marino 	if (donecheck(jdirection, jd, transid)) {
529*86d7f5d3SJohn Marino 	    jfree(jfin, jd);
530*86d7f5d3SJohn Marino 	    break;
531*86d7f5d3SJohn Marino 	}
532*86d7f5d3SJohn Marino 	jd = jread(jfin, jd, jdirection);
533*86d7f5d3SJohn Marino     }
534*86d7f5d3SJohn Marino     jclose(jsrecord.ss_jfout);
535*86d7f5d3SJohn Marino     jsrecord.ss_jfout = NULL;
536*86d7f5d3SJohn Marino     jsession_term(&jsdebug);
537*86d7f5d3SJohn Marino     jsession_term(&jsrecord);
538*86d7f5d3SJohn Marino }
539*86d7f5d3SJohn Marino 
540*86d7f5d3SJohn Marino static void
jscan_do_debug(struct jfile * jf,const char * dummy1 __unused,const char * dummy __unused,int64_t transid __unused)541*86d7f5d3SJohn Marino jscan_do_debug(struct jfile *jf, const char *dummy1 __unused,
542*86d7f5d3SJohn Marino 	       const char *dummy __unused, int64_t transid __unused)
543*86d7f5d3SJohn Marino {
544*86d7f5d3SJohn Marino     struct jsession jsdebug;
545*86d7f5d3SJohn Marino     struct jdata *jd;
546*86d7f5d3SJohn Marino 
547*86d7f5d3SJohn Marino     jsession_init(&jsdebug, jf, jdirection,
548*86d7f5d3SJohn Marino 		  NULL, 0);
549*86d7f5d3SJohn Marino     jd = NULL;
550*86d7f5d3SJohn Marino     while ((jd = jread(jf, jd, jdirection)) != NULL) {
551*86d7f5d3SJohn Marino 	if (jsession_check(&jsdebug, jd))
552*86d7f5d3SJohn Marino 	    dump_debug(&jsdebug, jd);
553*86d7f5d3SJohn Marino 	if (donecheck(jdirection, jd, transid)) {
554*86d7f5d3SJohn Marino 	    jfree(jf, jd);
555*86d7f5d3SJohn Marino 	    break;
556*86d7f5d3SJohn Marino 	}
557*86d7f5d3SJohn Marino     }
558*86d7f5d3SJohn Marino     jsession_term(&jsdebug);
559*86d7f5d3SJohn Marino }
560*86d7f5d3SJohn Marino 
561*86d7f5d3SJohn Marino static void
usage(const char * av0)562*86d7f5d3SJohn Marino usage(const char *av0)
563*86d7f5d3SJohn Marino {
564*86d7f5d3SJohn Marino     fprintf(stderr,
565*86d7f5d3SJohn Marino 	"%s [-2dfuvF] [-D dir] [-m mirror_transid_file/none]\n"
566*86d7f5d3SJohn Marino 	"\t[-o/O output_transid_file/none]\n"
567*86d7f5d3SJohn Marino 	"\t[-s size[kmgt]] -w/W record_prefix] [input_file/input_prefix]\n",
568*86d7f5d3SJohn Marino 	av0);
569*86d7f5d3SJohn Marino     exit(1);
570*86d7f5d3SJohn Marino }
571*86d7f5d3SJohn Marino 
572