mirror of
https://github.com/schoebel/mars
synced 2024-12-27 17:12:32 +00:00
349 lines
7.8 KiB
C
349 lines
7.8 KiB
C
/*
|
|
* MARS Long Distance Replication Software
|
|
*
|
|
* This file is part of MARS project: http://schoebel.github.io/mars/
|
|
*
|
|
* Copyright (C) 2010-2014 Thomas Schoebel-Theuer
|
|
* Copyright (C) 2011-2014 1&1 Internet AG
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
|
|
/* This is PROVISIONARY hacker's tool for import / export
|
|
* of MARS transaction logfiles.
|
|
*
|
|
* NOT FOR END USERS!!!!!
|
|
*/
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include <time.h>
|
|
|
|
/* FIXME: some _provisionary_ hacks to bridge the gap between kernelspace and userspace...
|
|
*/
|
|
#define bool int
|
|
#define false 0
|
|
#define true 1
|
|
#define likely(x) x
|
|
#define unlikely(x) x
|
|
#define MARS_INF printf
|
|
#define MARS_WRN printf
|
|
#define MARS_ERR printf
|
|
#define mars_digest_size 16
|
|
#define mars_digest(a,b,c) /*empty*/
|
|
#define loff_t long long
|
|
#define scnprintf snprintf
|
|
#include "../kernel/lib_log.h"
|
|
|
|
static
|
|
int read_record(
|
|
struct log_header *lh,
|
|
void *buf,
|
|
int maxlen,
|
|
int fh,
|
|
loff_t pos,
|
|
void **payload,
|
|
int *payload_len,
|
|
unsigned int *seq_nr)
|
|
{
|
|
ssize_t status;
|
|
|
|
status = pread(fh, buf, maxlen, pos);
|
|
if (status < 0) {
|
|
MARS_ERR("cannot pread() %d bytes, status = %d\n", maxlen, (int)status);
|
|
return -errno;
|
|
}
|
|
if (!status) {
|
|
MARS_INF("got EOF\n");
|
|
return 0;
|
|
}
|
|
|
|
return log_scan(buf, status, pos, 0, true, lh, payload, payload_len, seq_nr);
|
|
}
|
|
|
|
static
|
|
int write_record(
|
|
int out_fd,
|
|
void *buf,
|
|
int buf_len,
|
|
char *desc)
|
|
{
|
|
struct log_header lh = {
|
|
.l_len = buf_len,
|
|
};
|
|
unsigned short total_len = buf_len + OVERHEAD;
|
|
char data[total_len];
|
|
int offset = 0;
|
|
int len = strlen(desc);
|
|
int crc = 0;
|
|
int status;
|
|
|
|
// extract filename from desc path
|
|
while (len > 0 && desc[len-1] != '/')
|
|
len--;
|
|
desc += len;
|
|
|
|
status = sscanf(
|
|
desc,
|
|
"%u,%ld.%lu,%ld.%lu,%hx,%lld",
|
|
&lh.l_seq_nr,
|
|
&lh.l_stamp.tv_sec,
|
|
&lh.l_stamp.tv_nsec,
|
|
&lh.l_written.tv_sec,
|
|
&lh.l_written.tv_nsec,
|
|
&lh.l_code,
|
|
&lh.l_pos
|
|
);
|
|
if (status != 7) {
|
|
MARS_ERR("only %d arguments parsable from '%s'\n", status, desc);
|
|
return -EINVAL;
|
|
}
|
|
|
|
DATA_PUT(data, offset, START_MAGIC);
|
|
DATA_PUT(data, offset, (char)FORMAT_VERSION);
|
|
DATA_PUT(data, offset, (char)1); // valid_flag
|
|
DATA_PUT(data, offset, total_len); // start of next header
|
|
DATA_PUT(data, offset, lh.l_stamp.tv_sec);
|
|
DATA_PUT(data, offset, lh.l_stamp.tv_nsec);
|
|
DATA_PUT(data, offset, lh.l_pos);
|
|
DATA_PUT(data, offset, lh.l_len);
|
|
DATA_PUT(data, offset, (short)0); // spare
|
|
DATA_PUT(data, offset, (int)0); // spare
|
|
DATA_PUT(data, offset, lh.l_code);
|
|
DATA_PUT(data, offset, (short)0); // spare
|
|
|
|
memcpy(data + offset, buf, buf_len);
|
|
offset += buf_len;
|
|
|
|
DATA_PUT(data, offset, END_MAGIC);
|
|
DATA_PUT(data, offset, crc);
|
|
DATA_PUT(data, offset, (char)1); // valid_flag copy
|
|
DATA_PUT(data, offset, (char)0); // spare
|
|
DATA_PUT(data, offset, (short)0); // spare
|
|
DATA_PUT(data, offset, lh.l_seq_nr);
|
|
DATA_PUT(data, offset, lh.l_written.tv_sec);
|
|
DATA_PUT(data, offset, lh.l_written.tv_nsec);
|
|
|
|
if (offset != total_len) {
|
|
MARS_ERR("offset %d != total_len %d\n", offset, total_len);
|
|
return -EINVAL;
|
|
}
|
|
|
|
status = write(out_fd, data, total_len);
|
|
if (status != total_len) {
|
|
MARS_ERR("bad write, status = %d errno = %d\n", status, errno);
|
|
return -EIO;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static
|
|
int make_dirs(char *out, int out_len, char *out_dirname, int old[], unsigned int seqnr)
|
|
{
|
|
int len;
|
|
unsigned int nr;
|
|
|
|
nr = seqnr / 1000000000;
|
|
|
|
len = scnprintf(out, out_len, "%s/%01u", out_dirname, nr);
|
|
|
|
if (old[0] != nr) {
|
|
old[0] = nr;
|
|
(void)mkdir(out, 0700);
|
|
}
|
|
|
|
nr = seqnr / 1000000 % 1000;
|
|
|
|
len += scnprintf(out + len, out_len - len, "/%03u", nr);
|
|
|
|
if (old[1] != nr) {
|
|
old[1] = nr;
|
|
(void)mkdir(out, 0700);
|
|
}
|
|
|
|
nr = seqnr / 1000 % 1000;
|
|
|
|
len += scnprintf(out + len, out_len - len, "/%03u", nr);
|
|
|
|
if (old[2] != nr) {
|
|
old[2] = nr;
|
|
(void)mkdir(out, 0700);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static
|
|
int export_logfile(char *in_filename, char *out_dirname)
|
|
{
|
|
char buf[4096 * 8];
|
|
int old[3] = { -1, -1, -1};
|
|
loff_t pos = 0;
|
|
unsigned int old_seqnr = 0;
|
|
int in_fd;
|
|
|
|
in_fd = open(in_filename, O_RDONLY);
|
|
if (in_fd < 0) {
|
|
MARS_ERR("cannot open input file '%s', errno = %d\n", in_filename, errno);
|
|
return -errno;
|
|
}
|
|
|
|
if (out_dirname) {
|
|
(void)mkdir(out_dirname, 0700);
|
|
}
|
|
|
|
for (;;) {
|
|
struct log_header lh = {};
|
|
void *payload = NULL;
|
|
int payload_len = 0;
|
|
unsigned int seqnr = 0;
|
|
int status;
|
|
|
|
status = read_record(&lh, buf, sizeof(buf), in_fd, pos, &payload, &payload_len, &seqnr);
|
|
if (status <= 0) {
|
|
return status;
|
|
}
|
|
|
|
if (old_seqnr > 0 && seqnr != old_seqnr + 1) {
|
|
printf("ERROR: seqnr = %d status = %d\n", seqnr, status);
|
|
} else {
|
|
//printf("OK: seqnr = %d status = %d\n", seqnr, status);
|
|
}
|
|
|
|
if (out_dirname) {
|
|
char out_name[1024];
|
|
int len;
|
|
int out_fd;
|
|
|
|
len = make_dirs(out_name, sizeof(out_name), out_dirname, old, seqnr);
|
|
|
|
snprintf(out_name + len, sizeof(out_name) - len,
|
|
"/%010u,%09u.%09u,%09u.%09u,%04x,%012llu",
|
|
seqnr,
|
|
(unsigned)lh.l_stamp.tv_sec,
|
|
(unsigned)lh.l_stamp.tv_nsec,
|
|
(unsigned)lh.l_written.tv_sec,
|
|
(unsigned)lh.l_written.tv_nsec,
|
|
lh.l_code,
|
|
(unsigned long long)lh.l_pos
|
|
);
|
|
|
|
out_fd = creat(out_name, 0600);
|
|
if (out_fd < 0) {
|
|
MARS_ERR("cannot open output file '%s', errno = %d\n", out_name, errno);
|
|
return -errno;
|
|
}
|
|
write(out_fd, payload, payload_len);
|
|
close(out_fd);
|
|
}
|
|
|
|
pos += status;
|
|
old_seqnr = seqnr;
|
|
}
|
|
}
|
|
|
|
static
|
|
int import_logfile(char *in_dirname, char *out_filename)
|
|
{
|
|
char buf[4096 * 8];
|
|
char cmd[256];
|
|
int out_fd;
|
|
FILE *names;
|
|
|
|
snprintf(cmd, sizeof(cmd), "find '%s' -type f -name '[0-9]*[0-9]' | sort -n", in_dirname);
|
|
|
|
names = popen(cmd, "r");
|
|
if (!names) {
|
|
MARS_ERR("cannot popen command '%s', errno = %d\n", cmd, errno);
|
|
return -errno;
|
|
}
|
|
|
|
out_fd = creat(out_filename, 0600);
|
|
if (out_fd < 0) {
|
|
MARS_ERR("cannot open output file '%s', errno = %d\n", out_filename, errno);
|
|
return -errno;
|
|
}
|
|
|
|
for (;;) {
|
|
char path[1024];
|
|
int len;
|
|
int in_fd;
|
|
int status;
|
|
|
|
if (!fgets(path, sizeof(path), names)) {
|
|
break;
|
|
}
|
|
// chomp the terminating \n
|
|
len = strlen(path);
|
|
if (len <= 1)
|
|
continue;
|
|
if (path[len-1] == '\n')
|
|
path[len-1] = '\0';
|
|
|
|
in_fd = open(path, O_RDONLY);
|
|
if (in_fd < 0) {
|
|
MARS_ERR("cannot open input file '%s', errno = %d\n", path, errno);
|
|
return -errno;
|
|
}
|
|
|
|
status = read(in_fd, buf, sizeof(buf));
|
|
if (status < 0) {
|
|
MARS_ERR("cannot read from input file '%s', errno = %d\n", path, errno);
|
|
return -errno;
|
|
}
|
|
close(in_fd);
|
|
if (status >= sizeof(buf)) {
|
|
MARS_ERR("input file '%s' contains a too long record\n", path);
|
|
return -EINVAL;
|
|
}
|
|
|
|
printf("record '%s' len = %d\n", path, status);
|
|
status = write_record(out_fd, buf, status, path);
|
|
if (status < 0)
|
|
break;
|
|
}
|
|
|
|
close(out_fd);
|
|
pclose(names);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
if (argc < 3) {
|
|
printf("usage: mars-log-impex {im,ex}port filename [dirname]\n");
|
|
return -1;
|
|
}
|
|
|
|
if (!strcmp(argv[1], "export")) {
|
|
char *out_dirname = NULL;
|
|
if (argc > 3 && argv[3])
|
|
out_dirname = argv[3];
|
|
return export_logfile(argv[2], out_dirname);
|
|
}
|
|
if (!strcmp(argv[1], "import") && argc > 3) {
|
|
return import_logfile(argv[3], argv[2]);
|
|
}
|
|
|
|
return 0;
|
|
}
|