#include "hammer.h"
#define LINE1 0,20
#define LINE2 20,78
#define LINE3 90,70
#define SERIALBUF_SIZE (512 * 1024)
typedef struct histogram {
hammer_tid_t tid;
uint64_t bytes;
} *histogram_t;
const char *ScoreBoardFile;
const char *RestrictTarget;
static int read_mrecords(int fd, char *buf, u_int size,
struct hammer_ioc_mrecord_head *pickup);
static int generate_histogram(int fd, const char *filesystem,
histogram_t *histogram_ary,
struct hammer_ioc_mirror_rw *mirror_base,
int *repeatp);
static hammer_ioc_mrecord_any_t read_mrecord(int fdin, int *errorp,
struct hammer_ioc_mrecord_head *pickup);
static void write_mrecord(int fdout, uint32_t type,
hammer_ioc_mrecord_any_t mrec, int bytes);
static void generate_mrec_header(int fd, int pfs_id,
union hammer_ioc_mrecord_any *mrec_tmp);
static int validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
struct hammer_ioc_mrecord_head *pickup,
hammer_tid_t *tid_begp, hammer_tid_t *tid_endp);
static void update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id);
static ssize_t writebw(int fd, const void *buf, size_t nbytes,
uint64_t *bwcount, struct timeval *tv1);
static int getyntty(void);
static void score_printf(size_t i, size_t w, const char *ctl, ...);
static void hammer_check_restrict(const char *filesystem);
static void mirror_usage(int code);
void
hammer_cmd_mirror_read(char **av, int ac, int streaming)
{
struct hammer_ioc_mirror_rw mirror;
struct hammer_ioc_pseudofs_rw pfs;
union hammer_ioc_mrecord_any mrec_tmp;
struct hammer_ioc_mrecord_head pickup;
hammer_ioc_mrecord_any_t mrec;
hammer_tid_t sync_tid;
histogram_t histogram_ary;
const char *filesystem;
char *buf = malloc(SERIALBUF_SIZE);
int interrupted = 0;
int error;
int fd;
int n;
int didwork;
int histogram;
int histindex;
int histmax;
int repeat = 0;
int sameline;
int64_t total_bytes;
time_t base_t = time(NULL);
struct timeval bwtv;
uint64_t bwcount;
uint64_t estbytes;
if (ac == 0 || ac > 2) {
mirror_usage(1);
}
filesystem = av[0];
hammer_check_restrict(filesystem);
pickup.signature = 0;
pickup.type = 0;
histogram = 0;
histindex = 0;
histmax = 0;
histogram_ary = NULL;
sameline = 0;
again:
bzero(&mirror, sizeof(mirror));
hammer_key_beg_init(&mirror.key_beg);
hammer_key_end_init(&mirror.key_end);
fd = getpfs(&pfs, filesystem);
if (streaming >= 0)
score_printf(LINE1, "Running");
if (streaming >= 0 && VerboseOpt && VerboseOpt < 2) {
fprintf(stderr, "%cRunning \b\b", (sameline ? '\r' : '\n'));
fflush(stderr);
sameline = 1;
}
sameline = 1;
total_bytes = 0;
gettimeofday(&bwtv, NULL);
bwcount = 0;
generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
&mrec_tmp, sizeof(mrec_tmp.pfs));
if (TwoWayPipeOpt) {
mirror.tid_beg = 0;
n = validate_mrec_header(fd, 0, 0, pfs.pfs_id, &pickup,
NULL, &mirror.tid_beg);
if (n < 0) {
relpfs(fd, &pfs);
free(buf);
free(histogram_ary);
return;
}
++mirror.tid_beg;
} else if (streaming && histogram) {
mirror.tid_beg = histogram_ary[histindex].tid + 1;
} else {
mirror.tid_beg = 0;
}
generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
mirror.ubuf = buf;
mirror.size = SERIALBUF_SIZE;
mirror.pfs_id = pfs.pfs_id;
mirror.shared_uuid = pfs.ondisk->shared_uuid;
#if 0
if (streaming && histogram && histindex == histend) {
if (mirror.tid_end - mirror.tid_beg > BULK_MINIMUM)
histogram = 0;
}
#endif
if (histogram == 0 && BulkOpt == 0) {
if (VerboseOpt && repeat == 0) {
fprintf(stderr, "\n");
sameline = 0;
}
histmax = generate_histogram(fd, filesystem,
&histogram_ary, &mirror,
&repeat);
histindex = 0;
histogram = 1;
if (streaming == 0)
streaming = -1;
}
if (streaming && histogram) {
++histindex;
mirror.tid_end = histogram_ary[histindex].tid;
estbytes = histogram_ary[histindex-1].bytes;
mrec_tmp.pfs.pfsd.sync_end_tid = mirror.tid_end;
} else {
estbytes = 0;
}
write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
&mrec_tmp, sizeof(mrec_tmp.pfs));
if (TwoWayPipeOpt == 0 && histogram == 0)
hammer_get_cycle(&mirror.key_beg, &mirror.tid_beg);
if (ac == 2)
mirror.tid_beg = strtoull(av[1], NULL, 0);
if (streaming == 0 || VerboseOpt >= 2) {
fprintf(stderr,
"Mirror-read: Mirror %016jx to %016jx",
(uintmax_t)mirror.tid_beg, (uintmax_t)mirror.tid_end);
if (histogram)
fprintf(stderr, " (bulk= %ju)", (uintmax_t)estbytes);
fprintf(stderr, "\n");
fflush(stderr);
}
if (mirror.key_beg.obj_id != (int64_t)HAMMER_MIN_OBJID) {
fprintf(stderr, "Mirror-read: Resuming at object %016jx\n",
(uintmax_t)mirror.key_beg.obj_id);
}
if (mirror.tid_beg >= mirror.tid_end) {
if (streaming == 0 || VerboseOpt >= 2)
fprintf(stderr, "Mirror-read: No work to do\n");
sleep(DelayOpt);
didwork = 0;
histogram = 0;
goto done;
}
didwork = 1;
mirror.ubuf = buf;
mirror.size = SERIALBUF_SIZE;
do {
mirror.count = 0;
mirror.pfs_id = pfs.pfs_id;
mirror.shared_uuid = pfs.ondisk->shared_uuid;
if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
score_printf(LINE3, "Mirror-read %s failed: %s",
filesystem, strerror(errno));
err(1, "Mirror-read %s failed", filesystem);
}
if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
score_printf(LINE3, "Mirror-read %s fatal error %d",
filesystem, mirror.head.error);
errx(1, "Mirror-read %s fatal error %d",
filesystem, mirror.head.error);
}
if (mirror.count) {
if (BandwidthOpt) {
n = writebw(1, mirror.ubuf, mirror.count,
&bwcount, &bwtv);
} else {
n = write(1, mirror.ubuf, mirror.count);
}
if (n != mirror.count) {
score_printf(LINE3,
"Mirror-read %s failed: "
"short write",
filesystem);
errx(1, "Mirror-read %s failed: short write",
filesystem);
}
}
total_bytes += mirror.count;
if (streaming && VerboseOpt) {
fprintf(stderr,
"\rscan obj=%016jx tids=%016jx:%016jx %11jd",
(uintmax_t)mirror.key_cur.obj_id,
(uintmax_t)mirror.tid_beg,
(uintmax_t)mirror.tid_end,
(intmax_t)total_bytes);
fflush(stderr);
sameline = 0;
} else if (streaming) {
score_printf(LINE2,
"obj=%016jx tids=%016jx:%016jx %11jd",
(uintmax_t)mirror.key_cur.obj_id,
(uintmax_t)mirror.tid_beg,
(uintmax_t)mirror.tid_end,
(intmax_t)total_bytes);
}
mirror.key_beg = mirror.key_cur;
if (TimeoutOpt &&
(unsigned)(time(NULL) - base_t) > (unsigned)TimeoutOpt) {
score_printf(LINE3,
"Mirror-read %s interrupted by timer at"
" %016jx",
filesystem,
(uintmax_t)mirror.key_cur.obj_id);
fprintf(stderr,
"Mirror-read %s interrupted by timer at"
" %016jx\n",
filesystem,
(uintmax_t)mirror.key_cur.obj_id);
interrupted = 1;
break;
}
} while (mirror.count != 0);
done:
if (streaming && VerboseOpt && sameline == 0) {
fprintf(stderr, "\n");
fflush(stderr);
sameline = 1;
}
if (interrupted == 0) {
if (didwork) {
write_mrecord(1, HAMMER_MREC_TYPE_SYNC,
&mrec_tmp, sizeof(mrec_tmp.sync));
} else {
write_mrecord(1, HAMMER_MREC_TYPE_IDLE,
&mrec_tmp, sizeof(mrec_tmp.sync));
}
}
if (TwoWayPipeOpt) {
mrec = read_mrecord(0, &error, &pickup);
if (mrec == NULL ||
mrec->head.type != HAMMER_MREC_TYPE_UPDATE ||
mrec->head.rec_size != sizeof(mrec->update)) {
errx(1, "mirror_read: Did not get final "
"acknowledgement packet from target");
}
if (interrupted) {
if (CyclePath) {
hammer_set_cycle(&mirror.key_cur,
mirror.tid_beg);
fprintf(stderr, "Cyclefile %s updated for "
"continuation\n", CyclePath);
}
} else {
sync_tid = mrec->update.tid;
if (CyclePath) {
hammer_key_beg_init(&mirror.key_beg);
hammer_set_cycle(&mirror.key_beg, sync_tid);
fprintf(stderr,
"Cyclefile %s updated to 0x%016jx\n",
CyclePath, (uintmax_t)sync_tid);
}
}
free(mrec);
} else if (CyclePath) {
fprintf(stderr, "Warning: cycle file (-c option) cannot be "
"fully updated unless you use mirror-copy\n");
hammer_set_cycle(&mirror.key_beg, mirror.tid_beg);
}
if (streaming && interrupted == 0) {
time_t t1 = time(NULL);
time_t t2;
if (streaming && histogram) {
if (histindex != histmax) {
if (VerboseOpt && VerboseOpt < 2 &&
streaming >= 0) {
fprintf(stderr, " (bulk incremental)");
}
relpfs(fd, &pfs);
goto again;
}
}
if (VerboseOpt && streaming >= 0) {
fprintf(stderr, " W");
fflush(stderr);
} else if (streaming >= 0) {
score_printf(LINE1, "Waiting");
}
pfs.ondisk->sync_end_tid = mirror.tid_end;
if (streaming < 0) {
streaming = 0;
} else if (ioctl(fd, HAMMERIOC_WAI_PSEUDOFS, &pfs) < 0) {
score_printf(LINE3,
"Mirror-read %s: cannot stream: %s\n",
filesystem, strerror(errno));
fprintf(stderr,
"Mirror-read %s: cannot stream: %s\n",
filesystem, strerror(errno));
} else {
t2 = time(NULL) - t1;
if (t2 >= 0 && t2 < DelayOpt) {
if (VerboseOpt) {
fprintf(stderr, "\bD");
fflush(stderr);
}
sleep(DelayOpt - t2);
}
if (VerboseOpt) {
fprintf(stderr, "\b ");
fflush(stderr);
}
relpfs(fd, &pfs);
goto again;
}
}
write_mrecord(1, HAMMER_MREC_TYPE_TERM,
&mrec_tmp, sizeof(mrec_tmp.sync));
relpfs(fd, &pfs);
free(buf);
free(histogram_ary);
fprintf(stderr, "Mirror-read %s succeeded\n", filesystem);
}
#define HIST_COUNT (1024 * 1024)
static
int
generate_histogram(int fd, const char *filesystem,
histogram_t *histogram_ary,
struct hammer_ioc_mirror_rw *mirror_base,
int *repeatp)
{
struct hammer_ioc_mirror_rw mirror;
union hammer_ioc_mrecord_any *mrec;
hammer_tid_t tid_beg;
hammer_tid_t tid_end;
hammer_tid_t tid;
hammer_tid_t tidx;
uint64_t *tid_bytes;
uint64_t total;
uint64_t accum;
int chunkno;
int i;
int res;
int off;
int len;
mirror = *mirror_base;
tid_beg = mirror.tid_beg;
tid_end = mirror.tid_end;
mirror.head.flags |= HAMMER_IOC_MIRROR_NODATA;
if (*histogram_ary == NULL) {
*histogram_ary = malloc(sizeof(struct histogram) *
(HIST_COUNT + 2));
}
if (tid_beg >= tid_end)
return(0);
tid_bytes = malloc(sizeof(*tid_bytes) * (HIST_COUNT + 2));
bzero(tid_bytes, sizeof(*tid_bytes) * (HIST_COUNT + 2));
if (*repeatp == 0) {
fprintf(stderr, "Prescan to break up bulk transfer");
if (VerboseOpt > 1) {
fprintf(stderr, " (%juMB chunks)",
(uintmax_t)(SplitupOpt / (1024 * 1024)));
}
fprintf(stderr, "\n");
}
total = 0;
accum = 0;
chunkno = 0;
for (;;) {
mirror.count = 0;
if (ioctl(fd, HAMMERIOC_MIRROR_READ, &mirror) < 0) {
err(1, "Mirror-read %s failed", filesystem);
}
if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
errx(1, "Mirror-read %s fatal error %d",
filesystem, mirror.head.error);
}
for (off = 0;
off < mirror.count;
off += HAMMER_HEAD_DOALIGN(mrec->head.rec_size)) {
mrec = (void *)((char *)mirror.ubuf + off);
switch (mrec->head.type & HAMMER_MRECF_TYPE_LOMASK) {
case HAMMER_MREC_TYPE_REC:
case HAMMER_MREC_TYPE_PASS:
break;
default:
continue;
}
tid = mrec->rec.leaf.base.delete_tid;
if (tid && tid >= tid_beg && tid <= tid_end) {
len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
if (mrec->head.type ==
HAMMER_MREC_TYPE_REC) {
len -= HAMMER_HEAD_DOALIGN(
mrec->rec.leaf.data_len);
assert(len > 0);
}
i = (tid - tid_beg) * HIST_COUNT /
(tid_end - tid_beg);
tidx = tid_beg + i * (tid_end - tid_beg) /
HIST_COUNT;
if (tid == tidx && i)
--i;
assert(i >= 0 && i < HIST_COUNT);
tid_bytes[i] += len;
total += len;
accum += len;
}
tid = mrec->rec.leaf.base.create_tid;
if (tid && tid >= tid_beg && tid <= tid_end) {
len = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
if (mrec->head.type ==
HAMMER_MREC_TYPE_REC_NODATA) {
len += HAMMER_HEAD_DOALIGN(
mrec->rec.leaf.data_len);
}
i = (tid - tid_beg) * HIST_COUNT /
(tid_end - tid_beg);
tidx = tid_beg + i * (tid_end - tid_beg) /
HIST_COUNT;
if (tid == tidx && i)
--i;
assert(i >= 0 && i < HIST_COUNT);
tid_bytes[i] += len;
total += len;
accum += len;
}
}
if (*repeatp == 0 && accum > SplitupOpt) {
if (VerboseOpt > 1) {
fprintf(stderr, ".");
fflush(stderr);
}
++chunkno;
score_printf(LINE2, "Prescan chunk %d", chunkno);
accum = 0;
}
if (mirror.count == 0)
break;
mirror.key_beg = mirror.key_cur;
}
res = 0;
(*histogram_ary)[res].tid = tid_beg;
(*histogram_ary)[res].bytes = tid_bytes[0];
for (i = 1; i < HIST_COUNT; ++i) {
if ((*histogram_ary)[res].bytes >= SplitupOpt) {
++res;
(*histogram_ary)[res].tid = tid_beg +
i * (tid_end - tid_beg) /
HIST_COUNT;
(*histogram_ary)[res].bytes = 0;
}
(*histogram_ary)[res].bytes += tid_bytes[i];
}
++res;
(*histogram_ary)[res].tid = tid_end;
(*histogram_ary)[res].bytes = -1;
if (*repeatp == 0) {
if (VerboseOpt > 1)
fprintf(stderr, "\n");
score_printf(LINE3, "Prescan %d chunks, total %ju MBytes",
res, (uintmax_t)total / (1024 * 1024));
fprintf(stderr, "Prescan %d chunks, total %ju MBytes (",
res, (uintmax_t)total / (1024 * 1024));
for (i = 0; i < res && i < 3; ++i) {
if (i)
fprintf(stderr, ", ");
fprintf(stderr, "%ju",
(uintmax_t)(*histogram_ary)[i].bytes);
}
if (i < res)
fprintf(stderr, ", ...");
fprintf(stderr, ")\n");
}
assert(res <= HIST_COUNT);
*repeatp = 1;
free(tid_bytes);
return(res);
}
static
void
create_pfs(const char *filesystem, hammer_uuid_t *s_uuid)
{
if (ForceYesOpt == 1) {
fprintf(stderr, "PFS slave %s does not exist. "
"Auto create new slave PFS!\n", filesystem);
} else {
fprintf(stderr, "PFS slave %s does not exist.\n"
"Do you want to create a new slave PFS? [y/n] ",
filesystem);
fflush(stderr);
if (getyntty() != 1) {
errx(1, "Aborting operation");
}
}
char *shared_uuid = NULL;
hammer_uuid_to_string(s_uuid, &shared_uuid);
char *cmd = NULL;
asprintf(&cmd, "/sbin/hammer pfs-slave '%s' shared-uuid=%s 1>&2",
filesystem, shared_uuid);
free(shared_uuid);
if (cmd == NULL) {
errx(1, "Failed to alloc memory");
}
if (system(cmd) != 0)
fprintf(stderr, "Failed to create PFS\n");
free(cmd);
}
void
hammer_cmd_mirror_write(char **av, int ac)
{
struct hammer_ioc_mirror_rw mirror;
const char *filesystem;
char *buf = malloc(SERIALBUF_SIZE);
struct hammer_ioc_pseudofs_rw pfs;
struct hammer_ioc_mrecord_head pickup;
struct hammer_ioc_synctid synctid;
union hammer_ioc_mrecord_any mrec_tmp;
hammer_ioc_mrecord_any_t mrec;
struct stat st;
int error;
int fd;
int n;
if (ac != 1) {
mirror_usage(1);
}
filesystem = av[0];
hammer_check_restrict(filesystem);
pickup.signature = 0;
pickup.type = 0;
again:
bzero(&mirror, sizeof(mirror));
hammer_key_beg_init(&mirror.key_beg);
hammer_key_end_init(&mirror.key_end);
mirror.key_end = mirror.key_beg;
mrec = read_mrecord(0, &error, &pickup);
if (mrec == NULL) {
if (error == 0) {
errx(1, "validate_mrec_header: short read");
}
exit(1);
}
if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
free(buf);
return;
}
if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
errx(1, "validate_mrec_header: did not get expected "
"PFSD record type");
}
if (mrec->head.rec_size != sizeof(mrec->pfs)) {
errx(1, "validate_mrec_header: unexpected payload size");
}
if (lstat(filesystem, &st) != 0)
create_pfs(filesystem, &mrec->pfs.pfsd.shared_uuid);
free(mrec);
mrec = NULL;
fd = getpfs(&pfs, filesystem);
mirror.tid_beg = 0;
if (TwoWayPipeOpt) {
generate_mrec_header(fd, pfs.pfs_id, &mrec_tmp);
if (mirror.tid_beg < mrec_tmp.pfs.pfsd.sync_beg_tid)
mirror.tid_beg = mrec_tmp.pfs.pfsd.sync_beg_tid;
mirror.tid_end = mrec_tmp.pfs.pfsd.sync_end_tid;
write_mrecord(1, HAMMER_MREC_TYPE_PFSD,
&mrec_tmp, sizeof(mrec_tmp.pfs));
}
n = validate_mrec_header(fd, 0, 1, pfs.pfs_id, &pickup,
&mirror.tid_beg, &mirror.tid_end);
if (n < 0) {
relpfs(fd, &pfs);
free(buf);
return;
}
mirror.ubuf = buf;
mirror.size = SERIALBUF_SIZE;
for (;;) {
mirror.count = 0;
mirror.pfs_id = pfs.pfs_id;
mirror.shared_uuid = pfs.ondisk->shared_uuid;
mirror.size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
if (mirror.size <= 0)
break;
if (ioctl(fd, HAMMERIOC_MIRROR_WRITE, &mirror) < 0) {
err(1, "Mirror-write %s failed", filesystem);
}
if (mirror.head.flags & HAMMER_IOC_HEAD_ERROR) {
errx(1, "Mirror-write %s fatal error %d",
filesystem, mirror.head.error);
}
#if 0
if (mirror.head.flags & HAMMER_IOC_HEAD_INTR) {
errx(1, "Mirror-write %s interrupted by timer at"
" %016llx",
filesystem,
mirror.key_cur.obj_id);
}
#endif
}
mrec = read_mrecord(0, &error, &pickup);
if (mrec && mrec->head.type == HAMMER_MREC_TYPE_TERM) {
fprintf(stderr, "Mirror-write: received termination request\n");
relpfs(fd, &pfs);
free(mrec);
free(buf);
return;
}
if (mrec == NULL ||
(mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
mrec->head.type != HAMMER_MREC_TYPE_IDLE) ||
mrec->head.rec_size != sizeof(mrec->sync)) {
errx(1, "Mirror-write %s: Did not get termination "
"sync record, or rec_size is wrong rt=%d",
filesystem, (mrec ? (int)mrec->head.type : -1));
}
if (mrec->head.type == HAMMER_MREC_TYPE_SYNC) {
update_pfs_snapshot(fd, mirror.tid_end, pfs.pfs_id);
bzero(&synctid, sizeof(synctid));
synctid.op = HAMMER_SYNCTID_SYNC2;
ioctl(fd, HAMMERIOC_SYNCTID, &synctid);
if (VerboseOpt >= 2) {
fprintf(stderr, "Mirror-write %s: succeeded\n",
filesystem);
}
}
free(mrec);
mrec = NULL;
if (TwoWayPipeOpt) {
mrec_tmp.update.tid = mirror.tid_end;
write_mrecord(1, HAMMER_MREC_TYPE_UPDATE,
&mrec_tmp, sizeof(mrec_tmp.update));
} else {
printf("Source can update synctid to 0x%016jx\n",
(uintmax_t)mirror.tid_end);
}
relpfs(fd, &pfs);
goto again;
}
void
hammer_cmd_mirror_dump(char **av, int ac)
{
char *buf = malloc(SERIALBUF_SIZE);
struct hammer_ioc_mrecord_head pickup;
hammer_ioc_mrecord_any_t mrec;
int error;
int size;
int offset;
int bytes;
int header_only = 0;
if (ac == 1 && strcmp(*av, "header") == 0) {
header_only = 1;
} else if (ac != 0) {
mirror_usage(1);
}
pickup.signature = 0;
pickup.type = 0;
mrec = read_mrecord(0, &error, &pickup);
if (header_only && mrec != NULL) {
dump_pfsd(&mrec->pfs.pfsd, -1);
free(mrec);
free(buf);
return;
}
free(mrec);
again:
for (;;) {
size = read_mrecords(0, buf, SERIALBUF_SIZE, &pickup);
if (size <= 0)
break;
offset = 0;
while (offset < size) {
mrec = (void *)((char *)buf + offset);
bytes = HAMMER_HEAD_DOALIGN(mrec->head.rec_size);
if (offset + bytes > size) {
errx(1, "Misaligned record");
}
switch(mrec->head.type & HAMMER_MRECF_TYPE_MASK) {
case HAMMER_MREC_TYPE_REC_BADCRC:
case HAMMER_MREC_TYPE_REC:
printf("Record lo=%08x obj=%016jx key=%016jx "
"rt=%02x ot=%02x",
mrec->rec.leaf.base.localization,
(uintmax_t)mrec->rec.leaf.base.obj_id,
(uintmax_t)mrec->rec.leaf.base.key,
mrec->rec.leaf.base.rec_type,
mrec->rec.leaf.base.obj_type);
if (mrec->head.type ==
HAMMER_MREC_TYPE_REC_BADCRC) {
printf(" (BAD CRC)");
}
printf("\n");
printf(" tids %016jx:%016jx data=%d\n",
(uintmax_t)mrec->rec.leaf.base.create_tid,
(uintmax_t)mrec->rec.leaf.base.delete_tid,
mrec->rec.leaf.data_len);
break;
case HAMMER_MREC_TYPE_PASS:
printf("Pass lo=%08x obj=%016jx key=%016jx "
"rt=%02x ot=%02x\n",
mrec->rec.leaf.base.localization,
(uintmax_t)mrec->rec.leaf.base.obj_id,
(uintmax_t)mrec->rec.leaf.base.key,
mrec->rec.leaf.base.rec_type,
mrec->rec.leaf.base.obj_type);
printf(" tids %016jx:%016jx data=%d\n",
(uintmax_t)mrec->rec.leaf.base.create_tid,
(uintmax_t)mrec->rec.leaf.base.delete_tid,
mrec->rec.leaf.data_len);
break;
case HAMMER_MREC_TYPE_SKIP:
printf("Skip lo=%08x obj=%016jx key=%016jx rt=%02x to\n"
" lo=%08x obj=%016jx key=%016jx rt=%02x\n",
mrec->skip.skip_beg.localization,
(uintmax_t)mrec->skip.skip_beg.obj_id,
(uintmax_t)mrec->skip.skip_beg.key,
mrec->skip.skip_beg.rec_type,
mrec->skip.skip_end.localization,
(uintmax_t)mrec->skip.skip_end.obj_id,
(uintmax_t)mrec->skip.skip_end.key,
mrec->skip.skip_end.rec_type);
default:
break;
}
offset += bytes;
}
}
mrec = read_mrecord(0, &error, &pickup);
if (mrec == NULL ||
(mrec->head.type != HAMMER_MREC_TYPE_SYNC &&
mrec->head.type != HAMMER_MREC_TYPE_IDLE)) {
fprintf(stderr, "Mirror-dump: Did not get termination "
"sync record\n");
}
free(mrec);
mrec = read_mrecord(0, &error, &pickup);
if (mrec) {
free(mrec);
goto again;
}
free(buf);
}
void
hammer_cmd_mirror_copy(char **av, int ac, int streaming)
{
pid_t pid1;
pid_t pid2;
int fds[2];
const char *xav[32];
char tbuf[16];
char *sh, *user, *host, *rfs;
int xac;
if (ac != 2) {
mirror_usage(1);
}
TwoWayPipeOpt = 1;
signal(SIGPIPE, SIG_IGN);
again:
if (pipe(fds) < 0) {
err(1, "pipe");
}
if ((pid1 = fork()) == 0) {
signal(SIGPIPE, SIG_DFL);
dup2(fds[0], 0);
dup2(fds[0], 1);
close(fds[0]);
close(fds[1]);
if ((rfs = strchr(av[0], ':')) != NULL) {
xac = 0;
if((sh = getenv("HAMMER_RSH")) == NULL)
xav[xac++] = "ssh";
else
xav[xac++] = sh;
if (CompressOpt)
xav[xac++] = "-C";
if ((host = strchr(av[0], '@')) != NULL) {
user = strndup( av[0], (host++ - av[0]));
host = strndup( host, (rfs++ - host));
xav[xac++] = "-l";
xav[xac++] = user;
xav[xac++] = host;
} else {
host = strndup( av[0], (rfs++ - av[0]));
user = NULL;
xav[xac++] = host;
}
if (SshPort) {
xav[xac++] = "-p";
xav[xac++] = SshPort;
}
xav[xac++] = "hammer";
switch(VerboseOpt) {
case 0:
break;
case 1:
xav[xac++] = "-v";
break;
case 2:
xav[xac++] = "-vv";
break;
default:
xav[xac++] = "-vvv";
break;
}
if (ForceYesOpt)
xav[xac++] = "-y";
xav[xac++] = "-2";
if (TimeoutOpt) {
snprintf(tbuf, sizeof(tbuf), "%d", TimeoutOpt);
xav[xac++] = "-t";
xav[xac++] = tbuf;
}
if (SplitupOptStr) {
xav[xac++] = "-S";
xav[xac++] = SplitupOptStr;
}
if (streaming)
xav[xac++] = "mirror-read-stream";
else
xav[xac++] = "mirror-read";
xav[xac++] = rfs;
xav[xac++] = NULL;
execvp(*xav, (void *)xav);
} else {
hammer_cmd_mirror_read(av, 1, streaming);
fflush(stdout);
fflush(stderr);
}
_exit(1);
}
if ((pid2 = fork()) == 0) {
signal(SIGPIPE, SIG_DFL);
dup2(fds[1], 0);
dup2(fds[1], 1);
close(fds[0]);
close(fds[1]);
if ((rfs = strchr(av[1], ':')) != NULL) {
xac = 0;
if((sh = getenv("HAMMER_RSH")) == NULL)
xav[xac++] = "ssh";
else
xav[xac++] = sh;
if (CompressOpt)
xav[xac++] = "-C";
if ((host = strchr(av[1], '@')) != NULL) {
user = strndup( av[1], (host++ - av[1]));
host = strndup( host, (rfs++ - host));
xav[xac++] = "-l";
xav[xac++] = user;
xav[xac++] = host;
} else {
host = strndup( av[1], (rfs++ - av[1]));
user = NULL;
xav[xac++] = host;
}
if (SshPort) {
xav[xac++] = "-p";
xav[xac++] = SshPort;
}
xav[xac++] = "hammer";
switch(VerboseOpt) {
case 0:
break;
case 1:
xav[xac++] = "-v";
break;
case 2:
xav[xac++] = "-vv";
break;
default:
xav[xac++] = "-vvv";
break;
}
if (ForceYesOpt)
xav[xac++] = "-y";
xav[xac++] = "-2";
xav[xac++] = "mirror-write";
xav[xac++] = rfs;
xav[xac++] = NULL;
execvp(*xav, (void *)xav);
} else {
hammer_cmd_mirror_write(av + 1, 1);
fflush(stdout);
fflush(stderr);
}
_exit(1);
}
close(fds[0]);
close(fds[1]);
while (waitpid(pid1, NULL, 0) <= 0)
;
while (waitpid(pid2, NULL, 0) <= 0)
;
if (streaming) {
if (VerboseOpt) {
fprintf(stderr, "\nLost Link\n");
fflush(stderr);
}
sleep(15 + DelayOpt);
goto again;
}
}
static
int
read_mrecords(int fd, char *buf, u_int size, struct hammer_ioc_mrecord_head *pickup)
{
hammer_ioc_mrecord_any_t mrec;
u_int count;
size_t n;
size_t i;
size_t bytes;
int type;
count = 0;
while (size - count >= HAMMER_MREC_HEADSIZE) {
fflush(stdout);
if (pickup->signature == 0) {
for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
i = read(fd, (char *)pickup + n,
HAMMER_MREC_HEADSIZE - n);
if (i <= 0)
break;
}
if (n == 0)
break;
if (n != HAMMER_MREC_HEADSIZE) {
errx(1, "read_mrecords: short read on pipe");
}
if (pickup->signature != HAMMER_IOC_MIRROR_SIGNATURE) {
errx(1, "read_mrecords: malformed record on pipe, "
"bad signature");
}
}
if (pickup->rec_size < HAMMER_MREC_HEADSIZE ||
pickup->rec_size > sizeof(*mrec) + HAMMER_XBUFSIZE) {
errx(1, "read_mrecords: malformed record on pipe, "
"illegal rec_size");
}
bytes = HAMMER_HEAD_DOALIGN(pickup->rec_size);
if (size - count < bytes)
break;
type = pickup->type & HAMMER_MRECF_TYPE_LOMASK;
if (type != HAMMER_MREC_TYPE_PFSD &&
type != HAMMER_MREC_TYPE_REC &&
type != HAMMER_MREC_TYPE_SKIP &&
type != HAMMER_MREC_TYPE_PASS) {
break;
}
for (n = HAMMER_MREC_HEADSIZE; n < bytes; n += i) {
i = read(fd, buf + count + n, bytes - n);
if (i <= 0)
break;
}
if (n != bytes) {
errx(1, "read_mrecords: short read on pipe");
}
bcopy(pickup, buf + count, HAMMER_MREC_HEADSIZE);
pickup->signature = 0;
pickup->type = 0;
mrec = (void *)(buf + count);
if (!hammer_crc_test_mrec_head(&mrec->head, mrec->head.rec_size)) {
errx(1, "read_mrecords: malformed record on pipe, bad crc");
}
type = mrec->head.type & HAMMER_MRECF_TYPE_MASK;
if (type == HAMMER_MREC_TYPE_REC) {
if (mrec->head.rec_size <
sizeof(mrec->rec) + mrec->rec.leaf.data_len) {
errx(1, "read_mrecords: malformed record on "
"pipe, illegal element data_len");
}
if (mrec->rec.leaf.data_len &&
mrec->rec.leaf.data_offset &&
hammer_crc_test_leaf(HammerVersion, &mrec->rec + 1, &mrec->rec.leaf) == 0) {
fprintf(stderr,
"read_mrecords: data_crc did not "
"match data! obj=%016jx key=%016jx\n",
(uintmax_t)mrec->rec.leaf.base.obj_id,
(uintmax_t)mrec->rec.leaf.base.key);
fprintf(stderr,
"continuing, but there are problems\n");
}
}
count += bytes;
}
return(count);
}
static
hammer_ioc_mrecord_any_t
read_mrecord(int fdin, int *errorp, struct hammer_ioc_mrecord_head *pickup)
{
hammer_ioc_mrecord_any_t mrec;
struct hammer_ioc_mrecord_head mrechd;
size_t bytes;
size_t n;
size_t i;
if (pickup && pickup->type != 0) {
mrechd = *pickup;
pickup->signature = 0;
pickup->type = 0;
n = HAMMER_MREC_HEADSIZE;
} else {
for (n = 0; n < HAMMER_MREC_HEADSIZE; n += i) {
i = read(fdin, (char *)&mrechd + n, HAMMER_MREC_HEADSIZE - n);
if (i <= 0)
break;
}
if (n == 0) {
*errorp = 0;
return(NULL);
}
if (n != HAMMER_MREC_HEADSIZE) {
fprintf(stderr, "short read of mrecord header\n");
*errorp = EPIPE;
return(NULL);
}
}
if (mrechd.signature != HAMMER_IOC_MIRROR_SIGNATURE) {
fprintf(stderr, "read_mrecord: bad signature\n");
*errorp = EINVAL;
return(NULL);
}
bytes = HAMMER_HEAD_DOALIGN(mrechd.rec_size);
assert(bytes >= sizeof(mrechd));
mrec = malloc(bytes);
mrec->head = mrechd;
while (n < bytes) {
i = read(fdin, (char *)mrec + n, bytes - n);
if (i <= 0)
break;
n += i;
}
if (n != bytes) {
fprintf(stderr, "read_mrecord: short read on payload\n");
*errorp = EPIPE;
return(NULL);
}
if (!hammer_crc_test_mrec_head(&mrec->head, mrec->head.rec_size)) {
fprintf(stderr, "read_mrecord: bad CRC\n");
*errorp = EINVAL;
return(NULL);
}
*errorp = 0;
return(mrec);
}
static
void
write_mrecord(int fdout, uint32_t type, hammer_ioc_mrecord_any_t mrec,
int bytes)
{
char zbuf[HAMMER_HEAD_ALIGN];
int pad;
pad = HAMMER_HEAD_DOALIGN(bytes) - bytes;
assert(bytes >= (int)sizeof(mrec->head));
bzero(&mrec->head, sizeof(mrec->head));
mrec->head.signature = HAMMER_IOC_MIRROR_SIGNATURE;
mrec->head.type = type;
mrec->head.rec_size = bytes;
hammer_crc_set_mrec_head(&mrec->head, bytes);
if (write(fdout, mrec, bytes) != bytes) {
err(1, "write_mrecord");
}
if (pad) {
bzero(zbuf, pad);
if (write(fdout, zbuf, pad) != pad) {
err(1, "write_mrecord");
}
}
}
static
void
generate_mrec_header(int fd, int pfs_id,
union hammer_ioc_mrecord_any *mrec_tmp)
{
struct hammer_ioc_pseudofs_rw pfs;
bzero(mrec_tmp, sizeof(*mrec_tmp));
clrpfs(&pfs, &mrec_tmp->pfs.pfsd, pfs_id);
if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
err(1, "Mirror-read: not a HAMMER fs/pseudofs!");
}
if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
errx(1, "Mirror-read: HAMMER PFS version mismatch!");
}
mrec_tmp->pfs.version = pfs.version;
}
static
int
validate_mrec_header(int fd, int fdin, int is_target, int pfs_id,
struct hammer_ioc_mrecord_head *pickup,
hammer_tid_t *tid_begp, hammer_tid_t *tid_endp)
{
struct hammer_ioc_pseudofs_rw pfs;
struct hammer_pseudofs_data pfsd;
hammer_ioc_mrecord_any_t mrec;
int error;
clrpfs(&pfs, &pfsd, pfs_id);
if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
err(1, "mirror-write: not a HAMMER fs/pseudofs!");
}
if (pfs.version != HAMMER_IOC_PSEUDOFS_VERSION) {
errx(1, "mirror-write: HAMMER PFS version mismatch!");
}
mrec = read_mrecord(fdin, &error, pickup);
if (mrec == NULL) {
if (error == 0) {
errx(1, "validate_mrec_header: short read");
}
exit(1);
}
if (mrec->head.type == HAMMER_MREC_TYPE_TERM) {
free(mrec);
return(-1);
}
if (mrec->head.type != HAMMER_MREC_TYPE_PFSD) {
errx(1, "validate_mrec_header: did not get expected "
"PFSD record type");
}
if (mrec->head.rec_size != sizeof(mrec->pfs)) {
errx(1, "validate_mrec_header: unexpected payload size");
}
if (mrec->pfs.version != pfs.version) {
errx(1, "validate_mrec_header: Version mismatch");
}
if (hammer_uuid_compare(&mrec->pfs.pfsd.shared_uuid, &pfsd.shared_uuid)) {
errx(1, "mirror-write: source and target have "
"different shared-uuid's!");
}
if (is_target && hammer_is_pfs_master(&pfsd)) {
errx(1, "mirror-write: target must be in slave mode");
}
if (tid_begp)
*tid_begp = mrec->pfs.pfsd.sync_beg_tid;
if (tid_endp)
*tid_endp = mrec->pfs.pfsd.sync_end_tid;
free(mrec);
return(0);
}
static
void
update_pfs_snapshot(int fd, hammer_tid_t snapshot_tid, int pfs_id)
{
struct hammer_ioc_pseudofs_rw pfs;
struct hammer_pseudofs_data pfsd;
clrpfs(&pfs, &pfsd, pfs_id);
if (ioctl(fd, HAMMERIOC_GET_PSEUDOFS, &pfs) != 0) {
err(1, "update_pfs_snapshot (read)");
}
if (pfsd.sync_end_tid != snapshot_tid) {
pfsd.sync_end_tid = snapshot_tid;
if (ioctl(fd, HAMMERIOC_SET_PSEUDOFS, &pfs) != 0) {
err(1, "update_pfs_snapshot (rewrite)");
}
if (VerboseOpt >= 2) {
fprintf(stderr,
"Mirror-write: Completed, updated snapshot "
"to %016jx\n",
(uintmax_t)snapshot_tid);
fflush(stderr);
}
}
}
static
ssize_t
writebw(int fd, const void *buf, size_t nbytes,
uint64_t *bwcount, struct timeval *tv1)
{
struct timeval tv2;
size_t n;
ssize_t r;
ssize_t a;
int usec;
a = 0;
r = 0;
while (nbytes) {
if (*bwcount + nbytes > BandwidthOpt)
n = BandwidthOpt - *bwcount;
else
n = nbytes;
if (n)
r = write(fd, buf, n);
if (r >= 0) {
a += r;
nbytes -= r;
buf = (const char *)buf + r;
}
if ((size_t)r != n)
break;
*bwcount += n;
if (*bwcount >= BandwidthOpt) {
gettimeofday(&tv2, NULL);
usec = (int)(tv2.tv_sec - tv1->tv_sec) * 1000000 +
(int)(tv2.tv_usec - tv1->tv_usec);
if (usec >= 0 && usec < 1000000)
usleep(1000000 - usec);
gettimeofday(tv1, NULL);
*bwcount -= BandwidthOpt;
}
}
return(a ? a : r);
}
static
int
getyntty(void)
{
char buf[256];
FILE *fp;
int result;
fp = fopen("/dev/tty", "r");
if (fp == NULL) {
fprintf(stderr, "No terminal for response\n");
return(-1);
}
result = -1;
while (fgets(buf, sizeof(buf), fp) != NULL) {
if (buf[0] == 'y' || buf[0] == 'Y') {
result = 1;
break;
}
if (buf[0] == 'n' || buf[0] == 'N') {
result = 0;
break;
}
fprintf(stderr, "Response not understood\n");
break;
}
fclose(fp);
return(result);
}
static
void
score_printf(size_t i, size_t w, const char *ctl, ...)
{
va_list va;
size_t n;
static size_t SSize;
static int SFd = -1;
static char ScoreBuf[1024];
if (ScoreBoardFile == NULL)
return;
assert(i + w < sizeof(ScoreBuf));
if (SFd < 0) {
SFd = open(ScoreBoardFile, O_RDWR|O_CREAT|O_TRUNC, 0644);
if (SFd < 0)
return;
SSize = 0;
}
for (n = 0; n < i; ++n) {
if (ScoreBuf[n] == 0)
ScoreBuf[n] = ' ';
}
va_start(va, ctl);
vsnprintf(ScoreBuf + i, w - 1, ctl, va);
va_end(va);
n = strlen(ScoreBuf + i);
while (n < w - 1) {
ScoreBuf[i + n] = ' ';
++n;
}
ScoreBuf[i + n] = '\n';
if (SSize < i + w)
SSize = i + w;
pwrite(SFd, ScoreBuf, SSize, 0);
}
static
void
hammer_check_restrict(const char *filesystem)
{
size_t rlen;
int atslash;
if (RestrictTarget == NULL)
return;
rlen = strlen(RestrictTarget);
if (strncmp(filesystem, RestrictTarget, rlen) != 0) {
errx(1, "hammer-remote: restricted target");
}
atslash = 1;
while (filesystem[rlen]) {
if (atslash &&
filesystem[rlen] == '.' &&
filesystem[rlen+1] == '.') {
errx(1, "hammer-remote: '..' not allowed");
}
if (filesystem[rlen] == '/')
atslash = 1;
else
atslash = 0;
++rlen;
}
}
static
void
mirror_usage(int code)
{
fprintf(stderr,
"hammer mirror-read <filesystem> [begin-tid]\n"
"hammer mirror-read-stream <filesystem> [begin-tid]\n"
"hammer mirror-write <filesystem>\n"
"hammer mirror-dump [header]\n"
"hammer mirror-copy [[user@]host:]<filesystem>"
" [[user@]host:]<filesystem>\n"
"hammer mirror-stream [[user@]host:]<filesystem>"
" [[user@]host:]<filesystem>\n"
);
exit(code);
}