#include "jscan.h"
static int donecheck(enum jdirection direction, struct jdata *jd,
int64_t transid);
static void usage(const char *av0);
int jmodes;
int fsync_opt;
int verbose_opt;
off_t prefix_file_size = 100 * 1024 * 1024;
off_t trans_count;
static enum jdirection jdirection = JD_FORWARDS;
static void jscan_do_output(struct jfile *, const char *,
const char *, int64_t);
static void jscan_do_mirror(struct jfile *, const char *,
const char *, int64_t);
static void jscan_do_record(struct jfile *, const char *,
const char *, int64_t);
static void jscan_do_debug(struct jfile *, const char *,
const char *, int64_t);
static void fork_subprocess(struct jfile *,
void (*)(struct jfile *, const char *,
const char *, int64_t),
const char *,
const char *, const char *, int64_t);
int
main(int ac, char **av)
{
const char *input_prefix = NULL;
char *output_transid_file = NULL;
char *mirror_transid_file = NULL;
const char *mirror_directory = ".";
char *record_prefix = NULL;
char *record_transid_file = NULL;
struct jsession jsdebug;
struct jsession jsoutput;
struct jsession jsmirror;
char *ptr;
int64_t mirror_transid;
int64_t output_transid;
int64_t record_transid;
int64_t transid;
int input_fd;
struct stat st;
struct jfile *jf;
struct jdata *jd;
int ch;
while ((ch = getopt(ac, av, "2c:dfm:o:s:uvw:D:O:W:F")) != -1) {
switch(ch) {
case '2':
jmodes |= JMODEF_INPUT_FULL;
break;
case 'c':
trans_count = strtoll(optarg, &ptr, 0);
switch(*ptr) {
case 't':
trans_count *= 1024;
case 'g':
trans_count *= 1024;
case 'm':
trans_count *= 1024;
case 'k':
trans_count *= 1024;
break;
case 0:
break;
default:
fprintf(stderr, "Bad suffix for value specified with -c, use 'k', 'm', 'g', 't', or nothing\n");
usage(av[0]);
}
break;
case 'd':
jmodes |= JMODEF_DEBUG;
break;
case 'f':
jmodes |= JMODEF_LOOP_FOREVER;
break;
case 'v':
++verbose_opt;
break;
case 'm':
jmodes |= JMODEF_MIRROR;
if (strcmp(optarg, "none") != 0)
mirror_transid_file = optarg;
break;
case 'O':
jmodes |= JMODEF_OUTPUT_FULL;
case 'o':
jmodes |= JMODEF_OUTPUT;
if (strcmp(optarg, "none") != 0)
output_transid_file = optarg;
break;
case 's':
prefix_file_size = strtoll(optarg, &ptr, 0);
switch(*ptr) {
case 't':
prefix_file_size *= 1024;
case 'g':
prefix_file_size *= 1024;
case 'm':
prefix_file_size *= 1024;
case 'k':
prefix_file_size *= 1024;
break;
case 0:
break;
default:
fprintf(stderr, "Bad suffix for value specified with -s, use 'k', 'm', 'g', 't', or nothing\n");
usage(av[0]);
}
break;
case 'u':
jdirection = JD_BACKWARDS;
break;
case 'W':
jmodes |= JMODEF_RECORD_TMP;
case 'w':
jmodes |= JMODEF_RECORD;
record_prefix = optarg;
asprintf(&record_transid_file, "%s.transid", record_prefix);
break;
case 'D':
mirror_directory = optarg;
break;
case 'F':
++fsync_opt;
break;
default:
fprintf(stderr, "unknown option: -%c\n", optopt);
usage(av[0]);
}
}
if ((jmodes & JMODEF_COMMAND_MASK) == 0)
usage(av[0]);
if (optind > ac + 1) {
fprintf(stderr, "Only one input file or prefix may be specified,\n"
"or zero if stdin is to be the input.\n");
usage(av[0]);
}
if (strcmp(mirror_directory, ".") != 0) {
struct stat sb;
if (stat(mirror_directory, &sb) != 0) {
perror ("Could not stat mirror directory");
usage(av[0]);
}
if (!S_ISDIR(sb.st_mode))
{
fprintf (stderr, "Mirror directory '%s' is not a directory\n", mirror_directory);
usage(av[0]);
}
}
if (jdirection == JD_BACKWARDS && (jmodes & (JMODEF_RECORD|JMODEF_OUTPUT))) {
fprintf(stderr, "Undo mode is only good in mirroring mode and "
"cannot be mixed with other modes.\n");
exit(1);
}
jf = NULL;
if (optind == ac) {
input_prefix = "<stdin>";
input_fd = 0;
if (fstat(0, &st) < 0 || !S_ISREG(st.st_mode)) {
jmodes |= JMODEF_INPUT_PIPE;
if (jdirection == JD_BACKWARDS) {
fprintf(stderr, "Cannot scan journals on pipes backwards\n");
usage(av[0]);
}
}
jf = jopen_fd(input_fd);
} else if (stat(av[optind], &st) == 0 && S_ISREG(st.st_mode)) {
input_prefix = av[optind];
if ((input_fd = open(av[optind], O_RDONLY)) != 0) {
jf = jopen_fd(input_fd);
} else {
jf = NULL;
}
} else {
input_prefix = av[optind];
jf = jopen_prefix(input_prefix, 0);
jmodes |= JMODEF_INPUT_PREFIX;
}
if (jf == NULL) {
fprintf(stderr, "Unable to open input %s: %s\n",
input_prefix, strerror(errno));
exit(1);
}
get_transid_from_file(output_transid_file, &output_transid,
JMODEF_OUTPUT_TRANSID_GOOD);
get_transid_from_file(mirror_transid_file, &mirror_transid,
JMODEF_MIRROR_TRANSID_GOOD);
get_transid_from_file(record_transid_file, &record_transid,
JMODEF_RECORD_TRANSID_GOOD);
transid = LLONG_MAX;
if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && output_transid < transid)
transid = output_transid;
if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && mirror_transid < transid)
transid = mirror_transid;
if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && record_transid < transid)
transid = record_transid;
if ((jmodes & JMODEF_TRANSID_GOOD_MASK) == 0)
transid = 0;
if (verbose_opt) {
if (jmodes & JMODEF_OUTPUT) {
fprintf(stderr, "Starting transid for OUTPUT: %016jx\n",
(uintmax_t)output_transid);
}
if (jmodes & JMODEF_MIRROR) {
fprintf(stderr, "Starting transid for MIRROR: %016jx\n",
(uintmax_t)mirror_transid);
}
if (jmodes & JMODEF_RECORD) {
fprintf(stderr, "Starting transid for RECORD: %016jx\n",
(uintmax_t)record_transid);
}
}
if (strcmp(mirror_directory, ".") != 0) {
if (chdir (mirror_directory) != 0) {
perror ("Could not enter mirror directory");
exit (1);
}
}
if (jmodes & JMODEF_RECORD) {
if (jrecord_init(record_prefix) < 0) {
fprintf(stderr, "Unable to initialize file set for: %s\n",
record_prefix);
exit(1);
}
if (jmodes & JMODEF_MIRROR) {
fork_subprocess(jf, jscan_do_mirror, record_prefix,
mirror_transid_file,
mirror_directory, mirror_transid);
}
if (jmodes & JMODEF_OUTPUT) {
fork_subprocess(jf, jscan_do_output, record_prefix,
record_transid_file,
NULL, output_transid);
}
jscan_do_record(jf, record_transid_file, record_prefix, record_transid);
exit(0);
}
if (jmodes & JMODEF_INPUT_PREFIX) {
if ((jmodes & JMODEF_OUTPUT) && (jmodes & JMODEF_MIRROR)) {
fork_subprocess(jf, jscan_do_mirror, input_prefix,
mirror_transid_file,
mirror_directory, mirror_transid);
jscan_do_output(jf, output_transid_file, NULL, output_transid);
} else if (jmodes & JMODEF_OUTPUT) {
jscan_do_output(jf, output_transid_file, NULL, output_transid);
} else if (jmodes & JMODEF_MIRROR) {
jscan_do_mirror(jf, mirror_transid_file, mirror_directory,
mirror_transid);
} else if (jmodes & JMODEF_DEBUG) {
jscan_do_debug(jf, NULL, NULL, 0);
}
exit(0);
}
if ((jmodes & JMODEF_TRANSID_GOOD_MASK) && !(jmodes & JMODEF_INPUT_PIPE))
jd = jseek(jf, transid, jdirection);
else
jd = jread(jf, NULL, jdirection);
jmodes |= JMODEF_MEMORY_TRACKING;
jsession_init(&jsdebug, jf, jdirection,
NULL, 0);
jsession_init(&jsoutput, jf, jdirection,
output_transid_file, output_transid);
jsession_init(&jsmirror, jf, jdirection,
mirror_transid_file, mirror_transid);
jsmirror.ss_mirror_directory = mirror_directory;
while (jd != NULL) {
if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
dump_debug(&jsdebug, jd);
if ((jmodes & JMODEF_OUTPUT) && jsession_check(&jsoutput, jd))
dump_output(&jsoutput, jd);
if ((jmodes & JMODEF_MIRROR) && jsession_check(&jsmirror, jd))
dump_mirror(&jsmirror, jd);
if (donecheck(jdirection, jd, transid)) {
jfree(jf, jd);
break;
}
jd = jread(jf, jd, jdirection);
}
jclose(jf);
jsession_term(&jsdebug);
jsession_term(&jsoutput);
jsession_term(&jsmirror);
return(0);
}
static int
donecheck(enum jdirection direction, struct jdata *jd, int64_t transid)
{
if (direction == JD_FORWARDS) {
if (jd->jd_transid > transid && trans_count && --trans_count == 0)
return(1);
} else {
if (jd->jd_transid <= transid && trans_count && --trans_count == 0)
return(1);
}
return(0);
}
static void
fork_subprocess(struct jfile *jftoclose,
void (*func)(struct jfile *, const char *, const char *, int64_t),
const char *input_prefix, const char *transid_file, const char *info,
int64_t transid)
{
pid_t pid;
struct jfile *jf;
if ((pid = fork()) == 0) {
jmodes &= ~(JMODEF_DEBUG | JMODEF_INPUT_PIPE);
jmodes |= JMODEF_LOOP_FOREVER;
jclose(jftoclose);
jf = jopen_prefix(input_prefix, 0);
jmodes |= JMODEF_INPUT_PREFIX;
func(jf, transid_file, info, transid);
jclose(jf);
exit(0);
} else if (pid < 0) {
fprintf(stderr, "fork(): %s\n", strerror(errno));
exit(1);
}
}
static void
jscan_do_output(struct jfile *jf, const char *output_transid_file, const char *dummy __unused, int64_t transid)
{
struct jdata *jd;
struct jsession jsdebug;
struct jsession jsoutput;
jsession_init(&jsdebug, jf, jdirection,
NULL, 0);
jsession_init(&jsoutput, jf, jdirection,
output_transid_file, transid);
if ((jmodes & JMODEF_OUTPUT_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
jd = jseek(jf, transid, jdirection);
else
jd = jread(jf, NULL, jdirection);
while (jd != NULL) {
if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
dump_debug(&jsdebug, jd);
if (jsession_check(&jsoutput, jd))
dump_output(&jsoutput, jd);
if (donecheck(jdirection, jd, transid)) {
jfree(jf, jd);
break;
}
jd = jread(jf, jd, jdirection);
}
jsession_term(&jsdebug);
jsession_term(&jsoutput);
}
static void
jscan_do_mirror(struct jfile *jf, const char *mirror_transid_file, const char *mirror_directory, int64_t transid)
{
struct jsession jsdebug;
struct jsession jsmirror;
struct jdata *jd;
jsession_init(&jsdebug, jf, jdirection,
NULL, 0);
jsession_init(&jsmirror, jf, jdirection,
mirror_transid_file, transid);
jsmirror.ss_mirror_directory = mirror_directory;
if ((jmodes & JMODEF_MIRROR_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
jd = jseek(jf, transid, jdirection);
else
jd = jread(jf, NULL, jdirection);
while (jd != NULL) {
if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
dump_debug(&jsdebug, jd);
if (jsession_check(&jsmirror, jd))
dump_mirror(&jsmirror, jd);
if (donecheck(jdirection, jd, transid)) {
jfree(jf, jd);
break;
}
jd = jread(jf, jd, jdirection);
}
jsession_term(&jsdebug);
jsession_term(&jsmirror);
}
static void
jscan_do_record(struct jfile *jfin, const char *record_transid_file, const char *prefix, int64_t transid)
{
struct jsession jsdebug;
struct jsession jsrecord;
struct jdata *jd;
jsession_init(&jsdebug, jfin, jdirection,
NULL, 0);
jsession_init(&jsrecord, jfin, jdirection,
record_transid_file, transid);
assert(jdirection == JD_FORWARDS);
jsrecord.ss_jfout = jopen_prefix(prefix, 1);
if (jsrecord.ss_jfout == NULL) {
fprintf(stderr, "Unable to open prefix set for writing: %s\n", prefix);
exit(1);
}
if ((jmodes & JMODEF_RECORD_TRANSID_GOOD) && !(jmodes & JMODEF_INPUT_PIPE))
jd = jseek(jfin, transid, jdirection);
else
jd = jread(jfin, NULL, jdirection);
while (jd != NULL) {
if ((jmodes & JMODEF_DEBUG) && jsession_check(&jsdebug, jd))
dump_debug(&jsdebug, jd);
if (jsession_check(&jsrecord, jd))
dump_record(&jsrecord, jd);
if (donecheck(jdirection, jd, transid)) {
jfree(jfin, jd);
break;
}
jd = jread(jfin, jd, jdirection);
}
jclose(jsrecord.ss_jfout);
jsrecord.ss_jfout = NULL;
jsession_term(&jsdebug);
jsession_term(&jsrecord);
}
static void
jscan_do_debug(struct jfile *jf, const char *dummy1 __unused,
const char *dummy __unused, int64_t transid __unused)
{
struct jsession jsdebug;
struct jdata *jd;
jsession_init(&jsdebug, jf, jdirection,
NULL, 0);
jd = NULL;
while ((jd = jread(jf, jd, jdirection)) != NULL) {
if (jsession_check(&jsdebug, jd))
dump_debug(&jsdebug, jd);
if (donecheck(jdirection, jd, transid)) {
jfree(jf, jd);
break;
}
}
jsession_term(&jsdebug);
}
static void
usage(const char *av0)
{
fprintf(stderr,
"%s [-2dfuvF] [-D dir] [-m mirror_transid_file/none]\n"
"\t[-o/O output_transid_file/none]\n"
"\t[-s size[kmgt]] -w/W record_prefix] [input_file/input_prefix]\n",
av0);
exit(1);
}