redmine

rsyncdirect: Added support of custom arguments

... ... @@ -5,8 +5,9 @@ if CLSYNC
bin_PROGRAMS = clsync
clsync_SOURCES = calc.c cluster.c error.c fileutils.c glibex.c \
indexes.c main.c malloc.c sync.c calc.h cluster.h fileutils.h \
glibex.h main.h port-hacks.h sync.h common.h control.h
indexes.c main.c malloc.c stringex.c sync.c calc.h cluster.h \
fileutils.h glibex.h main.h port-hacks.h stringex.h sync.h \
common.h control.h
clsync_CFLAGS = $(AM_CFLAGS)
... ...
... ... @@ -187,29 +187,35 @@ struct eventinfo {
};
typedef struct eventinfo eventinfo_t;
struct thread_callbackfunct_arg {
char *excfpath;
char *incfpath;
};
typedef struct thread_callbackfunct_arg thread_callbackfunct_arg_t;
typedef int (*thread_callbackfunct_t)(ctx_t *ctx_p, char **argv);
typedef int (*thread_callbackfunct_t)(ctx_t *ctx_p, thread_callbackfunct_arg_t *arg_p);
struct threadinfo {
int thread_num;
uint32_t iteration;
thread_callbackfunct_t callback;
char **argv;
pthread_t pthread;
int exitcode;
int errcode;
state_t state;
ctx_t *ctx_p;
time_t starttime;
time_t expiretime;
int child_pid;
GHashTable *fpath2ei_ht; // file path -> event information
int try_n;
int thread_num;
uint32_t iteration;
thread_callbackfunct_t callback;
thread_callbackfunct_arg_t *callback_arg;
char **argv;
pthread_t pthread;
int exitcode;
int errcode;
state_t state;
ctx_t *ctx_p;
time_t starttime;
time_t expiretime;
int child_pid;
GHashTable *fpath2ei_ht; // file path -> event information
int try_n;
// for so-synchandler
int n;
api_eventinfo_t *ei;
int n;
api_eventinfo_t *ei;
};
typedef struct threadinfo threadinfo_t;
... ...
... ... @@ -98,6 +98,8 @@ enum flags_enum {
CONFIGBLOCKINHERITS = 18|OPTION_LONGOPTONLY,
MONITOR = 19|OPTION_LONGOPTONLY,
SYNCHANDLERARGS = 20|OPTION_LONGOPTONLY,
};
typedef enum flags_enum flags_t;
... ... @@ -242,6 +244,9 @@ struct ctx {
#endif
void *indexes_p;
void *fsmondata;
char *synchandler_argv[MAXARGUMENTS];
int synchandler_argc;
};
typedef struct ctx ctx_t;
... ...
/*
clsync - file tree sync utility based on inotify
clsync - file tree sync utility based on inotify/kqueue/bsm
Copyright (C) 2013 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
Copyright (C) 2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
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
... ... @@ -26,6 +26,7 @@
#include <grp.h> // For getgrnam()
#include "error.h"
#include "stringex.h"
#include "sync.h"
#include "malloc.h"
#include "cluster.h"
... ... @@ -38,6 +39,7 @@ static const struct option long_options[] =
{
{"watch-dir", required_argument, NULL, WATCHDIR},
{"sync-handler", required_argument, NULL, SYNCHANDLER},
{"--", required_argument, NULL, SYNCHANDLERARGS},
{"rules-file", required_argument, NULL, RULESFILE},
{"destination-dir", required_argument, NULL, DESTDIR},
{"mode", required_argument, NULL, MODE},
... ... @@ -191,7 +193,8 @@ int clsyncapi_getapiversion() {
* @retval NULL On error
*
*/
char *parameter_get(ctx_t *ctx_p, char *variable_name) {
char *parameter_get(char *variable_name, void *_ctx_p) {
ctx_t *ctx_p = _ctx_p;
const struct option *long_option_p = long_options;
int param_id = -1;
... ... @@ -222,7 +225,8 @@ char *parameter_get(ctx_t *ctx_p, char *variable_name) {
* @retval NULL On error
*
*/
char *parameter_expand(ctx_t *ctx_p, char *arg, int ignorewarnings) {
char *parameter_expand(ctx_t *ctx_p, char *arg, int exceptionflags, char *(*parameter_get)(char *variable_name, void *arg), void *parameter_get_arg) {
debug(9, "");
char *ret = NULL;
size_t ret_size = 0, ret_len = 0;
... ... @@ -239,8 +243,12 @@ char *parameter_expand(ctx_t *ctx_p, char *arg, int ignorewarnings) {
switch (*ptr) {
case 0:
if (ret == NULL) {
debug(3, "Expanding value \"%s\" to \"%s\" (case #1)", arg, arg);
return arg;
}
ret[ret_len] = 0;
debug(3, "Expanding value \"%s\" to \"%s\"", arg, ret);
debug(3, "Expanding value \"%s\" to \"%s\" (case #0)", arg, ret);
free(arg);
return ret;
case '%': {
... ... @@ -257,24 +265,33 @@ char *parameter_expand(ctx_t *ctx_p, char *arg, int ignorewarnings) {
switch (*ptr_nest) {
case 0:
ret[ret_len] = 0;
if (!(ignorewarnings&1))
warning("Unexpected end of macro-substitution \"%%%s\" in value \"%s\"; result value is \"%s\"", ptr_nest, arg, ret);
if (!(exceptionflags&1))
warning("Unexpected end of macro-substitution \"%s\" in value \"%s\"; result value is \"%s\"", ptr, arg, ret);
free(arg);
return ret;
case '%': {
char *variable_name;
char *variable_value;
size_t variable_value_len;
nest_searching = 0;
*ptr_nest = 0;
char *variable_name = &ptr[1];
char *variable_value = parameter_get(ctx_p, variable_name);
if (variable_value == NULL) {
if (!(ignorewarnings&2))
warning("Variable \"%s\" is not set (%s)", variable_name, strerror(errno));
if (ptr[1] >= 'A' && ptr[1] <= 'Z' && (exceptionflags&4)) { // Lazy substitution, preserving the value
variable_value = ptr;
variable_value_len = (ptr_nest - ptr + 1);
} else { // Substituting
*ptr_nest = 0;
variable_name = &ptr[1];
variable_value = parameter_get(variable_name, parameter_get_arg);
if (variable_value == NULL) {
if (!(exceptionflags&2))
warning("Variable \"%s\" is not set (%s)", variable_name, strerror(errno));
*ptr_nest = '%';
errno = 0;
break;
}
*ptr_nest = '%';
errno = 0;
break;
variable_value_len = strlen(variable_value);
}
*ptr_nest = '%';
size_t variable_value_len = strlen(variable_value);
if (ret_len+variable_value_len+1 >= ret_size) {
ret_size = ret_len+variable_value_len+1 + ALLOC_PORTION;
ret = xrealloc(ret, ret_size);
... ... @@ -302,6 +319,22 @@ char *parameter_expand(ctx_t *ctx_p, char *arg, int ignorewarnings) {
return arg;
}
static int synchandler_arg(char *arg, size_t arg_len, void *_ctx_p) {
ctx_t *ctx_p = _ctx_p;
debug(9, "(\"%s\", %u, %p)", arg, arg_len, _ctx_p);
if (ctx_p->synchandler_argc >= MAXARGUMENTS-2) {
errno = E2BIG;
error("There're too many sync-handler arguments "
"(%u > "XTOSTR(MAXARGUMENTS-2)"; arg == \"%s\").",
arg);
return errno;
}
ctx_p->synchandler_argv[ctx_p->synchandler_argc++] = arg;
return 0;
}
int parse_parameter(ctx_t *ctx_p, uint16_t param_id, char *arg, paramsource_t paramsource) {
#ifdef _DEBUG
fprintf(stderr, "Force-Debug: parse_parameter(): %i: %i = \"%s\"\n", paramsource, param_id, arg);
... ... @@ -325,7 +358,8 @@ int parse_parameter(ctx_t *ctx_p, uint16_t param_id, char *arg, paramsource_t pa
}
if (arg != NULL) {
arg = parameter_expand(ctx_p, arg, 0);
if (param_id != SYNCHANDLERARGS)
arg = parameter_expand(ctx_p, arg, 0, parameter_get, ctx_p);
if (ctx_p->flags_values_raw[param_id] != NULL)
free(ctx_p->flags_values_raw[param_id]);
... ... @@ -660,6 +694,9 @@ int parse_parameter(ctx_t *ctx_p, uint16_t param_id, char *arg, paramsource_t pa
}
break;
}
case SYNCHANDLERARGS:
str_splitargs(arg, synchandler_arg, ctx_p);
break;
default:
if(arg == NULL)
ctx_p->flags[param_id]++;
... ... @@ -682,7 +719,7 @@ int arguments_parse(int argc, char *argv[], struct ctx *ctx_p) {
char *optstring_ptr = optstring;
const struct option *lo_ptr = long_options;
while(lo_ptr->name != NULL) {
while (lo_ptr->name != NULL) {
if(!(lo_ptr->val & (OPTION_CONFIGONLY|OPTION_LONGOPTONLY))) {
*(optstring_ptr++) = lo_ptr->val & 0xff;
... ... @@ -702,20 +739,31 @@ int arguments_parse(int argc, char *argv[], struct ctx *ctx_p) {
#endif
// Parsing arguments
while(1) {
while (1) {
c = getopt_long(argc, argv, optstring, long_options, &option_index);
if (c == -1) break;
int ret = parse_parameter(ctx_p, c, optarg == NULL ? NULL : strdup(optarg), PS_ARGUMENT);
if(ret) return ret;
if (ret) return ret;
}
if (optind < argc) {
while (ctx_p->synchandler_argc)
free(ctx_p->synchandler_argv[--ctx_p->synchandler_argc]);
if ((optind+1 != argc) || (*argv[optind])) { // If there's only "" after the "--", just reset "synchandler_argc" to "0", otherwise:
do {
if (synchandler_arg(argv[optind++], 0, ctx_p))
return errno;
} while (optind < argc);
}
}
if(optind+1 < argc)
syntax();
return 0;
}
void gkf_parse(ctx_t *ctx_p, GKeyFile *gkf) {
debug(9, "");
char *config_block = ctx_p->config_block;
do {
const struct option *lo_ptr = long_options;
... ... @@ -758,7 +806,7 @@ int configs_parse(ctx_t *ctx_p) {
return 0;
}
debug(1, "Trying config-file \"%s\"", ctx_p->config_path);
debug(1, "Trying config-file \"%s\" (case #0)", ctx_p->config_path);
if (!g_key_file_load_from_file(gkf, ctx_p->config_path, G_KEY_FILE_NONE, &g_error)) {
error("Cannot open/parse file \"%s\" (g_error #%u.%u: %s)", ctx_p->config_path, g_error->domain, g_error->code, g_error->message);
g_key_file_free(gkf);
... ... @@ -789,7 +837,7 @@ int configs_parse(ctx_t *ctx_p) {
} else
memcpy(config_path_real, *config_path_p, config_path_len+1);
debug(1, "Trying config-file \"%s\"", config_path_real);
debug(1, "Trying config-file \"%s\" (case #1)", config_path_real);
if(!g_key_file_load_from_file(gkf, config_path_real, G_KEY_FILE_NONE, NULL)) {
debug(1, "Cannot open/parse file \"%s\"", config_path_real);
config_path_p++;
... ... @@ -819,6 +867,18 @@ void options_cleanup(ctx_t *ctx_p) {
i++;
}
#if 0
{
int i = 0;
while (i < ctx_p->synchandler_argc) {
// TODO: FIXME: Here's a double "free":
free(ctx_p->synchandler_argv[i]);
ctx_p->synchandler_argv[i] = NULL;
i++;
}
ctx_p->synchandler_argc = 0;
}
#endif
return;
}
... ... @@ -1205,10 +1265,18 @@ int main(int argc, char *argv[]) {
}
if (ctx_p->dump_path == NULL) {
ctx_p->dump_path = parameter_expand(ctx_p, strdup(DEFAULT_DUMPDIR), 2);
ctx_p->dump_path = parameter_expand(ctx_p, strdup(DEFAULT_DUMPDIR), 2, parameter_get, ctx_p);
ctx_p->flags_values_raw[DUMPDIR] = ctx_p->dump_path;
}
{
int i = 0;
while (i < ctx_p->synchandler_argc) {
ctx_p->synchandler_argv[i] = parameter_expand(ctx_p, strdup(ctx_p->synchandler_argv[i]), 4, parameter_get, ctx_p);
i++;
}
}
debug(4, "debugging flags: %u %u %u %u", ctx_p->flags[OUTPUT_METHOD], ctx_p->flags[QUIET], ctx_p->flags[VERBOSE], ctx_p->flags[DEBUG]);
main_status_update(ctx_p, STATE_STARTING);
... ...
... ... @@ -19,4 +19,5 @@
extern int main_rehash(ctx_t *ctx_p);
extern int main_status_update(ctx_t *ctx_p, state_t state);
extern char *parameter_expand(ctx_t *ctx_p, char *arg, int ignorewarnings, char *(*parameter_get)(char *variable_name, void *arg), void *parameter_get_arg);
... ...
... ... @@ -24,6 +24,9 @@
#include "error.h"
void *xmalloc(size_t size) {
#ifdef _DEBUG
debug(20, "(%u)", size);
#endif
#ifdef PARANOID
size++; // Just in case
#endif
... ... @@ -40,6 +43,9 @@ void *xmalloc(size_t size) {
}
void *xcalloc(size_t nmemb, size_t size) {
#ifdef _DEBUG
debug(20, "(%u, %u)", nmemb, size);
#endif
#ifdef PARANOID
nmemb++; // Just in case
size++; // Just in case
... ... @@ -55,6 +61,9 @@ void *xcalloc(size_t nmemb, size_t size) {
}
void *xrealloc(void *oldptr, size_t size) {
#ifdef _DEBUG
debug(20, "(%p, %u)", oldptr, size);
#endif
#ifdef PARANOID
size++; // Just in case
#endif
... ...
... ... @@ -7,7 +7,7 @@
.SH NAME
clsync \- live sync tool, written in GNU C
.SH SYNOPSIS
.B clsync [ ... ]
.B clsync [ ... ] -- [ sync\-handler\-arguments ]
.SH DESCRIPTION
.B clsync
executes
... ... @@ -106,8 +106,7 @@ calls
.IR rsyncdirect
.RS
calls rsync by path
.IR sync\-handler " directly (inflexible and unreliable, should be used only
as a proof of concept)"
.IR sync\-handler " directly"
.RE
.IR rsyncshell
.RS
... ... @@ -1045,26 +1044,50 @@ case
.RS
Executes for every sync:
.br
.I sync\-handler
\-\-inplace \-aH \-\-delete\-before [\-\-exclude\-from
.I rsync\-exclude\-listpath
]
\-\-include\-from
.I rsync\-listpath
\-\-exclude '*'
.I watch-dir/ dest-dir/
.I sync\-handler sync\-handler\-arguments
In this case,
.I sync\-handler
is supposed to be a path to
.B rsync
binary.
Default value for
.I sync\-handler\-arguments
is
.RS
\-aH \-\-delete \-\-exclude\-from %EXCLUDE\-LIST% \-\-include\-from
%INCLUDE-LIST% --exclude='*' %watch-dir%/ %destination-dir%/
.RE
if option
.I \-\-rsync-\-prefer\-include
is not set and
.RS
\-aH \-\-delete \-\-include\-from %INCLUDE-LIST% --exclude='*' %watch-dir%/
%destination-dir%/
.RE
if the option is set
Error code "24" from
.I sync\-handler
will be ignored in this case.
This case is supposed to be used only as a proof of concept.
Additional substitutions:
.RS
%INCLUDE-LIST%
.RS
Is replaced by path to include list file
.RE
%EXCLUDE-LIST%
.RS
Is replaced by path to exclude list file
.RE
.RE
Recommended case.
.RE
case
... ... @@ -1135,7 +1158,7 @@ be able to kill the child.
See example file "clsync-synchandler-rsyncso.c".
Recommended case. IMHO, this way is the best.
Recommended case.
.RE
case
... ...
/*
clsync - file tree sync utility based on inotify/kqueue/bsm
Copyright (C) 2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
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 3 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, see <http://www.gnu.org/licenses/>.
*/
#include <stdlib.h> // free()
#include <string.h> // strtok_r()
#include <errno.h> // errno
#include "malloc.h"
#include "error.h"
static int _str_splitargs(
char *ptr,
char **arg_start_p,
int quotes,
int (*handler)(char *, size_t, void *),
char *additional_arg
) {
char *arg_start, *arg;
size_t arg_len;
int rc;
arg_start = *arg_start_p;
*arg_start_p = &ptr[1];
arg_len = ptr - arg_start;
if (arg_len == 0) // Skipping nearby spaces
return 0;
arg = xmalloc(arg_len+1);
if (quotes) {
int s, d;
s = d = 0;
while (s < arg_len) {
if (arg_start[s])
arg[d++] = arg_start[s];
s++;
}
arg_len = d;
} else
memcpy(arg, arg_start, arg_len);
#ifdef _DEBUG
debug(15, "%p %p %i: <%s>", arg_start, ptr, arg_len, arg);
#endif
arg[arg_len] = 0;
if ((rc = handler(arg, arg_len, additional_arg))) {
free(arg);
return rc;
}
return 0;
}
int str_splitargs(
char *instr,
int (*handler)(char *, size_t, void *),
void *arg
) {
debug(9, "");
char *arg_start, *ptr;
int quotes = 0;
ptr = instr;
arg_start = instr;
while (1) {
ptr = strpbrk(ptr, " \t\"\'");
#ifdef _DEBUG
debug(10, "ptr == %p", ptr);
#endif
if (ptr == NULL)
break;
#ifdef _DEBUG
debug(10, "*ptr == \"%c\" (%i)", *ptr, *ptr);
#endif
switch (*(ptr++)) {
case ' ':
case '\t': {
int rc;
if ((rc = _str_splitargs(&ptr[-1], &arg_start, quotes, handler, arg)))
return rc;
quotes = 0;
break;
}
case '"':
ptr[-1] = 0;
quotes++;
while ((ptr = strchr(ptr, '"')) != NULL) {
// Checking for escaping
char *p;
p = &ptr[-1];
while (*p == '\\') {
p--;
#ifdef PARANOID
if (p < instr)
critical("Dangerous internal error");
#endif
}
if ((ptr-p)%2)
break;
}
if (ptr == NULL) {
errno = EINVAL;
error("Unterminated quote <\"> in string: <%s>", instr);
return errno;
}
*ptr = 0;
quotes++;
ptr++;
break;
case '\'':
ptr[-1] = 0;
quotes++;
ptr = strchr(ptr, '\'');
if (ptr == NULL) {
errno = EINVAL;
error("Unterminated quote <'> in string: <%s>", instr);
return errno;
}
*ptr = 0;
quotes++;
ptr++;
break;
}
}
return _str_splitargs(strchr(arg_start, 0), &arg_start, quotes, handler, arg);
}
... ...
/*
clsync - file tree sync utility based on inotify/kqueue/bsm
Copyright (C) 2014 Dmitry Yu Okunev <dyokunev@ut.mephi.ru> 0x8E30679C
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 3 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, see <http://www.gnu.org/licenses/>.
*/
extern int str_splitargs(const char *const instr, int (*handler)(char *outstr, size_t outstr_len, void *arg), void *arg);
... ...
This diff is collapsed. Click to expand it.