sparse-tools/src/blksync.c

260 lines
6.3 KiB
C

#include <getopt.h>
#include <hash.h>
#include <blksync.h>
char sHelp[] = "usage: dsync [ -v ] [ -4 | -6 ] [ -depth | -r ] [ -s ] [ -p <port> ] [ -b <blocksize> ] <file> [ host ]\n"
"\t-h\tshow this help\n"
"\t-v\targ_verbose\n"
"\t-depth\tlisten and wait for connection\n"
"\t-r\targ_reverse_mode connection (sync from arg_listener_mode instead of to)\n"
"\t-b\tset non-default blocksize\n"
"\t-p\tuse tcp port <port> for listening or connecting\n"
"\t-4\tuse ipv4\n"
"\t-6\tuse ipv6\n"
"\t-s\tsimulate (don't change target file)\n"
"\t-t\ttest mode, only output block hashes\n"
;
int af = AF_UNSPEC;
uint16_t port = 13487;
int main_listen();
int main_connect();
int show_help(char *msg){
if (msg && strlen(msg))
fprintf(stdout, "%s\n", msg);
fprintf(stdout, sHelp);
fflush(stdout);
return 0;
}
int main(int argc, char *argv[])
{
int opt;
long _l;
bsync_engine_t *engine = create_engine();
if (!engine)
{
fprintf(stderr, "could not allocate engine memory\n");
fflush(stderr);
exit(EXIT_FAILURE);
}
while ((opt = getopt(argc, argv, "v46lrb:p:shtd"))>0){
switch (opt){
case 'h':
return show_help(NULL);
case 'v':
engine->ds_flags |= DS_VERBOSE;
break;
case 'l':
engine->ds_flags |= DS_LISTENER;
break;
case 'r':
engine->ds_flags |= DS_REVERSE;
break;
case 's':
engine->ds_flags |= DS_SIMULATE;
break;
case '4':
af = AF_INET;
break;
case '6':
af = AF_INET6;
break;
case 'p':
_l = strtol(optarg, NULL, 0 );
if (_l > 65535)
return show_help("port must be less then 65536");
port = _l;
break;
case 'b':
_l = strtol(optarg, NULL, 0 );
if (_l >= (1L<<32))
return show_help("blocksize must be smaller then 1<<32\n");
engine->blocksize = _l;
break;
case 't':
engine->sr_mode = SR_TEST;
break;
case 'd':
engine->ds_flags |= DS_DUMPBLOCKS;
break;
}
}
if (engine->sr_mode != SR_TEST)
engine->sr_mode = ((engine->ds_flags & DS_LISTENER)==DS_LISTENER) == ((engine->ds_flags & DS_REVERSE)==DS_REVERSE) ? SR_SENDER : SR_RECEIVER;
if (optind >= argc)
return show_help("need a file or device to sync");
strncpy( engine->filename, argv[optind++], sizeof(engine->filename));
if ((engine->sr_mode != SR_TEST) && (optind >= argc) && !(engine->ds_flags & DS_LISTENER))
show_help("need a hostname\n");
if (optind < argc)
strncpy(engine->hostname, argv[optind++], sizeof(engine->hostname));
engine->file = open( engine->filename, engine->sr_mode == SR_SENDER ? O_RDONLY : (O_RDWR | O_CREAT), S_IRWXU | S_IRWXG | S_IRWXO);
if (engine->file < 0){
fprintf(stderr, "could not open %s\n", engine->filename);
exit(EXIT_FAILURE);
}
engine->filesize = lseek( engine->file, 0, SEEK_END);
lseek(engine->file, 0, SEEK_SET);
if (!(engine->sr_mode == SR_TEST)) {
if (engine->ds_flags & DS_LISTENER) {
assert_x(accept_connection(engine, engine->hostname, port), NULL);
} else {
assert_x(connect_to_host(engine, engine->hostname, port), NULL);
}
}
if (engine->ds_flags & DS_VERBOSE)
dump_engine(engine);
switch (engine->sr_mode){
case SR_SENDER:
bs_sender(engine);
break;
case SR_RECEIVER:
bs_receiver(engine);
break;
case SR_TEST:
bs_test(engine);
break;
default:
exit(EXIT_FAILURE);
}
close(engine->file);
close(engine->listenSocket);
close(engine->clientSocket);
if (engine)
free(engine);
}
int accept_connection(bsync_engine_t *engine, char *hostname, int port){
struct addrinfo hints;
struct addrinfo *result, *ai;
int one = 1;
char sport[12];
sprintf(sport, "%d", port);
memset( &hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int s = getaddrinfo(hostname[0] ? hostname : NULL, sport, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (ai = result; ai != NULL; ai = ai->ai_next) {
engine->listenSocket = socket( ai->ai_family, ai->ai_socktype, ai->ai_protocol );
if (engine->listenSocket < 0)
continue;
setsockopt( engine->listenSocket, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
if (bind(engine->listenSocket, ai->ai_addr, ai->ai_addrlen)!=-1)
break;
close(engine->listenSocket);
}
if (ai == NULL){
fprintf(stderr, "could not bind to %s : %d\n", hostname, port);
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
listen(engine->listenSocket, 1);
engine->clientSocket = accept( engine->listenSocket, NULL, 0);
if (engine->clientSocket < 0){
fprintf(stderr, "no one connected\n");
exit(EXIT_FAILURE);
}
return 0;
}
int connect_to_host(bsync_engine_t *engine, char *hostname, int port){
struct addrinfo hints;
struct addrinfo *result, *ai;
char sport[12];
sprintf(sport, "%d", port);
memset( &hints, 0, sizeof(hints));
hints.ai_family = af;
hints.ai_socktype = SOCK_STREAM;
int s = getaddrinfo(hostname, sport, &hints, &result);
if (s != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
for (ai = result; ai != NULL; ai = ai->ai_next) {
engine->clientSocket = socket( ai->ai_family, ai->ai_socktype, ai->ai_protocol );
if (engine->clientSocket < 0)
continue;
if (connect(engine->clientSocket, ai->ai_addr, ai->ai_addrlen)!=-1)
break;
close(engine->clientSocket);
}
if (ai == NULL){
fprintf(stderr, "could not connect to host %s : %d\n", hostname, port);
exit(EXIT_FAILURE);
}
freeaddrinfo(result);
return 0;
}
void dump_engine(bsync_engine_t *engine){
fprintf( stderr, "blksync engine dump:\n");
fprintf( stderr,"sr_mode=%x ds_flags=%x\n", engine->sr_mode, engine->ds_flags);
fprintf( stderr,"filename=%s\n", engine->filename);
fprintf( stderr,"filesize=%ld (0x%lx)\n", engine->filesize, engine->filesize);
fprintf( stderr,"hostname=%s\n", engine->hostname);
fprintf( stderr,"blocksize=%d\n", engine->blocksize);
fprintf( stderr,"file=%d\n", engine->file);
fprintf( stderr,"listenSocket=%d\n", engine->listenSocket);
fprintf( stderr,"clientSocket=%d\n", engine->clientSocket);
fprintf( stderr,"\n");
fflush(stderr);
}
bsync_engine_t *create_engine(){
bsync_engine_t *engine = malloc(sizeof(bsync_engine_t));
if (engine) {
memset(engine, 0, sizeof(bsync_engine_t));
pthread_mutex_init(&engine->m_file, NULL);
pthread_mutex_init(&engine->m_socket, NULL);
engine->t_start = time(NULL);
}
return engine;
}