xref: /dflybsd-src/sbin/hammer/cmd_mirror.c (revision 764d0c8c7a2e13ccf8e3819cee3ee1db4c81acd6)
1a7fbbf91SMatthew Dillon /*
2a7fbbf91SMatthew Dillon  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3a7fbbf91SMatthew Dillon  *
4a7fbbf91SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5a7fbbf91SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
6a7fbbf91SMatthew Dillon  *
7a7fbbf91SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
8a7fbbf91SMatthew Dillon  * modification, are permitted provided that the following conditions
9a7fbbf91SMatthew Dillon  * are met:
10a7fbbf91SMatthew Dillon  *
11a7fbbf91SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
12a7fbbf91SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
13a7fbbf91SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
14a7fbbf91SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
15a7fbbf91SMatthew Dillon  *    the documentation and/or other materials provided with the
16a7fbbf91SMatthew Dillon  *    distribution.
17a7fbbf91SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
18a7fbbf91SMatthew Dillon  *    contributors may be used to endorse or promote products derived
19a7fbbf91SMatthew Dillon  *    from this software without specific, prior written permission.
20a7fbbf91SMatthew Dillon  *
21a7fbbf91SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22a7fbbf91SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23a7fbbf91SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24a7fbbf91SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25a7fbbf91SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26a7fbbf91SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27a7fbbf91SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28a7fbbf91SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29a7fbbf91SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30a7fbbf91SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31a7fbbf91SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32a7fbbf91SMatthew Dillon  * SUCH DAMAGE.
33a7fbbf91SMatthew Dillon  */
34a7fbbf91SMatthew Dillon 
35a7fbbf91SMatthew Dillon #include "hammer.h"
36a7fbbf91SMatthew Dillon 
37269cdd19SMatthew Dillon #define LINE1	0,20
38269cdd19SMatthew Dillon #define LINE2	20,78
39269cdd19SMatthew Dillon #define LINE3	90,70
40269cdd19SMatthew Dillon 
41a7fbbf91SMatthew Dillon #define SERIALBUF_SIZE	(512 * 1024)
42a7fbbf91SMatthew Dillon 
43ced4470dSMatthew Dillon typedef struct histogram {
44ced4470dSMatthew Dillon 	hammer_tid_t	tid;
45ced4470dSMatthew Dillon 	u_int64_t	bytes;
46ced4470dSMatthew Dillon } *histogram_t;
47ced4470dSMatthew Dillon 
48a7fbbf91SMatthew Dillon static int read_mrecords(int fd, char *buf, u_int size,
4917dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
50e7f926a5SMatthew Dillon static int generate_histogram(int fd, const char *filesystem,
51ced4470dSMatthew Dillon 			 histogram_t *histogram_ary,
5239e88285SMatthew Dillon 			 struct hammer_ioc_mirror_rw *mirror_base,
5339e88285SMatthew Dillon 			 int *repeatp);
5417dd83bcSMatthew Dillon static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
5517dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_head_t pickup);
5617dd83bcSMatthew Dillon static void write_mrecord(int fdout, u_int32_t type,
5717dd83bcSMatthew Dillon 			 hammer_ioc_mrecord_any_t mrec, int bytes);
58e7f926a5SMatthew Dillon static void generate_mrec_header(int fd, int pfs_id,
59e7f926a5SMatthew Dillon 			 union hammer_ioc_mrecord_any *mrec_tmp);
6048eadef9SMatthew Dillon static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
6148eadef9SMatthew Dillon 			 struct hammer_ioc_mrecord_head *pickup,
6234ebae70SMatthew Dillon 			 hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
63d4e5b69bSMatthew Dillon static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
6448eadef9SMatthew Dillon static ssize_t writebw(int fd, const void *buf, size_t nbytes,
6548eadef9SMatthew Dillon 			u_int64_t *bwcount, struct timeval *tv1);
6601a72c9fSMatthew Dillon static int getyn(void);
67a7fbbf91SMatthew Dillon static void mirror_usage(int code);
68a7fbbf91SMatthew Dillon 
6917dd83bcSMatthew Dillon /*
7017dd83bcSMatthew Dillon  * Generate a mirroring data stream from the specific source over the
7117dd83bcSMatthew Dillon  * entire key range, but restricted to the specified transaction range.
7217dd83bcSMatthew Dillon  *
7317dd83bcSMatthew Dillon  * The HAMMER VFS does most of the work, we add a few new mrecord
7417dd83bcSMatthew Dillon  * types to negotiate the TID ranges and verify that the entire
7517dd83bcSMatthew Dillon  * stream made it to the destination.
7639e88285SMatthew Dillon  *
7739e88285SMatthew Dillon  * streaming will be 0 for mirror-read, 1 for mirror-stream.  The code will
7839e88285SMatthew Dillon  * set up a fake value of -1 when running the histogram for mirror-read.
7917dd83bcSMatthew Dillon  */
80a7fbbf91SMatthew Dillon void
8148eadef9SMatthew Dillon hammer_cmd_mirror_read(char **av, int ac, int streaming)
82a7fbbf91SMatthew Dillon {
83a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
84d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
8517dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
8648eadef9SMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
8717dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
88243ca327SMatthew Dillon 	hammer_tid_t sync_tid;
89ced4470dSMatthew Dillon 	histogram_t histogram_ary;
908b640662SAntonio Huete Jimenez 	char *filesystem;
91a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
92243ca327SMatthew Dillon 	int interrupted = 0;
93243ca327SMatthew Dillon 	int error;
94a7fbbf91SMatthew Dillon 	int fd;
95243ca327SMatthew Dillon 	int n;
9648eadef9SMatthew Dillon 	int didwork;
97e7f926a5SMatthew Dillon 	int histogram;
98ced4470dSMatthew Dillon 	int histindex;
99ced4470dSMatthew Dillon 	int histmax;
10039e88285SMatthew Dillon 	int repeat = 0;
10139e88285SMatthew Dillon 	int sameline;
10248eadef9SMatthew Dillon 	int64_t total_bytes;
103243ca327SMatthew Dillon 	time_t base_t = time(NULL);
10448eadef9SMatthew Dillon 	struct timeval bwtv;
10548eadef9SMatthew Dillon 	u_int64_t bwcount;
106ced4470dSMatthew Dillon 	u_int64_t estbytes;
107a7fbbf91SMatthew Dillon 
10834bb69d8SThomas Nikolajsen 	if (ac == 0 || ac > 2)
109a7fbbf91SMatthew Dillon 		mirror_usage(1);
110a7fbbf91SMatthew Dillon 	filesystem = av[0];
11169f5a58cSMatthew Dillon 	hammer_check_restrict(filesystem);
112a7fbbf91SMatthew Dillon 
11348eadef9SMatthew Dillon 	pickup.signature = 0;
11448eadef9SMatthew Dillon 	pickup.type = 0;
115ced4470dSMatthew Dillon 	histogram = 0;
116ced4470dSMatthew Dillon 	histindex = 0;
117ced4470dSMatthew Dillon 	histmax = 0;
118e7f926a5SMatthew Dillon 	histogram_ary = NULL;
11939e88285SMatthew Dillon 	sameline = 0;
12048eadef9SMatthew Dillon 
12148eadef9SMatthew Dillon again:
122a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
123a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
124a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
125a7fbbf91SMatthew Dillon 
126d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
127a7fbbf91SMatthew Dillon 
128269cdd19SMatthew Dillon 	if (streaming >= 0)
129269cdd19SMatthew Dillon 		score_printf(LINE1, "Running");
130269cdd19SMatthew Dillon 
13139e88285SMatthew Dillon 	if (streaming >= 0 && VerboseOpt && VerboseOpt < 2) {
13239e88285SMatthew Dillon 		fprintf(stderr, "%cRunning  \b\b", (sameline ? '\r' : '\n'));
13348eadef9SMatthew Dillon 		fflush(stderr);
13439e88285SMatthew Dillon 		sameline = 1;
13548eadef9SMatthew Dillon 	}
13639e88285SMatthew Dillon 	sameline = 1;
13748eadef9SMatthew Dillon 	total_bytes = 0;
13848eadef9SMatthew Dillon 	gettimeofday(&bwtv, NULL);
13948eadef9SMatthew Dillon 	bwcount = 0;
14048eadef9SMatthew Dillon 
141243ca327SMatthew Dillon 	/*
142e7f926a5SMatthew Dillon 	 * Send initial header for the purpose of determining the
143e7f926a5SMatthew Dillon 	 * shared-uuid.
14401a72c9fSMatthew Dillon 	 */
145e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
146e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
147e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
14801a72c9fSMatthew Dillon 
14901a72c9fSMatthew Dillon 	/*
150d4e5b69bSMatthew Dillon 	 * In 2-way mode the target will send us a PFS info packet
151d4e5b69bSMatthew Dillon 	 * first.  Use the target's current snapshot TID as our default
152d4e5b69bSMatthew Dillon 	 * begin TID.
153243ca327SMatthew Dillon 	 */
15448eadef9SMatthew Dillon 	if (TwoWayPipeOpt) {
15539e88285SMatthew Dillon 		mirror.tid_beg = 0;
15648eadef9SMatthew Dillon 		n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup,
157d4e5b69bSMatthew Dillon 					 NULL, &mirror.tid_beg);
15848eadef9SMatthew Dillon 		if (n < 0) {	/* got TERM record */
15948eadef9SMatthew Dillon 			relpfs(fd, &pfs);
160*764d0c8cSTomohiro Kusumi 			free(buf);
161*764d0c8cSTomohiro Kusumi 			free(histogram_ary);
16248eadef9SMatthew Dillon 			return;
16348eadef9SMatthew Dillon 		}
16448eadef9SMatthew Dillon 		++mirror.tid_beg;
16539e88285SMatthew Dillon 	} else if (streaming && histogram) {
16639e88285SMatthew Dillon 		mirror.tid_beg = histogram_ary[histindex].tid + 1;
16739e88285SMatthew Dillon 	} else {
16839e88285SMatthew Dillon 		mirror.tid_beg = 0;
16948eadef9SMatthew Dillon 	}
170d4e5b69bSMatthew Dillon 
171d4e5b69bSMatthew Dillon 	/*
172d4e5b69bSMatthew Dillon 	 * Write out the PFS header, tid_beg will be updated if our PFS
173d4e5b69bSMatthew Dillon 	 * has a larger begin sync.  tid_end is set to the latest source
174d4e5b69bSMatthew Dillon 	 * TID whos flush cycle has completed.
175d4e5b69bSMatthew Dillon 	 */
176e7f926a5SMatthew Dillon 	generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
177e7f926a5SMatthew Dillon 	if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
178e7f926a5SMatthew Dillon 		mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
179e7f926a5SMatthew Dillon 	mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
180e7f926a5SMatthew Dillon 	mirror.ubuf = buf;
181e7f926a5SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
182e7f926a5SMatthew Dillon 	mirror.pfs_id = pfs.pfs_id;
183e7f926a5SMatthew Dillon 	mirror.shared_uuid = pfs.ondisk->shared_uuid;
184d4e5b69bSMatthew Dillon 
185d4e5b69bSMatthew Dillon 	/*
186e7f926a5SMatthew Dillon 	 * XXX If the histogram is exhausted and the TID delta is large
187e7f926a5SMatthew Dillon 	 *     the stream might have been offline for a while and is
188e7f926a5SMatthew Dillon 	 *     now picking it up again.  Do another histogram.
189d4e5b69bSMatthew Dillon 	 */
190e7f926a5SMatthew Dillon #if 0
19139e88285SMatthew Dillon 	if (streaming && histogram && histindex == histend) {
192e7f926a5SMatthew Dillon 		if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM)
193ced4470dSMatthew Dillon 			histogram = 0;
194e7f926a5SMatthew Dillon 	}
195e7f926a5SMatthew Dillon #endif
19634ebae70SMatthew Dillon 
197e7f926a5SMatthew Dillon 	/*
198e7f926a5SMatthew Dillon 	 * Initial bulk startup control, try to do some incremental
199e7f926a5SMatthew Dillon 	 * mirroring in order to allow the stream to be killed and
200e7f926a5SMatthew Dillon 	 * restarted without having to start over.
201e7f926a5SMatthew Dillon 	 */
202ced4470dSMatthew Dillon 	if (histogram == 0 && BulkOpt == 0) {
20339e88285SMatthew Dillon 		if (VerboseOpt && repeat == 0) {
204e7f926a5SMatthew Dillon 			fprintf(stderr, "\n");
20539e88285SMatthew Dillon 			sameline = 0;
20639e88285SMatthew Dillon 		}
207ced4470dSMatthew Dillon 		histmax = generate_histogram(fd, filesystem,
20839e88285SMatthew Dillon 					     &histogram_ary, &mirror,
20939e88285SMatthew Dillon 					     &repeat);
210ced4470dSMatthew Dillon 		histindex = 0;
211ced4470dSMatthew Dillon 		histogram = 1;
21239e88285SMatthew Dillon 
21339e88285SMatthew Dillon 		/*
21439e88285SMatthew Dillon 		 * Just stream the histogram, then stop
21539e88285SMatthew Dillon 		 */
21639e88285SMatthew Dillon 		if (streaming == 0)
21739e88285SMatthew Dillon 			streaming = -1;
218e7f926a5SMatthew Dillon 	}
219e7f926a5SMatthew Dillon 
22039e88285SMatthew Dillon 	if (streaming && histogram) {
221ced4470dSMatthew Dillon 		++histindex;
222ced4470dSMatthew Dillon 		mirror.tid_end = histogram_ary[histindex].tid;
223ced4470dSMatthew Dillon 		estbytes = histogram_ary[histindex-1].bytes;
224e7f926a5SMatthew Dillon 		mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end;
225ced4470dSMatthew Dillon 	} else {
226ced4470dSMatthew Dillon 		estbytes = 0;
227e7f926a5SMatthew Dillon 	}
228e7f926a5SMatthew Dillon 
229e7f926a5SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
230e7f926a5SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.pfs));
231e7f926a5SMatthew Dillon 
232e7f926a5SMatthew Dillon 	/*
233e7f926a5SMatthew Dillon 	 * A cycle file overrides the beginning TID only if we are
23439e88285SMatthew Dillon 	 * not operating in two-way or histogram mode.
235e7f926a5SMatthew Dillon 	 */
23639e88285SMatthew Dillon 	if (TwoWayPipeOpt == 0 && histogram == 0) {
237e7f926a5SMatthew Dillon 		hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
238e7f926a5SMatthew Dillon 	}
239e7f926a5SMatthew Dillon 
240e7f926a5SMatthew Dillon 	/*
241e7f926a5SMatthew Dillon 	 * An additional argument overrides the beginning TID regardless
242e7f926a5SMatthew Dillon 	 * of what mode we are in.  This is not recommending if operating
243e7f926a5SMatthew Dillon 	 * in two-way mode.
244e7f926a5SMatthew Dillon 	 */
24517dd83bcSMatthew Dillon 	if (ac == 2)
24617dd83bcSMatthew Dillon 		mirror.tid_beg = strtoull(av[1], NULL, 0);
24717dd83bcSMatthew Dillon 
24848eadef9SMatthew Dillon 	if (streaming == 0 || VerboseOpt >= 2) {
24948eadef9SMatthew Dillon 		fprintf(stderr,
250ced4470dSMatthew Dillon 			"Mirror-read: Mirror %016jx to %016jx",
251a276dc6bSMatthew Dillon 			(uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end);
252ced4470dSMatthew Dillon 		if (histogram)
253ced4470dSMatthew Dillon 			fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes);
2543d7b2393SMatthew Dillon 		fprintf(stderr, "\n");
2553d7b2393SMatthew Dillon 		fflush(stderr);
25648eadef9SMatthew Dillon 	}
257243ca327SMatthew Dillon 	if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
258a276dc6bSMatthew Dillon 		fprintf(stderr, "Mirror-read: Resuming at object %016jx\n",
259a276dc6bSMatthew Dillon 			(uintmax_t)mirror.key_beg.obj_id);
260243ca327SMatthew Dillon 	}
261243ca327SMatthew Dillon 
262243ca327SMatthew Dillon 	/*
2639dc76cb1SMatthew Dillon 	 * Nothing to do if begin equals end.
2649dc76cb1SMatthew Dillon 	 */
26548eadef9SMatthew Dillon 	if (mirror.tid_beg >= mirror.tid_end) {
26648eadef9SMatthew Dillon 		if (streaming == 0 || VerboseOpt >= 2)
26748eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-read: No work to do\n");
26839e88285SMatthew Dillon 		sleep(DelayOpt);
26948eadef9SMatthew Dillon 		didwork = 0;
27039e88285SMatthew Dillon 		histogram = 0;
2719dc76cb1SMatthew Dillon 		goto done;
2729dc76cb1SMatthew Dillon 	}
27348eadef9SMatthew Dillon 	didwork = 1;
2749dc76cb1SMatthew Dillon 
2759dc76cb1SMatthew Dillon 	/*
276243ca327SMatthew Dillon 	 * Write out bulk records
277243ca327SMatthew Dillon 	 */
278a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
279a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
280a7fbbf91SMatthew Dillon 
281a7fbbf91SMatthew Dillon 	do {
282a7fbbf91SMatthew Dillon 		mirror.count = 0;
283d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
284d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
285a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
286269cdd19SMatthew Dillon 			score_printf(LINE3, "Mirror-read %s failed: %s",
287269cdd19SMatthew Dillon 				     filesystem, strerror(errno));
288a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
289a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
290a7fbbf91SMatthew Dillon 			exit(1);
291a7fbbf91SMatthew Dillon 		}
2929c67b4d2SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
293269cdd19SMatthew Dillon 			score_printf(LINE3, "Mirror-read %s fatal error %d",
294269cdd19SMatthew Dillon 				     filesystem, mirror.head.error);
2959c67b4d2SMatthew Dillon 			fprintf(stderr,
2969c67b4d2SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
2979c67b4d2SMatthew Dillon 				filesystem, mirror.head.error);
2989c67b4d2SMatthew Dillon 			exit(1);
2999c67b4d2SMatthew Dillon 		}
300243ca327SMatthew Dillon 		if (mirror.count) {
30148eadef9SMatthew Dillon 			if (BandwidthOpt) {
30248eadef9SMatthew Dillon 				n = writebw(1, mirror.ubuf, mirror.count,
30348eadef9SMatthew Dillon 					    &bwcount, &bwtv);
30448eadef9SMatthew Dillon 			} else {
305243ca327SMatthew Dillon 				n = write(1, mirror.ubuf, mirror.count);
30648eadef9SMatthew Dillon 			}
307243ca327SMatthew Dillon 			if (n != mirror.count) {
308269cdd19SMatthew Dillon 				score_printf(LINE3,
309269cdd19SMatthew Dillon 					     "Mirror-read %s failed: "
310269cdd19SMatthew Dillon 					     "short write",
311269cdd19SMatthew Dillon 					     filesystem);
312269cdd19SMatthew Dillon 				fprintf(stderr,
313269cdd19SMatthew Dillon 					"Mirror-read %s failed: "
314243ca327SMatthew Dillon 					"short write\n",
315243ca327SMatthew Dillon 				filesystem);
316243ca327SMatthew Dillon 				exit(1);
317243ca327SMatthew Dillon 			}
318a7fbbf91SMatthew Dillon 		}
31948eadef9SMatthew Dillon 		total_bytes += mirror.count;
32048eadef9SMatthew Dillon 		if (streaming && VerboseOpt) {
321e7f926a5SMatthew Dillon 			fprintf(stderr,
32239e88285SMatthew Dillon 				"\rscan obj=%016jx tids=%016jx:%016jx %11jd",
323a276dc6bSMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id,
324a276dc6bSMatthew Dillon 				(uintmax_t)mirror.tid_beg,
325a276dc6bSMatthew Dillon 				(uintmax_t)mirror.tid_end,
326a276dc6bSMatthew Dillon 				(intmax_t)total_bytes);
32748eadef9SMatthew Dillon 			fflush(stderr);
32839e88285SMatthew Dillon 			sameline = 0;
329269cdd19SMatthew Dillon 		} else if (streaming) {
330269cdd19SMatthew Dillon 			score_printf(LINE2,
331269cdd19SMatthew Dillon 				"obj=%016jx tids=%016jx:%016jx %11jd",
332269cdd19SMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id,
333269cdd19SMatthew Dillon 				(uintmax_t)mirror.tid_beg,
334269cdd19SMatthew Dillon 				(uintmax_t)mirror.tid_end,
335269cdd19SMatthew Dillon 				(intmax_t)total_bytes);
33648eadef9SMatthew Dillon 		}
337a7fbbf91SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
338e7f926a5SMatthew Dillon 
339e7f926a5SMatthew Dillon 		/*
340e7f926a5SMatthew Dillon 		 * Deal with time limit option
341e7f926a5SMatthew Dillon 		 */
342243ca327SMatthew Dillon 		if (TimeoutOpt &&
343243ca327SMatthew Dillon 		    (unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
344269cdd19SMatthew Dillon 			score_printf(LINE3,
345269cdd19SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
346269cdd19SMatthew Dillon 				" %016jx",
347269cdd19SMatthew Dillon 				filesystem,
348269cdd19SMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id);
349243ca327SMatthew Dillon 			fprintf(stderr,
350243ca327SMatthew Dillon 				"Mirror-read %s interrupted by timer at"
351a276dc6bSMatthew Dillon 				" %016jx\n",
352243ca327SMatthew Dillon 				filesystem,
353a276dc6bSMatthew Dillon 				(uintmax_t)mirror.key_cur.obj_id);
354243ca327SMatthew Dillon 			interrupted = 1;
355243ca327SMatthew Dillon 			break;
356243ca327SMatthew Dillon 		}
357a7fbbf91SMatthew Dillon 	} while (mirror.count != 0);
358a7fbbf91SMatthew Dillon 
35948eadef9SMatthew Dillon done:
36039e88285SMatthew Dillon 	if (streaming && VerboseOpt && sameline == 0) {
361ced4470dSMatthew Dillon 		fprintf(stderr, "\n");
362ced4470dSMatthew Dillon 		fflush(stderr);
36339e88285SMatthew Dillon 		sameline = 1;
364ced4470dSMatthew Dillon 	}
365ced4470dSMatthew Dillon 
366243ca327SMatthew Dillon 	/*
36748eadef9SMatthew Dillon 	 * Write out the termination sync record - only if not interrupted
368243ca327SMatthew Dillon 	 */
36948eadef9SMatthew Dillon 	if (interrupted == 0) {
37048eadef9SMatthew Dillon 		if (didwork) {
37117dd83bcSMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
37217dd83bcSMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
37348eadef9SMatthew Dillon 		} else {
37448eadef9SMatthew Dillon 			write_mrecord(1, HAMMER_MREC_TYPE_IDLE,
37548eadef9SMatthew Dillon 				      &mrec_tmp, sizeof(mrec_tmp.sync));
37648eadef9SMatthew Dillon 		}
37748eadef9SMatthew Dillon 	}
37834ebae70SMatthew Dillon 
379243ca327SMatthew Dillon 	/*
380243ca327SMatthew Dillon 	 * If the -2 option was given (automatic when doing mirror-copy),
381243ca327SMatthew Dillon 	 * a two-way pipe is assumed and we expect a response mrec from
382243ca327SMatthew Dillon 	 * the target.
383243ca327SMatthew Dillon 	 */
384243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
38548eadef9SMatthew Dillon 		mrec = read_mrecord(0, &error, &pickup);
38617dd83bcSMatthew Dillon 		if (mrec == NULL ||
38717dd83bcSMatthew Dillon 		    mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
38817dd83bcSMatthew Dillon 		    mrec->head.rec_size != sizeof(mrec->update)) {
389243ca327SMatthew Dillon 			fprintf(stderr, "mirror_read: Did not get final "
390243ca327SMatthew Dillon 					"acknowledgement packet from target\n");
391243ca327SMatthew Dillon 			exit(1);
392243ca327SMatthew Dillon 		}
393243ca327SMatthew Dillon 		if (interrupted) {
394243ca327SMatthew Dillon 			if (CyclePath) {
395ced4470dSMatthew Dillon 				hammer_set_cycle(&mirror.key_cur,
396ced4470dSMatthew Dillon 						 mirror.tid_beg);
39734bb69d8SThomas Nikolajsen 				fprintf(stderr, "Cyclefile %s updated for "
39834bb69d8SThomas Nikolajsen 					"continuation\n", CyclePath);
399243ca327SMatthew Dillon 			}
400243ca327SMatthew Dillon 		} else {
40117dd83bcSMatthew Dillon 			sync_tid = mrec->update.tid;
402243ca327SMatthew Dillon 			if (CyclePath) {
403243ca327SMatthew Dillon 				hammer_key_beg_init(&mirror.key_beg);
404243ca327SMatthew Dillon 				hammer_set_cycle(&mirror.key_beg, sync_tid);
405a276dc6bSMatthew Dillon 				fprintf(stderr,
406a276dc6bSMatthew Dillon 					"Cyclefile %s updated to 0x%016jx\n",
407a276dc6bSMatthew Dillon 					CyclePath, (uintmax_t)sync_tid);
408243ca327SMatthew Dillon 			}
409243ca327SMatthew Dillon 		}
410cc71ff00STomohiro Kusumi 		free(mrec);
411243ca327SMatthew Dillon 	} else if (CyclePath) {
412243ca327SMatthew Dillon 		/* NOTE! mirror.tid_beg cannot be updated */
413243ca327SMatthew Dillon 		fprintf(stderr, "Warning: cycle file (-c option) cannot be "
414243ca327SMatthew Dillon 				"fully updated unless you use mirror-copy\n");
415243ca327SMatthew Dillon 		hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
416243ca327SMatthew Dillon 	}
41748eadef9SMatthew Dillon 	if (streaming && interrupted == 0) {
41848eadef9SMatthew Dillon 		time_t t1 = time(NULL);
41948eadef9SMatthew Dillon 		time_t t2;
42048eadef9SMatthew Dillon 
421e7f926a5SMatthew Dillon 		/*
42239e88285SMatthew Dillon 		 * Try to break down large bulk transfers into smaller ones
42339e88285SMatthew Dillon 		 * so it can sync the transaction id on the slave.  This
42439e88285SMatthew Dillon 		 * way if we get interrupted a restart doesn't have to
42539e88285SMatthew Dillon 		 * start from scratch.
426e7f926a5SMatthew Dillon 		 */
42739e88285SMatthew Dillon 		if (streaming && histogram) {
428ced4470dSMatthew Dillon 			if (histindex != histmax) {
42939e88285SMatthew Dillon 				if (VerboseOpt && VerboseOpt < 2 &&
43039e88285SMatthew Dillon 				    streaming >= 0) {
431e7f926a5SMatthew Dillon 					fprintf(stderr, " (bulk incremental)");
43239e88285SMatthew Dillon 				}
43339e88285SMatthew Dillon 				relpfs(fd, &pfs);
434e7f926a5SMatthew Dillon 				goto again;
435e7f926a5SMatthew Dillon 			}
436ced4470dSMatthew Dillon 		}
437e7f926a5SMatthew Dillon 
43839e88285SMatthew Dillon 		if (VerboseOpt && streaming >= 0) {
43948eadef9SMatthew Dillon 			fprintf(stderr, " W");
44048eadef9SMatthew Dillon 			fflush(stderr);
441269cdd19SMatthew Dillon 		} else if (streaming >= 0) {
442269cdd19SMatthew Dillon 			score_printf(LINE1, "Waiting");
44348eadef9SMatthew Dillon 		}
44448eadef9SMatthew Dillon 		pfs.ondisk->sync_end_tid = mirror.tid_end;
44539e88285SMatthew Dillon 		if (streaming < 0) {
44639e88285SMatthew Dillon 			/*
44739e88285SMatthew Dillon 			 * Fake streaming mode when using a histogram to
44839e88285SMatthew Dillon 			 * break up a mirror-read, do not wait on source.
44939e88285SMatthew Dillon 			 */
45039e88285SMatthew Dillon 			streaming = 0;
45139e88285SMatthew Dillon 		} else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) {
452269cdd19SMatthew Dillon 			score_printf(LINE3,
453269cdd19SMatthew Dillon 				     "Mirror-read %s: cannot stream: %s\n",
454269cdd19SMatthew Dillon 				     filesystem, strerror(errno));
455269cdd19SMatthew Dillon 			fprintf(stderr,
456269cdd19SMatthew Dillon 				"Mirror-read %s: cannot stream: %s\n",
45748eadef9SMatthew Dillon 				filesystem, strerror(errno));
45848eadef9SMatthew Dillon 		} else {
45948eadef9SMatthew Dillon 			t2 = time(NULL) - t1;
46048eadef9SMatthew Dillon 			if (t2 >= 0 && t2 < DelayOpt) {
46148eadef9SMatthew Dillon 				if (VerboseOpt) {
46248eadef9SMatthew Dillon 					fprintf(stderr, "\bD");
46348eadef9SMatthew Dillon 					fflush(stderr);
46448eadef9SMatthew Dillon 				}
46548eadef9SMatthew Dillon 				sleep(DelayOpt - t2);
46648eadef9SMatthew Dillon 			}
46748eadef9SMatthew Dillon 			if (VerboseOpt) {
46848eadef9SMatthew Dillon 				fprintf(stderr, "\b ");
46948eadef9SMatthew Dillon 				fflush(stderr);
47048eadef9SMatthew Dillon 			}
47148eadef9SMatthew Dillon 			relpfs(fd, &pfs);
47248eadef9SMatthew Dillon 			goto again;
47348eadef9SMatthew Dillon 		}
47448eadef9SMatthew Dillon 	}
47548eadef9SMatthew Dillon 	write_mrecord(1, HAMMER_MREC_TYPE_TERM,
47648eadef9SMatthew Dillon 		      &mrec_tmp, sizeof(mrec_tmp.sync));
47748eadef9SMatthew Dillon 	relpfs(fd, &pfs);
478*764d0c8cSTomohiro Kusumi 	free(buf);
479*764d0c8cSTomohiro Kusumi 	free(histogram_ary);
480a7fbbf91SMatthew Dillon 	fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
481a7fbbf91SMatthew Dillon }
482a7fbbf91SMatthew Dillon 
483e7f926a5SMatthew Dillon /*
4843d7b2393SMatthew Dillon  * What we are trying to do here is figure out how much data is
4853d7b2393SMatthew Dillon  * going to be sent for the TID range and to break the TID range
4863d7b2393SMatthew Dillon  * down into reasonably-sized slices (from the point of view of
4873d7b2393SMatthew Dillon  * data sent) so a lost connection can restart at a reasonable
4883d7b2393SMatthew Dillon  * place and not all the way back at the beginning.
489ced4470dSMatthew Dillon  *
490ced4470dSMatthew Dillon  * An entry's TID serves as the end_tid for the prior entry
491ced4470dSMatthew Dillon  * So we have to offset the calculation by 1 so that TID falls into
492ced4470dSMatthew Dillon  * the previous entry when populating entries.
493ced4470dSMatthew Dillon  *
494ced4470dSMatthew Dillon  * Because the transaction id space is bursty we need a relatively
495ced4470dSMatthew Dillon  * large number of buckets (like a million) to do a reasonable job
496ced4470dSMatthew Dillon  * for things like an initial bulk mirrors on a very large filesystem.
497e7f926a5SMatthew Dillon  */
498ced4470dSMatthew Dillon #define HIST_COUNT	(1024 * 1024)
4993d7b2393SMatthew Dillon 
500e7f926a5SMatthew Dillon static int
501e7f926a5SMatthew Dillon generate_histogram(int fd, const char *filesystem,
502ced4470dSMatthew Dillon 		   histogram_t *histogram_ary,
50339e88285SMatthew Dillon 		   struct hammer_ioc_mirror_rw *mirror_base,
50439e88285SMatthew Dillon 		   int *repeatp)
505e7f926a5SMatthew Dillon {
506e7f926a5SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
5073d7b2393SMatthew Dillon 	union hammer_ioc_mrecord_any *mrec;
508e7f926a5SMatthew Dillon 	hammer_tid_t tid_beg;
509e7f926a5SMatthew Dillon 	hammer_tid_t tid_end;
510ced4470dSMatthew Dillon 	hammer_tid_t tid;
511ced4470dSMatthew Dillon 	hammer_tid_t tidx;
512ced4470dSMatthew Dillon 	u_int64_t *tid_bytes;
5133d7b2393SMatthew Dillon 	u_int64_t total;
5143d7b2393SMatthew Dillon 	u_int64_t accum;
515269cdd19SMatthew Dillon 	int chunkno;
516e7f926a5SMatthew Dillon 	int i;
5173d7b2393SMatthew Dillon 	int res;
5183d7b2393SMatthew Dillon 	int off;
5193d7b2393SMatthew Dillon 	int len;
520e7f926a5SMatthew Dillon 
521e7f926a5SMatthew Dillon 	mirror = *mirror_base;
522e7f926a5SMatthew Dillon 	tid_beg = mirror.tid_beg;
523e7f926a5SMatthew Dillon 	tid_end = mirror.tid_end;
5243d7b2393SMatthew Dillon 	mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA;
525e7f926a5SMatthew Dillon 
5263d7b2393SMatthew Dillon 	if (*histogram_ary == NULL) {
527ced4470dSMatthew Dillon 		*histogram_ary = malloc(sizeof(struct histogram) *
5283d7b2393SMatthew Dillon 					(HIST_COUNT + 2));
5293d7b2393SMatthew Dillon 	}
5303d7b2393SMatthew Dillon 	if (tid_beg >= tid_end)
531e7f926a5SMatthew Dillon 		return(0);
532e7f926a5SMatthew Dillon 
533ced4470dSMatthew Dillon 	/* needs 2 extra */
534ced4470dSMatthew Dillon 	tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2));
5354839c61eSSascha Wildner 	bzero(tid_bytes, sizeof(*tid_bytes) * (HIST_COUNT + 2));
536ced4470dSMatthew Dillon 
53739e88285SMatthew Dillon 	if (*repeatp == 0) {
5383d7b2393SMatthew Dillon 		fprintf(stderr, "Prescan to break up bulk transfer");
5393d7b2393SMatthew Dillon 		if (VerboseOpt > 1)
5403d7b2393SMatthew Dillon 			fprintf(stderr, " (%juMB chunks)",
5413d7b2393SMatthew Dillon 				(uintmax_t)(SplitupOpt / (1024 * 1024)));
5423d7b2393SMatthew Dillon 		fprintf(stderr, "\n");
54339e88285SMatthew Dillon 	}
544e7f926a5SMatthew Dillon 
545ced4470dSMatthew Dillon 	/*
546ced4470dSMatthew Dillon 	 * Note: (tid_beg,tid_end), range is inclusive of both beg & end.
547ced4470dSMatthew Dillon 	 *
548ced4470dSMatthew Dillon 	 * Note: Estimates can be off when the mirror is way behind due
549ced4470dSMatthew Dillon 	 *	 to skips.
550ced4470dSMatthew Dillon 	 */
5513d7b2393SMatthew Dillon 	total = 0;
5523d7b2393SMatthew Dillon 	accum = 0;
553269cdd19SMatthew Dillon 	chunkno = 0;
5543d7b2393SMatthew Dillon 	for (;;) {
5553d7b2393SMatthew Dillon 		mirror.count = 0;
556e7f926a5SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
557e7f926a5SMatthew Dillon 			fprintf(stderr, "Mirror-read %s failed: %s\n",
558e7f926a5SMatthew Dillon 				filesystem, strerror(errno));
559e7f926a5SMatthew Dillon 			exit(1);
560e7f926a5SMatthew Dillon 		}
561e7f926a5SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
562e7f926a5SMatthew Dillon 			fprintf(stderr,
563e7f926a5SMatthew Dillon 				"Mirror-read %s fatal error %d\n",
564e7f926a5SMatthew Dillon 				filesystem, mirror.head.error);
565e7f926a5SMatthew Dillon 			exit(1);
566e7f926a5SMatthew Dillon 		}
5673d7b2393SMatthew Dillon 		for (off = 0;
5683d7b2393SMatthew Dillon 		     off < mirror.count;
5693d7b2393SMatthew Dillon 		     off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size)
5703d7b2393SMatthew Dillon 		) {
5713d7b2393SMatthew Dillon 			mrec = (void *)((char *)mirror.ubuf + off);
572e7f926a5SMatthew Dillon 
573e7f926a5SMatthew Dillon 			/*
574ced4470dSMatthew Dillon 			 * We only care about general RECs and PASS
575ced4470dSMatthew Dillon 			 * records.  We ignore SKIPs.
576e7f926a5SMatthew Dillon 			 */
577ced4470dSMatthew Dillon 			switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) {
578ced4470dSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
579ced4470dSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
580ced4470dSMatthew Dillon 				break;
581ced4470dSMatthew Dillon 			default:
582ced4470dSMatthew Dillon 				continue;
583e7f926a5SMatthew Dillon 			}
5843d7b2393SMatthew Dillon 
585ced4470dSMatthew Dillon 			/*
586ced4470dSMatthew Dillon 			 * Calculate for two indices, create_tid and
587ced4470dSMatthew Dillon 			 * delete_tid.  Record data only applies to
588ced4470dSMatthew Dillon 			 * the create_tid.
589ced4470dSMatthew Dillon 			 *
590ced4470dSMatthew Dillon 			 * When tid is exactly on the boundary it really
591ced4470dSMatthew Dillon 			 * belongs to the previous entry because scans
592ced4470dSMatthew Dillon 			 * are inclusive of the ending entry.
593ced4470dSMatthew Dillon 			 */
594ced4470dSMatthew Dillon 			tid = mrec->rec.leaf.base.delete_tid;
595ced4470dSMatthew Dillon 			if (tid && tid >= tid_beg && tid <= tid_end) {
596ced4470dSMatthew Dillon 				len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
597ced4470dSMatthew Dillon 				if (mrec->head.type ==
598ced4470dSMatthew Dillon 				    HAMMER_MREC_TYPE_REC) {
599ced4470dSMatthew Dillon 					len -= HAMMER_HEAD_DOALIGN(
600ced4470dSMatthew Dillon 						    mrec->rec.leaf.data_len);
601ced4470dSMatthew Dillon 					assert(len > 0);
602ced4470dSMatthew Dillon 				}
603ced4470dSMatthew Dillon 				i = (tid - tid_beg) * HIST_COUNT /
6043d7b2393SMatthew Dillon 				    (tid_end - tid_beg);
605ced4470dSMatthew Dillon 				tidx = tid_beg + i * (tid_end - tid_beg) /
606ced4470dSMatthew Dillon 						 HIST_COUNT;
607ced4470dSMatthew Dillon 				if (tid == tidx && i)
608ced4470dSMatthew Dillon 					--i;
609ced4470dSMatthew Dillon 				assert(i >= 0 && i < HIST_COUNT);
6103d7b2393SMatthew Dillon 				tid_bytes[i] += len;
6113d7b2393SMatthew Dillon 				total += len;
6123d7b2393SMatthew Dillon 				accum += len;
6133d7b2393SMatthew Dillon 			}
614ced4470dSMatthew Dillon 
615ced4470dSMatthew Dillon 			tid = mrec->rec.leaf.base.create_tid;
616ced4470dSMatthew Dillon 			if (tid && tid >= tid_beg && tid <= tid_end) {
617ced4470dSMatthew Dillon 				len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
618ced4470dSMatthew Dillon 				if (mrec->head.type ==
619ced4470dSMatthew Dillon 				    HAMMER_MREC_TYPE_REC_NODATA) {
620ced4470dSMatthew Dillon 					len += HAMMER_HEAD_DOALIGN(
621ced4470dSMatthew Dillon 						    mrec->rec.leaf.data_len);
622ced4470dSMatthew Dillon 				}
623ced4470dSMatthew Dillon 				i = (tid - tid_beg) * HIST_COUNT /
624ced4470dSMatthew Dillon 				    (tid_end - tid_beg);
625ced4470dSMatthew Dillon 				tidx = tid_beg + i * (tid_end - tid_beg) /
626ced4470dSMatthew Dillon 						 HIST_COUNT;
627ced4470dSMatthew Dillon 				if (tid == tidx && i)
628ced4470dSMatthew Dillon 					--i;
629ced4470dSMatthew Dillon 				assert(i >= 0 && i < HIST_COUNT);
630ced4470dSMatthew Dillon 				tid_bytes[i] += len;
631ced4470dSMatthew Dillon 				total += len;
632ced4470dSMatthew Dillon 				accum += len;
6333d7b2393SMatthew Dillon 			}
6343d7b2393SMatthew Dillon 		}
63539e88285SMatthew Dillon 		if (*repeatp == 0 && accum > SplitupOpt) {
636269cdd19SMatthew Dillon 			if (VerboseOpt > 1) {
6373d7b2393SMatthew Dillon 				fprintf(stderr, ".");
6383d7b2393SMatthew Dillon 				fflush(stderr);
6393d7b2393SMatthew Dillon 			}
640269cdd19SMatthew Dillon 			++chunkno;
641269cdd19SMatthew Dillon 			score_printf(LINE2, "Prescan chunk %d", chunkno);
642269cdd19SMatthew Dillon 			accum = 0;
6433d7b2393SMatthew Dillon 		}
6443d7b2393SMatthew Dillon 		if (mirror.count == 0)
6453d7b2393SMatthew Dillon 			break;
6463d7b2393SMatthew Dillon 		mirror.key_beg = mirror.key_cur;
6473d7b2393SMatthew Dillon 	}
6483d7b2393SMatthew Dillon 
6493d7b2393SMatthew Dillon 	/*
650d6c40a21SFrançois Tigeot 	 * Reduce to SplitupOpt (default 4GB) chunks.  This code may
651ced4470dSMatthew Dillon 	 * use up to two additional elements.  Do the array in-place.
652ced4470dSMatthew Dillon 	 *
653ced4470dSMatthew Dillon 	 * Inefficient degenerate cases can occur if we do not accumulate
654ced4470dSMatthew Dillon 	 * at least the requested split amount, so error on the side of
655ced4470dSMatthew Dillon 	 * going over a bit.
6563d7b2393SMatthew Dillon 	 */
6573d7b2393SMatthew Dillon 	res = 0;
658ced4470dSMatthew Dillon 	(*histogram_ary)[res].tid = tid_beg;
659ced4470dSMatthew Dillon 	(*histogram_ary)[res].bytes = tid_bytes[0];
660ced4470dSMatthew Dillon 	for (i = 1; i < HIST_COUNT; ++i) {
661ced4470dSMatthew Dillon 		if ((*histogram_ary)[res].bytes >= SplitupOpt) {
662ced4470dSMatthew Dillon 			++res;
663ced4470dSMatthew Dillon 			(*histogram_ary)[res].tid = tid_beg +
6643d7b2393SMatthew Dillon 					i * (tid_end - tid_beg) /
6653d7b2393SMatthew Dillon 					HIST_COUNT;
666ced4470dSMatthew Dillon 			(*histogram_ary)[res].bytes = 0;
667ced4470dSMatthew Dillon 
6683d7b2393SMatthew Dillon 		}
669ced4470dSMatthew Dillon 		(*histogram_ary)[res].bytes += tid_bytes[i];
6703d7b2393SMatthew Dillon 	}
671ced4470dSMatthew Dillon 	++res;
672ced4470dSMatthew Dillon 	(*histogram_ary)[res].tid = tid_end;
673ced4470dSMatthew Dillon 	(*histogram_ary)[res].bytes = -1;
674ced4470dSMatthew Dillon 
67539e88285SMatthew Dillon 	if (*repeatp == 0) {
6763d7b2393SMatthew Dillon 		if (VerboseOpt > 1)
6773d7b2393SMatthew Dillon 			fprintf(stderr, "\n");	/* newline after ... */
678269cdd19SMatthew Dillon 		score_printf(LINE3, "Prescan %d chunks, total %ju MBytes",
679269cdd19SMatthew Dillon 			res, (uintmax_t)total / (1024 * 1024));
68027eff55eSMatthew Dillon 		fprintf(stderr, "Prescan %d chunks, total %ju MBytes (",
6813d7b2393SMatthew Dillon 			res, (uintmax_t)total / (1024 * 1024));
682ced4470dSMatthew Dillon 		for (i = 0; i < res && i < 3; ++i) {
683ced4470dSMatthew Dillon 			if (i)
684ced4470dSMatthew Dillon 				fprintf(stderr, ", ");
68539e88285SMatthew Dillon 			fprintf(stderr, "%ju",
68639e88285SMatthew Dillon 				(uintmax_t)(*histogram_ary)[i].bytes);
687ced4470dSMatthew Dillon 		}
688ced4470dSMatthew Dillon 		if (i < res)
689ced4470dSMatthew Dillon 			fprintf(stderr, ", ...");
690ced4470dSMatthew Dillon 		fprintf(stderr, ")\n");
69139e88285SMatthew Dillon 	}
69239e88285SMatthew Dillon 	assert(res <= HIST_COUNT);
69339e88285SMatthew Dillon 	*repeatp = 1;
694ced4470dSMatthew Dillon 
695ced4470dSMatthew Dillon 	free(tid_bytes);
6963d7b2393SMatthew Dillon 	return(res);
697e7f926a5SMatthew Dillon }
698e7f926a5SMatthew Dillon 
69901a72c9fSMatthew Dillon static void
70001a72c9fSMatthew Dillon create_pfs(const char *filesystem, uuid_t *s_uuid)
70101a72c9fSMatthew Dillon {
702f414d101SSascha Wildner 	if (ForceYesOpt == 1) {
70307485271SMichael Neumann 		fprintf(stderr, "PFS slave %s does not exist. "
70407485271SMichael Neumann 			"Auto create new slave PFS!\n", filesystem);
70507485271SMichael Neumann 
706f414d101SSascha Wildner 	} else {
70701a72c9fSMatthew Dillon 		fprintf(stderr, "PFS slave %s does not exist.\n"
70801a72c9fSMatthew Dillon 			"Do you want to create a new slave PFS? (yes|no) ",
70901a72c9fSMatthew Dillon 			filesystem);
71001a72c9fSMatthew Dillon 		fflush(stderr);
71101a72c9fSMatthew Dillon 		if (getyn() != 1) {
71201a72c9fSMatthew Dillon 			fprintf(stderr, "Aborting operation\n");
71301a72c9fSMatthew Dillon 			exit(1);
71401a72c9fSMatthew Dillon 		}
71507485271SMichael Neumann 	}
71601a72c9fSMatthew Dillon 
71701a72c9fSMatthew Dillon 	u_int32_t status;
71801a72c9fSMatthew Dillon 	char *shared_uuid = NULL;
71901a72c9fSMatthew Dillon 	uuid_to_string(s_uuid, &shared_uuid, &status);
72001a72c9fSMatthew Dillon 
72101a72c9fSMatthew Dillon 	char *cmd = NULL;
72201a72c9fSMatthew Dillon 	asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2",
72301a72c9fSMatthew Dillon 		 filesystem, shared_uuid);
72401a72c9fSMatthew Dillon 	free(shared_uuid);
72501a72c9fSMatthew Dillon 
72601a72c9fSMatthew Dillon 	if (cmd == NULL) {
72701a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to alloc memory\n");
72801a72c9fSMatthew Dillon 		exit(1);
72901a72c9fSMatthew Dillon 	}
73001a72c9fSMatthew Dillon 	if (system(cmd) != 0) {
73101a72c9fSMatthew Dillon 		fprintf(stderr, "Failed to create PFS\n");
73201a72c9fSMatthew Dillon 	}
73301a72c9fSMatthew Dillon 	free(cmd);
73401a72c9fSMatthew Dillon }
73501a72c9fSMatthew Dillon 
73617dd83bcSMatthew Dillon /*
73717dd83bcSMatthew Dillon  * Pipe the mirroring data stream on stdin to the HAMMER VFS, adding
73817dd83bcSMatthew Dillon  * some additional packet types to negotiate TID ranges and to verify
73917dd83bcSMatthew Dillon  * completion.  The HAMMER VFS does most of the work.
74017dd83bcSMatthew Dillon  *
74117dd83bcSMatthew Dillon  * It is important to note that the mirror.key_{beg,end} range must
74217dd83bcSMatthew Dillon  * match the ranged used by the original.  For now both sides use
74317dd83bcSMatthew Dillon  * range the entire key space.
74417dd83bcSMatthew Dillon  *
74517dd83bcSMatthew Dillon  * It is even more important that the records in the stream conform
74617dd83bcSMatthew Dillon  * to the TID range also supplied in the stream.  The HAMMER VFS will
74717dd83bcSMatthew Dillon  * use the REC, PASS, and SKIP record types to track the portions of
74817dd83bcSMatthew Dillon  * the B-Tree being scanned in order to be able to proactively delete
74917dd83bcSMatthew Dillon  * records on the target within those active areas that are not mentioned
75017dd83bcSMatthew Dillon  * by the source.
75117dd83bcSMatthew Dillon  *
75217dd83bcSMatthew Dillon  * The mirror.key_cur field is used by the VFS to do this tracking.  It
75317dd83bcSMatthew Dillon  * must be initialized to key_beg but then is persistently updated by
75417dd83bcSMatthew Dillon  * the HAMMER VFS on each successive ioctl() call.  If you blow up this
75517dd83bcSMatthew Dillon  * field you will blow up the mirror target, possibly to the point of
75617dd83bcSMatthew Dillon  * deleting everything.  As a safety measure the HAMMER VFS simply marks
75717dd83bcSMatthew Dillon  * the records that the source has destroyed as deleted on the target,
75817dd83bcSMatthew Dillon  * and normal pruning operations will deal with their final disposition
75917dd83bcSMatthew Dillon  * at some later time.
76017dd83bcSMatthew Dillon  */
761a7fbbf91SMatthew Dillon void
762a7fbbf91SMatthew Dillon hammer_cmd_mirror_write(char **av, int ac)
763a7fbbf91SMatthew Dillon {
764a7fbbf91SMatthew Dillon 	struct hammer_ioc_mirror_rw mirror;
7658b640662SAntonio Huete Jimenez 	char *filesystem;
766a7fbbf91SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
767d4e5b69bSMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
76817dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
769243ca327SMatthew Dillon 	struct hammer_ioc_synctid synctid;
77017dd83bcSMatthew Dillon 	union hammer_ioc_mrecord_any mrec_tmp;
77117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
77201a72c9fSMatthew Dillon 	struct stat st;
773243ca327SMatthew Dillon 	int error;
774243ca327SMatthew Dillon 	int fd;
77548eadef9SMatthew Dillon 	int n;
776a7fbbf91SMatthew Dillon 
77734bb69d8SThomas Nikolajsen 	if (ac != 1)
778a7fbbf91SMatthew Dillon 		mirror_usage(1);
779a7fbbf91SMatthew Dillon 	filesystem = av[0];
78069f5a58cSMatthew Dillon 	hammer_check_restrict(filesystem);
781a7fbbf91SMatthew Dillon 
78248eadef9SMatthew Dillon 	pickup.signature = 0;
78348eadef9SMatthew Dillon 	pickup.type = 0;
78448eadef9SMatthew Dillon 
78548eadef9SMatthew Dillon again:
786a7fbbf91SMatthew Dillon 	bzero(&mirror, sizeof(mirror));
787a7fbbf91SMatthew Dillon 	hammer_key_beg_init(&mirror.key_beg);
788a7fbbf91SMatthew Dillon 	hammer_key_end_init(&mirror.key_end);
78917dd83bcSMatthew Dillon 	mirror.key_end = mirror.key_beg;
790a7fbbf91SMatthew Dillon 
79101a72c9fSMatthew Dillon 	/*
79201a72c9fSMatthew Dillon 	 * Read initial packet
79301a72c9fSMatthew Dillon 	 */
79401a72c9fSMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
79501a72c9fSMatthew Dillon 	if (mrec == NULL) {
79601a72c9fSMatthew Dillon 		if (error == 0)
79701a72c9fSMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
79801a72c9fSMatthew Dillon 		exit(1);
79901a72c9fSMatthew Dillon 	}
80001a72c9fSMatthew Dillon 	/*
80101a72c9fSMatthew Dillon 	 * Validate packet
80201a72c9fSMatthew Dillon 	 */
80301a72c9fSMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
804*764d0c8cSTomohiro Kusumi 		free(buf);
80501a72c9fSMatthew Dillon 		return;
80601a72c9fSMatthew Dillon 	}
80701a72c9fSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
80801a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
80901a72c9fSMatthew Dillon 				"PFSD record type\n");
81001a72c9fSMatthew Dillon 		exit(1);
81101a72c9fSMatthew Dillon 	}
81201a72c9fSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
81301a72c9fSMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
81401a72c9fSMatthew Dillon 				"size\n");
81501a72c9fSMatthew Dillon 		exit(1);
81601a72c9fSMatthew Dillon 	}
81701a72c9fSMatthew Dillon 
81801a72c9fSMatthew Dillon 	/*
81901a72c9fSMatthew Dillon 	 * Create slave PFS if it doesn't yet exist
82001a72c9fSMatthew Dillon 	 */
82101a72c9fSMatthew Dillon 	if (lstat(filesystem, &st) != 0) {
82201a72c9fSMatthew Dillon 		create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid);
82301a72c9fSMatthew Dillon 	}
82401a72c9fSMatthew Dillon 	free(mrec);
82501a72c9fSMatthew Dillon 	mrec = NULL;
82601a72c9fSMatthew Dillon 
827d4e5b69bSMatthew Dillon 	fd = getpfs(&pfs, filesystem);
828a7fbbf91SMatthew Dillon 
829243ca327SMatthew Dillon 	/*
830d4e5b69bSMatthew Dillon 	 * In two-way mode the target writes out a PFS packet first.
831d4e5b69bSMatthew Dillon 	 * The source uses our tid_end as its tid_beg by default,
832d4e5b69bSMatthew Dillon 	 * picking up where it left off.
833243ca327SMatthew Dillon 	 */
834d4e5b69bSMatthew Dillon 	mirror.tid_beg = 0;
835d4e5b69bSMatthew Dillon 	if (TwoWayPipeOpt) {
836e7f926a5SMatthew Dillon 		generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
837e7f926a5SMatthew Dillon 		if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
838e7f926a5SMatthew Dillon 			mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
839e7f926a5SMatthew Dillon 		mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
840e7f926a5SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
841e7f926a5SMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.pfs));
842d4e5b69bSMatthew Dillon 	}
843d4e5b69bSMatthew Dillon 
844d4e5b69bSMatthew Dillon 	/*
84517dd83bcSMatthew Dillon 	 * Read and process the PFS header.  The source informs us of
84617dd83bcSMatthew Dillon 	 * the TID range the stream represents.
847d4e5b69bSMatthew Dillon 	 */
84848eadef9SMatthew Dillon 	n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup,
849d4e5b69bSMatthew Dillon 				 &mirror.tid_beg, &mirror.tid_end);
85048eadef9SMatthew Dillon 	if (n < 0) {	/* got TERM record */
85148eadef9SMatthew Dillon 		relpfs(fd, &pfs);
852*764d0c8cSTomohiro Kusumi 		free(buf);
85348eadef9SMatthew Dillon 		return;
85448eadef9SMatthew Dillon 	}
85534ebae70SMatthew Dillon 
856a7fbbf91SMatthew Dillon 	mirror.ubuf = buf;
857a7fbbf91SMatthew Dillon 	mirror.size = SERIALBUF_SIZE;
858a7fbbf91SMatthew Dillon 
859243ca327SMatthew Dillon 	/*
86017dd83bcSMatthew Dillon 	 * Read and process bulk records (REC, PASS, and SKIP types).
86117dd83bcSMatthew Dillon 	 *
86217dd83bcSMatthew Dillon 	 * On your life, do NOT mess with mirror.key_cur or your mirror
86317dd83bcSMatthew Dillon 	 * target may become history.
864243ca327SMatthew Dillon 	 */
865a7fbbf91SMatthew Dillon 	for (;;) {
866a7fbbf91SMatthew Dillon 		mirror.count = 0;
867d4e5b69bSMatthew Dillon 		mirror.pfs_id = pfs.pfs_id;
868d4e5b69bSMatthew Dillon 		mirror.shared_uuid = pfs.ondisk->shared_uuid;
869a7fbbf91SMatthew Dillon 		mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
870a7fbbf91SMatthew Dillon 		if (mirror.size <= 0)
871a7fbbf91SMatthew Dillon 			break;
872a7fbbf91SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
873a7fbbf91SMatthew Dillon 			fprintf(stderr, "Mirror-write %s failed: %s\n",
874a7fbbf91SMatthew Dillon 				filesystem, strerror(errno));
875a7fbbf91SMatthew Dillon 			exit(1);
876a7fbbf91SMatthew Dillon 		}
87717dd83bcSMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
87817dd83bcSMatthew Dillon 			fprintf(stderr,
87917dd83bcSMatthew Dillon 				"Mirror-write %s fatal error %d\n",
88017dd83bcSMatthew Dillon 				filesystem, mirror.head.error);
88117dd83bcSMatthew Dillon 			exit(1);
88217dd83bcSMatthew Dillon 		}
883243ca327SMatthew Dillon #if 0
884a7fbbf91SMatthew Dillon 		if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
885a7fbbf91SMatthew Dillon 			fprintf(stderr,
886a7fbbf91SMatthew Dillon 				"Mirror-write %s interrupted by timer at"
887243ca327SMatthew Dillon 				" %016llx\n",
888a7fbbf91SMatthew Dillon 				filesystem,
889243ca327SMatthew Dillon 				mirror.key_cur.obj_id);
890a7fbbf91SMatthew Dillon 			exit(0);
891a7fbbf91SMatthew Dillon 		}
892243ca327SMatthew Dillon #endif
893a7fbbf91SMatthew Dillon 	}
894243ca327SMatthew Dillon 
895243ca327SMatthew Dillon 	/*
896243ca327SMatthew Dillon 	 * Read and process the termination sync record.
897243ca327SMatthew Dillon 	 */
898243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
8999dc76cb1SMatthew Dillon 
9009dc76cb1SMatthew Dillon 	if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
90148eadef9SMatthew Dillon 		fprintf(stderr, "Mirror-write: received termination request\n");
902*764d0c8cSTomohiro Kusumi 		relpfs(fd, &pfs);
90348eadef9SMatthew Dillon 		free(mrec);
904*764d0c8cSTomohiro Kusumi 		free(buf);
9059dc76cb1SMatthew Dillon 		return;
9069dc76cb1SMatthew Dillon 	}
9079dc76cb1SMatthew Dillon 
90817dd83bcSMatthew Dillon 	if (mrec == NULL ||
90948eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
91048eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE) ||
91117dd83bcSMatthew Dillon 	    mrec->head.rec_size != sizeof(mrec->sync)) {
912243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-write %s: Did not get termination "
91317dd83bcSMatthew Dillon 				"sync record, or rec_size is wrong rt=%d\n",
914db7dd3cfSMatthew Dillon 				filesystem,
915a446fca6SSascha Wildner 				(mrec ? (int)mrec->head.type : -1));
916da44aa75SMatthew Dillon 		exit(1);
917243ca327SMatthew Dillon 	}
918243ca327SMatthew Dillon 
919243ca327SMatthew Dillon 	/*
920243ca327SMatthew Dillon 	 * Update the PFS info on the target so the user has visibility
92148eadef9SMatthew Dillon 	 * into the new snapshot, and sync the target filesystem.
922243ca327SMatthew Dillon 	 */
92348eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) {
924d4e5b69bSMatthew Dillon 		update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
925243ca327SMatthew Dillon 
926243ca327SMatthew Dillon 		bzero(&synctid, sizeof(synctid));
927243ca327SMatthew Dillon 		synctid.op = HAMMER_SYNCTID_SYNC2;
928243ca327SMatthew Dillon 		ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
929243ca327SMatthew Dillon 
93048eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
93148eadef9SMatthew Dillon 			fprintf(stderr, "Mirror-write %s: succeeded\n",
93248eadef9SMatthew Dillon 				filesystem);
93348eadef9SMatthew Dillon 		}
93448eadef9SMatthew Dillon 	}
93548eadef9SMatthew Dillon 
93648eadef9SMatthew Dillon 	free(mrec);
93748eadef9SMatthew Dillon 	mrec = NULL;
938243ca327SMatthew Dillon 
939243ca327SMatthew Dillon 	/*
940243ca327SMatthew Dillon 	 * Report back to the originator.
941243ca327SMatthew Dillon 	 */
942243ca327SMatthew Dillon 	if (TwoWayPipeOpt) {
94317dd83bcSMatthew Dillon 		mrec_tmp.update.tid = mirror.tid_end;
944243ca327SMatthew Dillon 		write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
94517dd83bcSMatthew Dillon 			      &mrec_tmp, sizeof(mrec_tmp.update));
946243ca327SMatthew Dillon 	} else {
947a276dc6bSMatthew Dillon 		printf("Source can update synctid to 0x%016jx\n",
948a276dc6bSMatthew Dillon 		       (uintmax_t)mirror.tid_end);
949243ca327SMatthew Dillon 	}
95048eadef9SMatthew Dillon 	relpfs(fd, &pfs);
95148eadef9SMatthew Dillon 	goto again;
952243ca327SMatthew Dillon }
953243ca327SMatthew Dillon 
954243ca327SMatthew Dillon void
9559f1b0121SAntonio Huete Jimenez hammer_cmd_mirror_dump(char **av, int ac)
956243ca327SMatthew Dillon {
957243ca327SMatthew Dillon 	char *buf = malloc(SERIALBUF_SIZE);
95817dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head pickup;
95917dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
960243ca327SMatthew Dillon 	int error;
961243ca327SMatthew Dillon 	int size;
96217dd83bcSMatthew Dillon 	int offset;
96317dd83bcSMatthew Dillon 	int bytes;
9649f1b0121SAntonio Huete Jimenez 	int header_only = 0;
9659f1b0121SAntonio Huete Jimenez 
9669f1b0121SAntonio Huete Jimenez 	if (ac == 1 && strcmp(*av, "header") == 0)
9679f1b0121SAntonio Huete Jimenez 		header_only = 1;
9689f1b0121SAntonio Huete Jimenez 	else if (ac != 0)
9699f1b0121SAntonio Huete Jimenez 		mirror_usage(1);
970243ca327SMatthew Dillon 
971243ca327SMatthew Dillon 	/*
972243ca327SMatthew Dillon 	 * Read and process the PFS header
973243ca327SMatthew Dillon 	 */
974243ca327SMatthew Dillon 	pickup.signature = 0;
975243ca327SMatthew Dillon 	pickup.type = 0;
976243ca327SMatthew Dillon 
977243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
978243ca327SMatthew Dillon 
9799f1b0121SAntonio Huete Jimenez 	/*
9809f1b0121SAntonio Huete Jimenez 	 * Dump the PFS header. mirror-dump takes its input from the output
9819f1b0121SAntonio Huete Jimenez 	 * of a mirror-read so getpfs() can't be used to get a fd to be passed
9829f1b0121SAntonio Huete Jimenez 	 * to dump_pfsd().
9839f1b0121SAntonio Huete Jimenez 	 */
9849f1b0121SAntonio Huete Jimenez 	if (header_only && mrec != NULL) {
9859f1b0121SAntonio Huete Jimenez 		dump_pfsd(&mrec->pfs.pfsd, -1);
986cc71ff00STomohiro Kusumi 		free(mrec);
987*764d0c8cSTomohiro Kusumi 		free(buf);
9889f1b0121SAntonio Huete Jimenez 		return;
9899f1b0121SAntonio Huete Jimenez 	}
990cc71ff00STomohiro Kusumi 	free(mrec);
9919f1b0121SAntonio Huete Jimenez 
99239e88285SMatthew Dillon again:
993243ca327SMatthew Dillon 	/*
994243ca327SMatthew Dillon 	 * Read and process bulk records
995243ca327SMatthew Dillon 	 */
996243ca327SMatthew Dillon 	for (;;) {
997243ca327SMatthew Dillon 		size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
998243ca327SMatthew Dillon 		if (size <= 0)
999243ca327SMatthew Dillon 			break;
100017dd83bcSMatthew Dillon 		offset = 0;
100117dd83bcSMatthew Dillon 		while (offset < size) {
100217dd83bcSMatthew Dillon 			mrec = (void *)((char *)buf + offset);
100317dd83bcSMatthew Dillon 			bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
100417dd83bcSMatthew Dillon 			if (offset + bytes > size) {
100517dd83bcSMatthew Dillon 				fprintf(stderr, "Misaligned record\n");
100617dd83bcSMatthew Dillon 				exit(1);
100717dd83bcSMatthew Dillon 			}
100817dd83bcSMatthew Dillon 
100985a8e8a7SMatthew Dillon 			switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) {
101085a8e8a7SMatthew Dillon 			case HAMMER_MREC_TYPE_REC_BADCRC:
101117dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_REC:
1012a276dc6bSMatthew Dillon 				printf("Record obj=%016jx key=%016jx "
101385a8e8a7SMatthew Dillon 				       "rt=%02x ot=%02x",
1014a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
1015a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key,
101617dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
101717dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
101885a8e8a7SMatthew Dillon 				if (mrec->head.type ==
101985a8e8a7SMatthew Dillon 				    HAMMER_MREC_TYPE_REC_BADCRC) {
102085a8e8a7SMatthew Dillon 					printf(" (BAD CRC)");
102185a8e8a7SMatthew Dillon 				}
102285a8e8a7SMatthew Dillon 				printf("\n");
1023a276dc6bSMatthew Dillon 				printf("       tids %016jx:%016jx data=%d\n",
1024a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.create_tid,
1025a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.delete_tid,
102617dd83bcSMatthew Dillon 				    mrec->rec.leaf.data_len);
102717dd83bcSMatthew Dillon 				break;
102817dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_PASS:
1029a276dc6bSMatthew Dillon 				printf("Pass   obj=%016jx key=%016jx "
103017dd83bcSMatthew Dillon 				       "rt=%02x ot=%02x\n",
1031a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
1032a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key,
103317dd83bcSMatthew Dillon 					mrec->rec.leaf.base.rec_type,
103417dd83bcSMatthew Dillon 					mrec->rec.leaf.base.obj_type);
1035a276dc6bSMatthew Dillon 				printf("       tids %016jx:%016jx data=%d\n",
1036a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.create_tid,
1037a276dc6bSMatthew Dillon 				    (uintmax_t)mrec->rec.leaf.base.delete_tid,
103817dd83bcSMatthew Dillon 					mrec->rec.leaf.data_len);
103917dd83bcSMatthew Dillon 				break;
104017dd83bcSMatthew Dillon 			case HAMMER_MREC_TYPE_SKIP:
1041a276dc6bSMatthew Dillon 				printf("Skip   obj=%016jx key=%016jx rt=%02x to\n"
1042a276dc6bSMatthew Dillon 				       "       obj=%016jx key=%016jx rt=%02x\n",
1043a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_beg.obj_id,
1044a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_beg.key,
104517dd83bcSMatthew Dillon 				       mrec->skip.skip_beg.rec_type,
1046a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_end.obj_id,
1047a276dc6bSMatthew Dillon 				       (uintmax_t)mrec->skip.skip_end.key,
104817dd83bcSMatthew Dillon 				       mrec->skip.skip_end.rec_type);
104917dd83bcSMatthew Dillon 			default:
105017dd83bcSMatthew Dillon 				break;
105117dd83bcSMatthew Dillon 			}
105217dd83bcSMatthew Dillon 			offset += bytes;
1053243ca327SMatthew Dillon 		}
1054243ca327SMatthew Dillon 	}
1055243ca327SMatthew Dillon 
1056243ca327SMatthew Dillon 	/*
1057243ca327SMatthew Dillon 	 * Read and process the termination sync record.
1058243ca327SMatthew Dillon 	 */
1059243ca327SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
106048eadef9SMatthew Dillon 	if (mrec == NULL ||
106148eadef9SMatthew Dillon 	    (mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
106248eadef9SMatthew Dillon 	     mrec->head.type != HAMMER_MREC_TYPE_IDLE)
106348eadef9SMatthew Dillon 	 ) {
1064243ca327SMatthew Dillon 		fprintf(stderr, "Mirror-dump: Did not get termination "
1065243ca327SMatthew Dillon 				"sync record\n");
1066243ca327SMatthew Dillon 	}
1067cc71ff00STomohiro Kusumi 	free(mrec);
106839e88285SMatthew Dillon 
106939e88285SMatthew Dillon 	/*
107039e88285SMatthew Dillon 	 * Continue with more batches until EOF.
107139e88285SMatthew Dillon 	 */
107239e88285SMatthew Dillon 	mrec = read_mrecord(0, &error, &pickup);
1073cc71ff00STomohiro Kusumi 	if (mrec) {
1074cc71ff00STomohiro Kusumi 		free(mrec);
107539e88285SMatthew Dillon 		goto again;
1076a7fbbf91SMatthew Dillon 	}
1077*764d0c8cSTomohiro Kusumi 	free(buf);
1078cc71ff00STomohiro Kusumi }
1079a7fbbf91SMatthew Dillon 
1080a7fbbf91SMatthew Dillon void
108148eadef9SMatthew Dillon hammer_cmd_mirror_copy(char **av, int ac, int streaming)
1082a7fbbf91SMatthew Dillon {
108334ebae70SMatthew Dillon 	pid_t pid1;
108434ebae70SMatthew Dillon 	pid_t pid2;
108534ebae70SMatthew Dillon 	int fds[2];
108608b71f9fSMatthew Dillon 	const char *xav[32];
1087243ca327SMatthew Dillon 	char tbuf[16];
1088e2c596b1SChris Turner 	char *sh, *user, *host, *rfs;
1089243ca327SMatthew Dillon 	int xac;
109034ebae70SMatthew Dillon 
109134ebae70SMatthew Dillon 	if (ac != 2)
109234ebae70SMatthew Dillon 		mirror_usage(1);
109334ebae70SMatthew Dillon 
1094e7f926a5SMatthew Dillon 	TwoWayPipeOpt = 1;
10950bd7a37cSMatthew Dillon 	signal(SIGPIPE, SIG_IGN);
1096e7f926a5SMatthew Dillon 
1097e7f926a5SMatthew Dillon again:
109834ebae70SMatthew Dillon 	if (pipe(fds) < 0) {
109934ebae70SMatthew Dillon 		perror("pipe");
110034ebae70SMatthew Dillon 		exit(1);
110134ebae70SMatthew Dillon 	}
110234ebae70SMatthew Dillon 
110334ebae70SMatthew Dillon 	/*
110434ebae70SMatthew Dillon 	 * Source
110534ebae70SMatthew Dillon 	 */
110634ebae70SMatthew Dillon 	if ((pid1 = fork()) == 0) {
11070bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
110834ebae70SMatthew Dillon 		dup2(fds[0], 0);
110934ebae70SMatthew Dillon 		dup2(fds[0], 1);
111034ebae70SMatthew Dillon 		close(fds[0]);
111134ebae70SMatthew Dillon 		close(fds[1]);
1112e2c596b1SChris Turner 		if ((rfs = strchr(av[0], ':')) != NULL) {
1113243ca327SMatthew Dillon 			xac = 0;
1114e2c596b1SChris Turner 
1115e2c596b1SChris Turner 			if((sh = getenv("HAMMER_RSH")) == NULL)
1116243ca327SMatthew Dillon 				xav[xac++] = "ssh";
1117e2c596b1SChris Turner 			else
1118e2c596b1SChris Turner 				xav[xac++] = sh;
1119e2c596b1SChris Turner 
11203a998207SMatthew Dillon 			if (CompressOpt)
11213a998207SMatthew Dillon 				xav[xac++] = "-C";
1122e2c596b1SChris Turner 
1123e2c596b1SChris Turner 			if ((host = strchr(av[0], '@')) != NULL) {
1124e2c596b1SChris Turner 				user = strndup( av[0], (host++ - av[0]));
1125e2c596b1SChris Turner 				host = strndup( host, (rfs++ - host));
1126e2c596b1SChris Turner 				xav[xac++] = "-l";
1127e2c596b1SChris Turner 				xav[xac++] = user;
1128e2c596b1SChris Turner 				xav[xac++] = host;
1129e2c596b1SChris Turner 			}
1130e2c596b1SChris Turner 			else {
1131e2c596b1SChris Turner 				host = strndup( av[0], (rfs++ - av[0]));
1132e2c596b1SChris Turner 				user = NULL;
1133e2c596b1SChris Turner 				xav[xac++] = host;
1134e2c596b1SChris Turner 			}
1135e2c596b1SChris Turner 
1136e2c596b1SChris Turner 
11376c45ca3eSMatthew Dillon 			if (SshPort) {
11386c45ca3eSMatthew Dillon 				xav[xac++] = "-p";
11396c45ca3eSMatthew Dillon 				xav[xac++] = SshPort;
11406c45ca3eSMatthew Dillon 			}
1141e2c596b1SChris Turner 
1142243ca327SMatthew Dillon 			xav[xac++] = "hammer";
114348eadef9SMatthew Dillon 
114448eadef9SMatthew Dillon 			switch(VerboseOpt) {
114548eadef9SMatthew Dillon 			case 0:
114648eadef9SMatthew Dillon 				break;
114748eadef9SMatthew Dillon 			case 1:
1148243ca327SMatthew Dillon 				xav[xac++] = "-v";
114948eadef9SMatthew Dillon 				break;
115048eadef9SMatthew Dillon 			case 2:
115148eadef9SMatthew Dillon 				xav[xac++] = "-vv";
115248eadef9SMatthew Dillon 				break;
115348eadef9SMatthew Dillon 			default:
115448eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
115548eadef9SMatthew Dillon 				break;
115648eadef9SMatthew Dillon 			}
115707485271SMichael Neumann 			if (ForceYesOpt) {
115807485271SMichael Neumann 				xav[xac++] = "-y";
115907485271SMichael Neumann 			}
1160243ca327SMatthew Dillon 			xav[xac++] = "-2";
1161243ca327SMatthew Dillon 			if (TimeoutOpt) {
1162243ca327SMatthew Dillon 				snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
1163243ca327SMatthew Dillon 				xav[xac++] = "-t";
1164243ca327SMatthew Dillon 				xav[xac++] = tbuf;
1165243ca327SMatthew Dillon 			}
116608b71f9fSMatthew Dillon 			if (SplitupOptStr) {
116708b71f9fSMatthew Dillon 				xav[xac++] = "-S";
116808b71f9fSMatthew Dillon 				xav[xac++] = SplitupOptStr;
116908b71f9fSMatthew Dillon 			}
117048eadef9SMatthew Dillon 			if (streaming)
1171901f434aSMatthew Dillon 				xav[xac++] = "mirror-read-stream";
117248eadef9SMatthew Dillon 			else
1173243ca327SMatthew Dillon 				xav[xac++] = "mirror-read";
1174e2c596b1SChris Turner 			xav[xac++] = rfs;
1175243ca327SMatthew Dillon 			xav[xac++] = NULL;
1176e2c596b1SChris Turner 			execvp(*xav, (void *)xav);
117734ebae70SMatthew Dillon 		} else {
117848eadef9SMatthew Dillon 			hammer_cmd_mirror_read(av, 1, streaming);
1179243ca327SMatthew Dillon 			fflush(stdout);
1180243ca327SMatthew Dillon 			fflush(stderr);
118134ebae70SMatthew Dillon 		}
118253d93cc7SMatthew Dillon 		_exit(1);
118334ebae70SMatthew Dillon 	}
118434ebae70SMatthew Dillon 
118534ebae70SMatthew Dillon 	/*
118634ebae70SMatthew Dillon 	 * Target
118734ebae70SMatthew Dillon 	 */
118834ebae70SMatthew Dillon 	if ((pid2 = fork()) == 0) {
11890bd7a37cSMatthew Dillon 		signal(SIGPIPE, SIG_DFL);
119034ebae70SMatthew Dillon 		dup2(fds[1], 0);
119134ebae70SMatthew Dillon 		dup2(fds[1], 1);
119234ebae70SMatthew Dillon 		close(fds[0]);
119334ebae70SMatthew Dillon 		close(fds[1]);
1194e2c596b1SChris Turner 		if ((rfs = strchr(av[1], ':')) != NULL) {
1195243ca327SMatthew Dillon 			xac = 0;
1196e2c596b1SChris Turner 
1197e2c596b1SChris Turner 			if((sh = getenv("HAMMER_RSH")) == NULL)
1198243ca327SMatthew Dillon 				xav[xac++] = "ssh";
1199e2c596b1SChris Turner 			else
1200e2c596b1SChris Turner 				xav[xac++] = sh;
1201e2c596b1SChris Turner 
12023a998207SMatthew Dillon 			if (CompressOpt)
12033a998207SMatthew Dillon 				xav[xac++] = "-C";
1204e2c596b1SChris Turner 
1205e2c596b1SChris Turner 			if ((host = strchr(av[1], '@')) != NULL) {
1206e2c596b1SChris Turner 				user = strndup( av[1], (host++ - av[1]));
1207e2c596b1SChris Turner 				host = strndup( host, (rfs++ - host));
1208e2c596b1SChris Turner 				xav[xac++] = "-l";
1209e2c596b1SChris Turner 				xav[xac++] = user;
1210e2c596b1SChris Turner 				xav[xac++] = host;
1211e2c596b1SChris Turner 			}
1212e2c596b1SChris Turner 			else {
1213e2c596b1SChris Turner 				host = strndup( av[1], (rfs++ - av[1]));
1214e2c596b1SChris Turner 				user = NULL;
1215e2c596b1SChris Turner 				xav[xac++] = host;
1216e2c596b1SChris Turner 			}
1217e2c596b1SChris Turner 
12186c45ca3eSMatthew Dillon 			if (SshPort) {
12196c45ca3eSMatthew Dillon 				xav[xac++] = "-p";
12206c45ca3eSMatthew Dillon 				xav[xac++] = SshPort;
12216c45ca3eSMatthew Dillon 			}
1222e2c596b1SChris Turner 
1223243ca327SMatthew Dillon 			xav[xac++] = "hammer";
122448eadef9SMatthew Dillon 
122548eadef9SMatthew Dillon 			switch(VerboseOpt) {
122648eadef9SMatthew Dillon 			case 0:
122748eadef9SMatthew Dillon 				break;
122848eadef9SMatthew Dillon 			case 1:
1229243ca327SMatthew Dillon 				xav[xac++] = "-v";
123048eadef9SMatthew Dillon 				break;
123148eadef9SMatthew Dillon 			case 2:
123248eadef9SMatthew Dillon 				xav[xac++] = "-vv";
123348eadef9SMatthew Dillon 				break;
123448eadef9SMatthew Dillon 			default:
123548eadef9SMatthew Dillon 				xav[xac++] = "-vvv";
123648eadef9SMatthew Dillon 				break;
123748eadef9SMatthew Dillon 			}
123807485271SMichael Neumann 			if (ForceYesOpt) {
123907485271SMichael Neumann 				xav[xac++] = "-y";
124007485271SMichael Neumann 			}
1241243ca327SMatthew Dillon 			xav[xac++] = "-2";
1242243ca327SMatthew Dillon 			xav[xac++] = "mirror-write";
1243e2c596b1SChris Turner 			xav[xac++] = rfs;
1244243ca327SMatthew Dillon 			xav[xac++] = NULL;
1245e2c596b1SChris Turner 			execvp(*xav, (void *)xav);
124634ebae70SMatthew Dillon 		} else {
124734ebae70SMatthew Dillon 			hammer_cmd_mirror_write(av + 1, 1);
1248243ca327SMatthew Dillon 			fflush(stdout);
1249243ca327SMatthew Dillon 			fflush(stderr);
125034ebae70SMatthew Dillon 		}
125153d93cc7SMatthew Dillon 		_exit(1);
125234ebae70SMatthew Dillon 	}
125334ebae70SMatthew Dillon 	close(fds[0]);
125434ebae70SMatthew Dillon 	close(fds[1]);
125534ebae70SMatthew Dillon 
125634ebae70SMatthew Dillon 	while (waitpid(pid1, NULL, 0) <= 0)
125734ebae70SMatthew Dillon 		;
125834ebae70SMatthew Dillon 	while (waitpid(pid2, NULL, 0) <= 0)
125934ebae70SMatthew Dillon 		;
1260e7f926a5SMatthew Dillon 
1261e7f926a5SMatthew Dillon 	/*
1262e7f926a5SMatthew Dillon 	 * If the link is lost restart
1263e7f926a5SMatthew Dillon 	 */
1264e7f926a5SMatthew Dillon 	if (streaming) {
1265e7f926a5SMatthew Dillon 		if (VerboseOpt) {
1266e7f926a5SMatthew Dillon 			fprintf(stderr, "\nLost Link\n");
1267e7f926a5SMatthew Dillon 			fflush(stderr);
1268e7f926a5SMatthew Dillon 		}
12690bd7a37cSMatthew Dillon 		sleep(15 + DelayOpt);
1270e7f926a5SMatthew Dillon 		goto again;
1271e7f926a5SMatthew Dillon 	}
1272e7f926a5SMatthew Dillon 
1273a7fbbf91SMatthew Dillon }
1274a7fbbf91SMatthew Dillon 
1275243ca327SMatthew Dillon /*
1276243ca327SMatthew Dillon  * Read and return multiple mrecords
1277243ca327SMatthew Dillon  */
1278a7fbbf91SMatthew Dillon static int
127917dd83bcSMatthew Dillon read_mrecords(int fd, char *buf, u_int size, hammer_ioc_mrecord_head_t pickup)
1280a7fbbf91SMatthew Dillon {
128117dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1282a7fbbf91SMatthew Dillon 	u_int count;
1283a7fbbf91SMatthew Dillon 	size_t n;
1284a7fbbf91SMatthew Dillon 	size_t i;
128517dd83bcSMatthew Dillon 	size_t bytes;
128685a8e8a7SMatthew Dillon 	int type;
1287a7fbbf91SMatthew Dillon 
1288a7fbbf91SMatthew Dillon 	count = 0;
1289a7fbbf91SMatthew Dillon 	while (size - count >= HAMMER_MREC_HEADSIZE) {
1290a7fbbf91SMatthew Dillon 		/*
1291a7fbbf91SMatthew Dillon 		 * Cached the record header in case we run out of buffer
1292a7fbbf91SMatthew Dillon 		 * space.
1293a7fbbf91SMatthew Dillon 		 */
129417dd83bcSMatthew Dillon 		fflush(stdout);
1295a7fbbf91SMatthew Dillon 		if (pickup->signature == 0) {
1296a7fbbf91SMatthew Dillon 			for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1297a7fbbf91SMatthew Dillon 				i = read(fd, (char *)pickup + n,
1298a7fbbf91SMatthew Dillon 					 HAMMER_MREC_HEADSIZE - n);
1299a7fbbf91SMatthew Dillon 				if (i <= 0)
1300a7fbbf91SMatthew Dillon 					break;
1301a7fbbf91SMatthew Dillon 			}
1302a7fbbf91SMatthew Dillon 			if (n == 0)
1303a7fbbf91SMatthew Dillon 				break;
1304a7fbbf91SMatthew Dillon 			if (n != HAMMER_MREC_HEADSIZE) {
1305a7fbbf91SMatthew Dillon 				fprintf(stderr, "read_mrecords: short read on pipe\n");
1306a7fbbf91SMatthew Dillon 				exit(1);
1307a7fbbf91SMatthew Dillon 			}
1308a7fbbf91SMatthew Dillon 			if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
130934bb69d8SThomas Nikolajsen 				fprintf(stderr, "read_mrecords: malformed record on pipe, "
131034bb69d8SThomas Nikolajsen 					"bad signature\n");
1311a7fbbf91SMatthew Dillon 				exit(1);
1312a7fbbf91SMatthew Dillon 			}
1313a7fbbf91SMatthew Dillon 		}
1314a7fbbf91SMatthew Dillon 		if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
131517dd83bcSMatthew Dillon 		    pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
131634bb69d8SThomas Nikolajsen 			fprintf(stderr, "read_mrecords: malformed record on pipe, "
131734bb69d8SThomas Nikolajsen 				"illegal rec_size\n");
1318a7fbbf91SMatthew Dillon 			exit(1);
1319a7fbbf91SMatthew Dillon 		}
1320a7fbbf91SMatthew Dillon 
1321a7fbbf91SMatthew Dillon 		/*
1322a7fbbf91SMatthew Dillon 		 * Stop if we have insufficient space for the record and data.
1323a7fbbf91SMatthew Dillon 		 */
132417dd83bcSMatthew Dillon 		bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
132517dd83bcSMatthew Dillon 		if (size - count < bytes)
1326a7fbbf91SMatthew Dillon 			break;
1327a7fbbf91SMatthew Dillon 
1328a7fbbf91SMatthew Dillon 		/*
132985a8e8a7SMatthew Dillon 		 * Stop if the record type is not a REC, SKIP, or PASS,
133085a8e8a7SMatthew Dillon 		 * which are the only types the ioctl supports.  Other types
133185a8e8a7SMatthew Dillon 		 * are used only by the userland protocol.
133285a8e8a7SMatthew Dillon 		 *
133385a8e8a7SMatthew Dillon 		 * Ignore all flags.
1334243ca327SMatthew Dillon 		 */
133585a8e8a7SMatthew Dillon 		type = pickup->type & HAMMER_MRECF_TYPE_LOMASK;
133685a8e8a7SMatthew Dillon 		if (type != HAMMER_MREC_TYPE_PFSD &&
133785a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_REC &&
133885a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_SKIP &&
133985a8e8a7SMatthew Dillon 		    type != HAMMER_MREC_TYPE_PASS) {
1340243ca327SMatthew Dillon 			break;
134117dd83bcSMatthew Dillon 		}
1342243ca327SMatthew Dillon 
1343243ca327SMatthew Dillon 		/*
1344a7fbbf91SMatthew Dillon 		 * Read the remainder and clear the pickup signature.
1345a7fbbf91SMatthew Dillon 		 */
134617dd83bcSMatthew Dillon 		for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
134717dd83bcSMatthew Dillon 			i = read(fd, buf + count + n, bytes - n);
1348a7fbbf91SMatthew Dillon 			if (i <= 0)
1349a7fbbf91SMatthew Dillon 				break;
1350a7fbbf91SMatthew Dillon 		}
135117dd83bcSMatthew Dillon 		if (n != bytes) {
1352a7fbbf91SMatthew Dillon 			fprintf(stderr, "read_mrecords: short read on pipe\n");
1353a7fbbf91SMatthew Dillon 			exit(1);
1354a7fbbf91SMatthew Dillon 		}
135517dd83bcSMatthew Dillon 
135617dd83bcSMatthew Dillon 		bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
135717dd83bcSMatthew Dillon 		pickup->signature = 0;
135817dd83bcSMatthew Dillon 		pickup->type = 0;
135917dd83bcSMatthew Dillon 		mrec = (void *)(buf + count);
136017dd83bcSMatthew Dillon 
136117dd83bcSMatthew Dillon 		/*
136217dd83bcSMatthew Dillon 		 * Validate the completed record
136317dd83bcSMatthew Dillon 		 */
136417dd83bcSMatthew Dillon 		if (mrec->head.rec_crc !=
136517dd83bcSMatthew Dillon 		    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
136617dd83bcSMatthew Dillon 			  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
136717dd83bcSMatthew Dillon 			fprintf(stderr, "read_mrecords: malformed record "
136817dd83bcSMatthew Dillon 					"on pipe, bad crc\n");
136917dd83bcSMatthew Dillon 			exit(1);
1370a7fbbf91SMatthew Dillon 		}
1371a7fbbf91SMatthew Dillon 
137217dd83bcSMatthew Dillon 		/*
137385a8e8a7SMatthew Dillon 		 * If its a B-Tree record validate the data crc.
137485a8e8a7SMatthew Dillon 		 *
137585a8e8a7SMatthew Dillon 		 * NOTE: If the VFS passes us an explicitly errorde mrec
137685a8e8a7SMatthew Dillon 		 *	 we just pass it through.
137717dd83bcSMatthew Dillon 		 */
137885a8e8a7SMatthew Dillon 		type = mrec->head.type & HAMMER_MRECF_TYPE_MASK;
137985a8e8a7SMatthew Dillon 
138085a8e8a7SMatthew Dillon 		if (type == HAMMER_MREC_TYPE_REC) {
138117dd83bcSMatthew Dillon 			if (mrec->head.rec_size <
138217dd83bcSMatthew Dillon 			    sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
138317dd83bcSMatthew Dillon 				fprintf(stderr,
138417dd83bcSMatthew Dillon 					"read_mrecords: malformed record on "
138517dd83bcSMatthew Dillon 					"pipe, illegal element data_len\n");
138617dd83bcSMatthew Dillon 				exit(1);
138717dd83bcSMatthew Dillon 			}
138817dd83bcSMatthew Dillon 			if (mrec->rec.leaf.data_len &&
138917dd83bcSMatthew Dillon 			    mrec->rec.leaf.data_offset &&
139017dd83bcSMatthew Dillon 			    hammer_crc_test_leaf(&mrec->rec + 1, &mrec->rec.leaf) == 0) {
139117dd83bcSMatthew Dillon 				fprintf(stderr,
139217dd83bcSMatthew Dillon 					"read_mrecords: data_crc did not "
1393a276dc6bSMatthew Dillon 					"match data! obj=%016jx key=%016jx\n",
1394a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.obj_id,
1395a276dc6bSMatthew Dillon 					(uintmax_t)mrec->rec.leaf.base.key);
139617dd83bcSMatthew Dillon 				fprintf(stderr,
139717dd83bcSMatthew Dillon 					"continuing, but there are problems\n");
139817dd83bcSMatthew Dillon 			}
139917dd83bcSMatthew Dillon 		}
140017dd83bcSMatthew Dillon 		count += bytes;
1401a7fbbf91SMatthew Dillon 	}
1402a7fbbf91SMatthew Dillon 	return(count);
1403a7fbbf91SMatthew Dillon }
1404a7fbbf91SMatthew Dillon 
140534ebae70SMatthew Dillon /*
140617dd83bcSMatthew Dillon  * Read and return a single mrecord.
1407243ca327SMatthew Dillon  */
1408243ca327SMatthew Dillon static
140917dd83bcSMatthew Dillon hammer_ioc_mrecord_any_t
141017dd83bcSMatthew Dillon read_mrecord(int fdin, int *errorp, hammer_ioc_mrecord_head_t pickup)
1411243ca327SMatthew Dillon {
141217dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
141317dd83bcSMatthew Dillon 	struct hammer_ioc_mrecord_head mrechd;
1414243ca327SMatthew Dillon 	size_t bytes;
1415243ca327SMatthew Dillon 	size_t n;
1416243ca327SMatthew Dillon 	size_t i;
1417243ca327SMatthew Dillon 
1418243ca327SMatthew Dillon 	if (pickup && pickup->type != 0) {
1419243ca327SMatthew Dillon 		mrechd = *pickup;
1420243ca327SMatthew Dillon 		pickup->signature = 0;
1421243ca327SMatthew Dillon 		pickup->type = 0;
1422243ca327SMatthew Dillon 		n = HAMMER_MREC_HEADSIZE;
1423243ca327SMatthew Dillon 	} else {
1424243ca327SMatthew Dillon 		/*
1425243ca327SMatthew Dillon 		 * Read in the PFSD header from the sender.
1426243ca327SMatthew Dillon 		 */
1427243ca327SMatthew Dillon 		for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
1428243ca327SMatthew Dillon 			i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
1429243ca327SMatthew Dillon 			if (i <= 0)
1430243ca327SMatthew Dillon 				break;
1431243ca327SMatthew Dillon 		}
1432243ca327SMatthew Dillon 		if (n == 0) {
1433243ca327SMatthew Dillon 			*errorp = 0;	/* EOF */
1434243ca327SMatthew Dillon 			return(NULL);
1435243ca327SMatthew Dillon 		}
1436243ca327SMatthew Dillon 		if (n != HAMMER_MREC_HEADSIZE) {
1437243ca327SMatthew Dillon 			fprintf(stderr, "short read of mrecord header\n");
1438243ca327SMatthew Dillon 			*errorp = EPIPE;
1439243ca327SMatthew Dillon 			return(NULL);
1440243ca327SMatthew Dillon 		}
1441243ca327SMatthew Dillon 	}
1442243ca327SMatthew Dillon 	if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
1443243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad signature\n");
1444243ca327SMatthew Dillon 		*errorp = EINVAL;
1445243ca327SMatthew Dillon 		return(NULL);
1446243ca327SMatthew Dillon 	}
144717dd83bcSMatthew Dillon 	bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
144817dd83bcSMatthew Dillon 	assert(bytes >= sizeof(mrechd));
1449243ca327SMatthew Dillon 	mrec = malloc(bytes);
145017dd83bcSMatthew Dillon 	mrec->head = mrechd;
145117dd83bcSMatthew Dillon 
1452243ca327SMatthew Dillon 	while (n < bytes) {
1453243ca327SMatthew Dillon 		i = read(fdin, (char *)mrec + n, bytes - n);
1454243ca327SMatthew Dillon 		if (i <= 0)
1455243ca327SMatthew Dillon 			break;
1456243ca327SMatthew Dillon 		n += i;
1457243ca327SMatthew Dillon 	}
1458243ca327SMatthew Dillon 	if (n != bytes) {
1459243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: short read on payload\n");
1460243ca327SMatthew Dillon 		*errorp = EPIPE;
1461243ca327SMatthew Dillon 		return(NULL);
1462243ca327SMatthew Dillon 	}
146317dd83bcSMatthew Dillon 	if (mrec->head.rec_crc !=
146417dd83bcSMatthew Dillon 	    crc32((char *)mrec + HAMMER_MREC_CRCOFF,
146517dd83bcSMatthew Dillon 		  mrec->head.rec_size - HAMMER_MREC_CRCOFF)) {
1466243ca327SMatthew Dillon 		fprintf(stderr, "read_mrecord: bad CRC\n");
1467243ca327SMatthew Dillon 		*errorp = EINVAL;
1468243ca327SMatthew Dillon 		return(NULL);
1469243ca327SMatthew Dillon 	}
1470243ca327SMatthew Dillon 	*errorp = 0;
1471243ca327SMatthew Dillon 	return(mrec);
1472243ca327SMatthew Dillon }
1473243ca327SMatthew Dillon 
1474243ca327SMatthew Dillon static
1475243ca327SMatthew Dillon void
147617dd83bcSMatthew Dillon write_mrecord(int fdout, u_int32_t type, hammer_ioc_mrecord_any_t mrec,
147717dd83bcSMatthew Dillon 	      int bytes)
1478243ca327SMatthew Dillon {
147917dd83bcSMatthew Dillon 	char zbuf[HAMMER_HEAD_ALIGN];
148017dd83bcSMatthew Dillon 	int pad;
1481243ca327SMatthew Dillon 
148217dd83bcSMatthew Dillon 	pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
148317dd83bcSMatthew Dillon 
148417dd83bcSMatthew Dillon 	assert(bytes >= (int)sizeof(mrec->head));
148517dd83bcSMatthew Dillon 	bzero(&mrec->head, sizeof(mrec->head));
148617dd83bcSMatthew Dillon 	mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
148717dd83bcSMatthew Dillon 	mrec->head.type = type;
148817dd83bcSMatthew Dillon 	mrec->head.rec_size = bytes;
148917dd83bcSMatthew Dillon 	mrec->head.rec_crc = crc32((char *)mrec + HAMMER_MREC_CRCOFF,
149017dd83bcSMatthew Dillon 				   bytes - HAMMER_MREC_CRCOFF);
149117dd83bcSMatthew Dillon 	if (write(fdout, mrec, bytes) != bytes) {
1492243ca327SMatthew Dillon 		fprintf(stderr, "write_mrecord: error %d (%s)\n",
1493243ca327SMatthew Dillon 			errno, strerror(errno));
1494243ca327SMatthew Dillon 		exit(1);
1495243ca327SMatthew Dillon 	}
149617dd83bcSMatthew Dillon 	if (pad) {
149717dd83bcSMatthew Dillon 		bzero(zbuf, pad);
149817dd83bcSMatthew Dillon 		if (write(fdout, zbuf, pad) != pad) {
149917dd83bcSMatthew Dillon 			fprintf(stderr, "write_mrecord: error %d (%s)\n",
150017dd83bcSMatthew Dillon 				errno, strerror(errno));
150117dd83bcSMatthew Dillon 			exit(1);
150217dd83bcSMatthew Dillon 		}
150317dd83bcSMatthew Dillon 	}
1504243ca327SMatthew Dillon }
1505243ca327SMatthew Dillon 
1506243ca327SMatthew Dillon /*
150734ebae70SMatthew Dillon  * Generate a mirroring header with the pfs information of the
150834ebae70SMatthew Dillon  * originating filesytem.
150934ebae70SMatthew Dillon  */
151034ebae70SMatthew Dillon static void
1511e7f926a5SMatthew Dillon generate_mrec_header(int fd, int pfs_id,
1512e7f926a5SMatthew Dillon 		     union hammer_ioc_mrecord_any *mrec_tmp)
151334ebae70SMatthew Dillon {
151434ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
151534ebae70SMatthew Dillon 
151634ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1517e7f926a5SMatthew Dillon 	bzero(mrec_tmp, sizeof(*mrec_tmp));
1518d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1519e7f926a5SMatthew Dillon 	pfs.ondisk = &mrec_tmp->pfs.pfsd;
1520e7f926a5SMatthew Dillon 	pfs.bytes = sizeof(mrec_tmp->pfs.pfsd);
152134ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
15229dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: not a HAMMER fs/pseudofs!\n");
152334ebae70SMatthew Dillon 		exit(1);
152434ebae70SMatthew Dillon 	}
152534ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
15269dc76cb1SMatthew Dillon 		fprintf(stderr, "Mirror-read: HAMMER pfs version mismatch!\n");
152734ebae70SMatthew Dillon 		exit(1);
152834ebae70SMatthew Dillon 	}
1529e7f926a5SMatthew Dillon 	mrec_tmp->pfs.version = pfs.version;
153034ebae70SMatthew Dillon }
153134ebae70SMatthew Dillon 
153234ebae70SMatthew Dillon /*
153334ebae70SMatthew Dillon  * Validate the pfs information from the originating filesystem
153434ebae70SMatthew Dillon  * against the target filesystem.  shared_uuid must match.
153548eadef9SMatthew Dillon  *
153648eadef9SMatthew Dillon  * return -1 if we got a TERM record
153734ebae70SMatthew Dillon  */
153848eadef9SMatthew Dillon static int
1539d4e5b69bSMatthew Dillon validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
154048eadef9SMatthew Dillon 		     struct hammer_ioc_mrecord_head *pickup,
154134ebae70SMatthew Dillon 		     hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
154234ebae70SMatthew Dillon {
154334ebae70SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
154434ebae70SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
154517dd83bcSMatthew Dillon 	hammer_ioc_mrecord_any_t mrec;
1546243ca327SMatthew Dillon 	int error;
154734ebae70SMatthew Dillon 
154834ebae70SMatthew Dillon 	/*
154934ebae70SMatthew Dillon 	 * Get the PFSD info from the target filesystem.
155034ebae70SMatthew Dillon 	 */
155134ebae70SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
155234ebae70SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1553d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
155434ebae70SMatthew Dillon 	pfs.ondisk = &pfsd;
155534ebae70SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
155634ebae70SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
155734ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: not a HAMMER fs/pseudofs!\n");
155834ebae70SMatthew Dillon 		exit(1);
155934ebae70SMatthew Dillon 	}
156034ebae70SMatthew Dillon 	if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
156134ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: HAMMER pfs version mismatch!\n");
156234ebae70SMatthew Dillon 		exit(1);
156334ebae70SMatthew Dillon 	}
156434ebae70SMatthew Dillon 
156548eadef9SMatthew Dillon 	mrec = read_mrecord(fdin, &error, pickup);
1566243ca327SMatthew Dillon 	if (mrec == NULL) {
1567243ca327SMatthew Dillon 		if (error == 0)
1568243ca327SMatthew Dillon 			fprintf(stderr, "validate_mrec_header: short read\n");
156934ebae70SMatthew Dillon 		exit(1);
157034ebae70SMatthew Dillon 	}
157148eadef9SMatthew Dillon 	if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
157248eadef9SMatthew Dillon 		free(mrec);
157348eadef9SMatthew Dillon 		return(-1);
157448eadef9SMatthew Dillon 	}
157548eadef9SMatthew Dillon 
157617dd83bcSMatthew Dillon 	if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
1577243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: did not get expected "
1578243ca327SMatthew Dillon 				"PFSD record type\n");
157934ebae70SMatthew Dillon 		exit(1);
158034ebae70SMatthew Dillon 	}
158117dd83bcSMatthew Dillon 	if (mrec->head.rec_size != sizeof(mrec->pfs)) {
1582243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: unexpected payload "
1583243ca327SMatthew Dillon 				"size\n");
158434ebae70SMatthew Dillon 		exit(1);
158534ebae70SMatthew Dillon 	}
158617dd83bcSMatthew Dillon 	if (mrec->pfs.version != pfs.version) {
1587243ca327SMatthew Dillon 		fprintf(stderr, "validate_mrec_header: Version mismatch\n");
158834ebae70SMatthew Dillon 		exit(1);
158934ebae70SMatthew Dillon 	}
159034ebae70SMatthew Dillon 
159134ebae70SMatthew Dillon 	/*
159234ebae70SMatthew Dillon 	 * Whew.  Ok, is the read PFS info compatible with the target?
159334ebae70SMatthew Dillon 	 */
159417dd83bcSMatthew Dillon 	if (bcmp(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid,
159517dd83bcSMatthew Dillon 		 sizeof(pfsd.shared_uuid)) != 0) {
159617dd83bcSMatthew Dillon 		fprintf(stderr,
159717dd83bcSMatthew Dillon 			"mirror-write: source and target have "
1598f265b84fSSascha Wildner 			"different shared-uuid's!\n");
159934ebae70SMatthew Dillon 		exit(1);
160034ebae70SMatthew Dillon 	}
1601d4e5b69bSMatthew Dillon 	if (is_target &&
1602d4e5b69bSMatthew Dillon 	    (pfsd.mirror_flags & HAMMER_PFSD_SLAVE) == 0) {
160334ebae70SMatthew Dillon 		fprintf(stderr, "mirror-write: target must be in slave mode\n");
160434ebae70SMatthew Dillon 		exit(1);
160534ebae70SMatthew Dillon 	}
1606d4e5b69bSMatthew Dillon 	if (tid_begp)
160717dd83bcSMatthew Dillon 		*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
1608d4e5b69bSMatthew Dillon 	if (tid_endp)
160917dd83bcSMatthew Dillon 		*tid_endp = mrec->pfs.pfsd.sync_end_tid;
1610243ca327SMatthew Dillon 	free(mrec);
161148eadef9SMatthew Dillon 	return(0);
161234ebae70SMatthew Dillon }
161334ebae70SMatthew Dillon 
161434ebae70SMatthew Dillon static void
1615d4e5b69bSMatthew Dillon update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
161634ebae70SMatthew Dillon {
1617243ca327SMatthew Dillon 	struct hammer_ioc_pseudofs_rw pfs;
1618243ca327SMatthew Dillon 	struct hammer_pseudofs_data pfsd;
161934ebae70SMatthew Dillon 
1620243ca327SMatthew Dillon 	bzero(&pfs, sizeof(pfs));
1621243ca327SMatthew Dillon 	bzero(&pfsd, sizeof(pfsd));
1622d4e5b69bSMatthew Dillon 	pfs.pfs_id = pfs_id;
1623243ca327SMatthew Dillon 	pfs.ondisk = &pfsd;
1624243ca327SMatthew Dillon 	pfs.bytes = sizeof(pfsd);
1625243ca327SMatthew Dillon 	if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
1626243ca327SMatthew Dillon 		perror("update_pfs_snapshot (read)");
1627243ca327SMatthew Dillon 		exit(1);
162834ebae70SMatthew Dillon 	}
16299c67b4d2SMatthew Dillon 	if (pfsd.sync_end_tid != snapshot_tid) {
1630ddc8e722SMatthew Dillon 		pfsd.sync_end_tid = snapshot_tid;
1631243ca327SMatthew Dillon 		if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
1632243ca327SMatthew Dillon 			perror("update_pfs_snapshot (rewrite)");
1633243ca327SMatthew Dillon 			exit(1);
163434ebae70SMatthew Dillon 		}
163548eadef9SMatthew Dillon 		if (VerboseOpt >= 2) {
16369dc76cb1SMatthew Dillon 			fprintf(stderr,
1637ced4470dSMatthew Dillon 				"Mirror-write: Completed, updated snapshot "
1638a276dc6bSMatthew Dillon 				"to %016jx\n",
1639a276dc6bSMatthew Dillon 				(uintmax_t)snapshot_tid);
1640ced4470dSMatthew Dillon 			fflush(stderr);
1641243ca327SMatthew Dillon 		}
16429c67b4d2SMatthew Dillon 	}
164348eadef9SMatthew Dillon }
1644243ca327SMatthew Dillon 
164548eadef9SMatthew Dillon /*
164648eadef9SMatthew Dillon  * Bandwidth-limited write in chunks
164748eadef9SMatthew Dillon  */
164848eadef9SMatthew Dillon static
164948eadef9SMatthew Dillon ssize_t
165048eadef9SMatthew Dillon writebw(int fd, const void *buf, size_t nbytes,
165148eadef9SMatthew Dillon 	u_int64_t *bwcount, struct timeval *tv1)
165248eadef9SMatthew Dillon {
165348eadef9SMatthew Dillon 	struct timeval tv2;
165448eadef9SMatthew Dillon 	size_t n;
165548eadef9SMatthew Dillon 	ssize_t r;
165648eadef9SMatthew Dillon 	ssize_t a;
165748eadef9SMatthew Dillon 	int usec;
165848eadef9SMatthew Dillon 
165948eadef9SMatthew Dillon 	a = 0;
166048eadef9SMatthew Dillon 	r = 0;
166148eadef9SMatthew Dillon 	while (nbytes) {
166248eadef9SMatthew Dillon 		if (*bwcount + nbytes > BandwidthOpt)
166348eadef9SMatthew Dillon 			n = BandwidthOpt - *bwcount;
166448eadef9SMatthew Dillon 		else
166548eadef9SMatthew Dillon 			n = nbytes;
166648eadef9SMatthew Dillon 		if (n)
166748eadef9SMatthew Dillon 			r = write(fd, buf, n);
166848eadef9SMatthew Dillon 		if (r >= 0) {
166948eadef9SMatthew Dillon 			a += r;
167048eadef9SMatthew Dillon 			nbytes -= r;
167148eadef9SMatthew Dillon 			buf = (const char *)buf + r;
167248eadef9SMatthew Dillon 		}
167348eadef9SMatthew Dillon 		if ((size_t)r != n)
167448eadef9SMatthew Dillon 			break;
167548eadef9SMatthew Dillon 		*bwcount += n;
167648eadef9SMatthew Dillon 		if (*bwcount >= BandwidthOpt) {
167748eadef9SMatthew Dillon 			gettimeofday(&tv2, NULL);
167848eadef9SMatthew Dillon 			usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 +
167948eadef9SMatthew Dillon 				(int)(tv2.tv_usec - tv1->tv_usec);
168048eadef9SMatthew Dillon 			if (usec >= 0 && usec < 1000000)
168148eadef9SMatthew Dillon 				usleep(1000000 - usec);
168248eadef9SMatthew Dillon 			gettimeofday(tv1, NULL);
168348eadef9SMatthew Dillon 			*bwcount -= BandwidthOpt;
168448eadef9SMatthew Dillon 		}
168548eadef9SMatthew Dillon 	}
168648eadef9SMatthew Dillon 	return(a ? a : r);
168748eadef9SMatthew Dillon }
168834ebae70SMatthew Dillon 
168901a72c9fSMatthew Dillon /*
169001a72c9fSMatthew Dillon  * Get a yes or no answer from the terminal.  The program may be run as
169101a72c9fSMatthew Dillon  * part of a two-way pipe so we cannot use stdin for this operation.
169201a72c9fSMatthew Dillon  */
169301a72c9fSMatthew Dillon static int
169401a72c9fSMatthew Dillon getyn(void)
169501a72c9fSMatthew Dillon {
169601a72c9fSMatthew Dillon 	char buf[256];
169701a72c9fSMatthew Dillon 	FILE *fp;
169801a72c9fSMatthew Dillon 	int result;
169901a72c9fSMatthew Dillon 
170001a72c9fSMatthew Dillon 	fp = fopen("/dev/tty", "r");
170101a72c9fSMatthew Dillon 	if (fp == NULL) {
170201a72c9fSMatthew Dillon 		fprintf(stderr, "No terminal for response\n");
170301a72c9fSMatthew Dillon 		return(-1);
170401a72c9fSMatthew Dillon 	}
170501a72c9fSMatthew Dillon 	result = -1;
170601a72c9fSMatthew Dillon 	while (fgets(buf, sizeof(buf), fp) != NULL) {
170701a72c9fSMatthew Dillon 		if (buf[0] == 'y' || buf[0] == 'Y') {
170801a72c9fSMatthew Dillon 			result = 1;
170901a72c9fSMatthew Dillon 			break;
171001a72c9fSMatthew Dillon 		}
171101a72c9fSMatthew Dillon 		if (buf[0] == 'n' || buf[0] == 'N') {
171201a72c9fSMatthew Dillon 			result = 0;
171301a72c9fSMatthew Dillon 			break;
171401a72c9fSMatthew Dillon 		}
171501a72c9fSMatthew Dillon 		fprintf(stderr, "Response not understood\n");
171601a72c9fSMatthew Dillon 		break;
171701a72c9fSMatthew Dillon 	}
171801a72c9fSMatthew Dillon 	fclose(fp);
171901a72c9fSMatthew Dillon 	return(result);
172001a72c9fSMatthew Dillon }
172101a72c9fSMatthew Dillon 
1722a7fbbf91SMatthew Dillon static void
1723a7fbbf91SMatthew Dillon mirror_usage(int code)
1724a7fbbf91SMatthew Dillon {
1725a7fbbf91SMatthew Dillon 	fprintf(stderr,
172634bb69d8SThomas Nikolajsen 		"hammer mirror-read <filesystem> [begin-tid]\n"
172734bb69d8SThomas Nikolajsen 		"hammer mirror-read-stream <filesystem> [begin-tid]\n"
1728a7fbbf91SMatthew Dillon 		"hammer mirror-write <filesystem>\n"
17299f1b0121SAntonio Huete Jimenez 		"hammer mirror-dump [header]\n"
173034bb69d8SThomas Nikolajsen 		"hammer mirror-copy [[user@]host:]<filesystem>"
173134bb69d8SThomas Nikolajsen 				  " [[user@]host:]<filesystem>\n"
173234bb69d8SThomas Nikolajsen 		"hammer mirror-stream [[user@]host:]<filesystem>"
173334bb69d8SThomas Nikolajsen 				    " [[user@]host:]<filesystem>\n"
1734a7fbbf91SMatthew Dillon 	);
1735a7fbbf91SMatthew Dillon 	exit(code);
1736a7fbbf91SMatthew Dillon }
173701a72c9fSMatthew Dillon 
1738