redmine

Initial commit, pt. 5

* Started inotify support
* Halted fanotify support
* Renamed from "fasync" to "clsync"
... ... @@ -9,4 +9,5 @@ insync-debug
# other:
build
rules
test*
... ...
... ... @@ -5,10 +5,11 @@ DEBUGCFLAGS = -pipe -Wall -Werror -ggdb3 -Wno-error=unused-variable -fstack-prot
objs=\
main.o\
output.o\
fasync.o\
fileutils.o\
sync.o\
malloc.o
binary=fasync
binary=clsync
binarydebug=$(binary)-debug
... ...
Notify engines:
It's suppported fanotify and inotify engines. However fanotify was not ready
at the moment of the code writting, so I was have to temporary fallback
to inotify.
I've freezed code that uses fanotify until it the fanotify will be ready.
The reason, why I didn't continue to use fanotify, that it was unable to
catch some events, like "directory creation" or "file deletion".
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -18,6 +18,7 @@
*/
#define _GNU_SOURCE
#define _XOPEN_SOURCE 700
#define _LARGEFILE64_SOURCE
#include <stdio.h>
... ... @@ -55,15 +56,17 @@ enum flags_enum {
BACKGROUND = 'b',
PTHREAD = 'p',
HELP = 'h',
DELAY = 't',
DEBUG = 'd',
QUITE = 'q',
VERBOSE = 'v'
VERBOSE = 'v',
OUTLISTSDIR = 'l',
FANOTIFY = 'f',
INOTIFY = 'i'
};
typedef enum flags_enum flags_t;
extern int flags[];
enum ruleaction_enum {
RULE_END = 0, // Terminator. To be able to end rules' chain
RULE_ACCEPT,
... ... @@ -78,3 +81,20 @@ struct rule {
};
typedef struct rule rule_t;
struct options {
int flags[(1<<8)];
char *watchdir;
char *actfpath;
char *rulfpath;
char *listoutdir;
int collectdelay;
int notifyengine;
};
enum notifyengine_enum {
NE_UNDEFINED = 0,
NE_FANOTIFY,
NE_INOTIFY
};
typedef enum notifyengine_enum notifyenfine_t;
... ...
... ... @@ -8,9 +8,12 @@
#define MAXRULES (1<<8)
#define MAXARGUMENTS (1<<8)
#define MAXTHREADS (1<<5)
#define FANOTIFY_FLAGS (FAN_CLOEXEC|FAN_UNLIMITED_QUEUE|FAN_UNLIMITED_MARKS)
#define FANOTIFY_EVFLAGS (O_LARGEFILE|O_RDONLY|O_CLOEXEC)
#define FANOTIFY_MARKMASK (FAN_CLOSE_WRITE)
#define FANOTIFY_MARKMASK (FAN_OPEN|FAN_MODIFY|FAN_CLOSE|FAN_ONDIR|FAN_EVENT_ON_CHILD)
#define DEFAULT_NOTIFYENGINE NE_INOTIFY
... ...
/*
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@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 "common.h"
#include "output.h"
#include "malloc.h"
char *fd2fpath_malloc(int fd) {
struct stat64 lstat;
if(fd <= 0) {
printf_e("Error: Invalid file descriptor supplied: fd2fpath_malloc(%i).\n", fd);
errno = EINVAL;
return NULL;
}
char *fpath = xmalloc((1<<8) + 2);
sprintf(fpath, "/proc/self/fd/%i", fd);
if(lstat64(fpath, &lstat)) {
printf_e("Error: Cannot fstat(%i, fstat): %s (errno: %i).\n", fd, strerror(errno), errno);
return NULL;
}
ssize_t fpathlen = lstat.st_size;
if(fpathlen > (1<<8))
fpath = xrealloc(fpath, fpathlen+2);
printf_ddd("Debug2: Getting file path from symlink \"%s\". Path length is: %i.\n", fpath, fpathlen);
if((fpathlen = readlink(fpath, fpath, fpathlen+1)) < 0) {
printf_e("Error: Cannot readlink(\"%s\", fpath, bufsize).\n", fpath);
return NULL;
}
printf_ddd("Debug2: The path is: \"%s\"\n", fpath);
fpath[fpathlen] = 0;
return fpath;
}
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@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 fasync_run(const char *dpath, const char *actfpath, rule_t *rules);
extern char *fd2fpath_malloc(int fd);
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -19,23 +19,26 @@
#include "common.h"
#include "output.h"
#include "fasync.h"
#include "sync.h"
#include "malloc.h"
int flags[(1<<8)] = {0};
static struct option long_options[] =
{
{"background", no_argument, &flags[BACKGROUND], BACKGROUND},
// {"pthread", no_argument, &flags[PTHREAD], PTHREAD}, // Not implemented, yet
{"verbose", no_argument, &flags[VERBOSE], VERBOSE},
{"debug", no_argument, &flags[DEBUG], DEBUG},
{"quite", no_argument, &flags[QUITE], QUITE},
{"help", no_argument, NULL, HELP},
{0, 0, 0, 0}
{"background", no_argument, NULL, BACKGROUND},
{"pthread", no_argument, NULL, PTHREAD}, // Not implemented, yet
{"collectdelay", required_argument, NULL, DELAY},
{"outlistsdir", required_argument, NULL, OUTLISTSDIR},
{"verbose", no_argument, NULL, VERBOSE},
{"debug", no_argument, NULL, DEBUG},
{"quite", no_argument, NULL, QUITE},
{"fanotify", no_argument, NULL, FANOTIFY},
{"inotify", no_argument, NULL, INOTIFY},
{"help", no_argument, NULL, HELP},
{0, 0, 0, 0}
};
int syntax() {
printf("syntax: fasync [flags] <watch dir> <action script> [file with rules regexps]\npossible flags:\n");
printf("syntax: clsync [flags] <watch dir> <action script> [file with rules regexps]\npossible flags:\n");
int i=0;
while(long_options[i].name != NULL) {
printf("\t--%-16s-%c\n", long_options[i].name, long_options[i].val);
... ... @@ -44,11 +47,11 @@ int syntax() {
exit(0);
}
char *parse_arguments(int argc, char *argv[], char **actfpath, char **exfpath) {
int parse_arguments(int argc, char *argv[], struct options *options) {
int c;
int option_index = 0;
while(1) {
c = getopt_long (argc, argv, "bpqvdh", long_options, &option_index);
c = getopt_long(argc, argv, "bl:t:pqvdhfa", long_options, &option_index);
if (c == -1) break;
switch (c) {
... ... @@ -56,28 +59,41 @@ char *parse_arguments(int argc, char *argv[], char **actfpath, char **exfpath) {
case 'h':
syntax();
break;
case 'l':
options->listoutdir = optarg;
break;
case 't':
options->collectdelay = atoi(optarg);
break;
case 'f':
options->notifyengine = NE_FANOTIFY;
break;
case 'i':
options->notifyengine = NE_INOTIFY;
break;
default:
flags[c]++;
options->flags[c]++;
break;
}
}
if(optind+1 >= argc)
syntax();
*actfpath = argv[optind+1];
options->actfpath = argv[optind+1];
if(optind+2 < argc)
*exfpath = argv[optind+2];
options->rulfpath = argv[optind+2];
return argv[optind];
options->watchdir = argv[optind];
return 0;
}
int parse_rules_fromfile(const char *exfpath, rule_t *rules) {
int parse_rules_fromfile(const char *rulfpath, rule_t *rules) {
char buf[BUFSIZ];
char *line_buf=NULL;
FILE *f = fopen(exfpath, "r");
FILE *f = fopen(rulfpath, "r");
if(f == NULL) {
printf_e("Error: Cannot open \"%s\" for reading: %s (errno: %i).\n", exfpath, strerror(errno), errno);
printf_e("Error: Cannot open \"%s\" for reading: %s (errno: %i).\n", rulfpath, strerror(errno), errno);
return errno;
}
... ... @@ -181,27 +197,30 @@ int becomedaemon() {
}
int main(int argc, char *argv[]) {
struct options options;
int ret = 0;
rule_t rules[MAXRULES];
char *actfpath, *exfpath=NULL;
char *dpath = parse_arguments(argc, argv, &actfpath, &exfpath);
out_init();
if(flags[DEBUG])
memset(&options, 0, sizeof(options));
options.notifyengine = DEFAULT_NOTIFYENGINE;
parse_arguments(argc, argv, &options);
out_init(options.flags);
if(options.flags[DEBUG])
debug_print_flags();
if(exfpath != NULL)
ret = parse_rules_fromfile(exfpath, rules);
if(options.rulfpath != NULL)
ret = parse_rules_fromfile(options.rulfpath, rules);
if(access(actfpath, X_OK) == -1) {
printf_e("Error: \"%s\" is not executable: %s (errno: %i).\n", actfpath, strerror(errno), errno);
if(access(options.actfpath, X_OK) == -1) {
printf_e("Error: \"%s\" is not executable: %s (errno: %i).\n", options.actfpath, strerror(errno), errno);
ret = errno;
}
if(flags[BACKGROUND])
if(options.flags[BACKGROUND])
ret = becomedaemon();
if(ret == 0)
ret = fasync_run(dpath, actfpath, rules);
ret = sync_run(&options, rules);
int i=0;
while((i < MAXRULES) && (rules[i].action != RULE_END))
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -28,7 +28,7 @@ char *xmalloc(size_t size) {
if(ret == NULL) {
printf_e("xmalloc(%i): Cannot allocate memory (#%i: %s).\n", size, errno, strerror(errno));
exit(ENOMEM);
exit(errno);
}
memset(ret, size, 0); // Just in case
... ... @@ -43,10 +43,23 @@ char *xcalloc(size_t nmemb, size_t size) {
if(ret == NULL) {
printf_e("xcalloc(%i): Cannot allocate memory (#%i: %s).\n", size, errno, strerror(errno));
exit(ENOMEM);
exit(errno);
}
memset(ret, nmemb*size, 0); // Just in case
return ret;
}
char *xrealloc(char *oldptr, size_t size) {
size++; // Just in case
char *ret = (char *)realloc(oldptr, size);
if(ret == NULL) {
printf_e("xrealloc(%p, %i): Cannot reallocate memory (#%i: %s).\n", oldptr, size, errno, strerror(errno));
exit(errno);
}
return ret;
}
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -20,4 +20,5 @@
extern char *xmalloc(size_t size);
extern char *xcalloc(size_t nmemb, size_t size);
extern char *xrealloc(char *oldptr, size_t size);
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -20,14 +20,19 @@
#include "common.h"
#include "output.h"
static int *flags;
printf_funct _printf_ddd=NULL;
printf_funct _printf_dd=NULL;
printf_funct _printf_d=NULL;
printf_funct _printf_v=NULL;
write_funct _write_ddd=NULL;
write_funct _write_dd=NULL;
write_funct _write_d=NULL;
write_funct _write_v=NULL;
int out_init() {
int out_init(int *flags_init) {
flags = flags_init;
// static char buf[OUTPUT_BUFSIZE];
if(flags[DEBUG]>0) {
_printf_d = printf_e;
... ... @@ -37,6 +42,10 @@ int out_init() {
_printf_dd = printf_e;
_write_dd = write_e;
}
if(flags[DEBUG]>2) {
_printf_ddd = printf_e;
_write_ddd = write_e;
}
if(flags[VERBOSE]) {
_printf_v = printf_e;
_write_v = write_e;
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -17,20 +17,24 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
extern int out_init();
extern int out_init(int *flags);
extern int debug_print_flags();
typedef int (*printf_funct)(const char *fmt, ...);
typedef int (*write_funct)(const char *buf, size_t len);
extern printf_funct _printf_ddd;
#define printf_ddd if(_printf_ddd!=NULL)_printf_ddd
extern printf_funct _printf_dd;
#define printf_dd if(_printf_dd!=NULL)_printf_dd
extern printf_funct _printf_d;
#define printf_d if(_printf_d!=NULL)_printf_d
extern printf_funct _printf_v;
#define printf_v if(_printf_v!=NULL)_printf_v
extern write_funct _write_ddd;
#define write_ddd if(_write_ddd!=null)_write_ddd
extern write_funct _write_dd;
#define write_dd if(_write_dd!=NULL)_write_dd
#define write_dd if(_write_dd!=null)_write_dd
extern write_funct _write_d;
#define write_d if(_write_d!=NULL)_write_d
extern write_funct _write_v;
... ...
/*
fasync - file tree sync utility based on fanotify
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@mephi.ru> 0x8E30679C
... ... @@ -19,8 +19,9 @@
#include "common.h"
#include "output.h"
#include "fileutils.h"
int fasync_exec(const char *actfpath, ...) {
int sync_exec(const char *actfpath, ...) {
va_list list;
va_start(list, actfpath);
... ... @@ -65,69 +66,36 @@ int fasync_exec(const char *actfpath, ...) {
return 0;
}
int fasync_initialsync(const char *path, const char *actfpath) {
return fasync_exec(actfpath, "initialsync", path, NULL);
int sync_initialsync(const char *path, const char *actfpath) {
return sync_exec(actfpath, "initialsync", path, NULL);
}
/*
int fasync_walk_fanotifyset(const char *dirpath, rule_t *rules) {
struct dirent *dent;
DIR *dir;
printf_dd("Debug2: fasync_walk_fanotifyset(\"%s\", rules).\n", dirpath);
char path[FILENAME_MAX+1];
size_t pathlen = strlen(dirpath);
if(pathlen+2 >= FILENAME_MAX) {
printf_e("Error: Too long path \"%s\" (#0)\n", dirpath);
return EINVAL;
}
if(!(dir = opendir(dirpath))) {
printf_e("Error: Cannot opendir() on directory \"%s\": %s (errno: %i).\n", dirpath, strerror(errno), errno);
return errno;
}
memcpy(path, dir, pathlen+1);
path[pathlen++] = '/';
path[pathlen ] = 0; // Just in case
while((dent = readdir(dir))) {
if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) continue;
size_t objnamelen = strlen(dent->d_name);
int sync_notify_mark(int notify_d, struct options *options, const char *path) {
switch(options->notifyengine) {
case NE_FANOTIFY: {
int fanotify_d = notify_d;
if(pathlen + objnamelen + 1>= FILENAME_MAX) {
printf_e("Error: Too long path \"%s/%s\" (#1)\n", dirpath, dent->d_name);
return EINVAL;
if((fanotify_mark(fanotify_d, FAN_MARK_ADD | FAN_MARK_DONT_FOLLOW,
FANOTIFY_MARKMASK, AT_FDCWD, path)) == -1)
{
printf_e("Error: Cannot fanotify_mark() on \"%s\": %s (errno: %i).\n",
path, strerror(errno), errno);
return errno;
}
return 0;
}
memcpy(&path[pathlen], dent->d_name, objnamelen+1);
printf_dd("Debug2: obj path <%s>\n", path);
case NE_INOTIFY:
return -1;
}
return 0;
printf_e("Error: unknown notify-engine: %i\n", options->notifyengine);
errno = EINVAL;
return -1;
}
*/
/*
int fasync_fanotifyset(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf) {
printf_dd("Debug2: fasync_fanotifyset(\"%s\", sb, %i, ftwbuf).\n", fpath, typeflag);
return FTW_CONTINUE;
}
int fasync_walk_fanotifyset(const char *dirpath, rule_t *rules) {
if(nftw(dirpath, fasync_fanotifyset, MAXFOPEN, FTW_ACTIONRETVAL|FTW_PHYS)) {
printf_e("Error: Cannot nftw() on \"%s\": %s (errno: %i).\n", dirpath, strerror(errno), errno);
return errno;
}
return 0;
}
*/
int fasync_walk_fanotifyset(int fanotify_d, const char *dirpath, rule_t *rules) {
int sync_walk_notifymark(int notify_d, struct options *options, const char *dirpath, rule_t *rules) {
const char *rootpaths[] = {dirpath, NULL};
FTS *tree;
printf_dd("Debug2: fasync_walk_fanotifyset(\"%s\", rules).\n", dirpath);
printf_dd("Debug2: sync_walk_notifymark(%i, options, \"%s\", rules).\n", notify_d, dirpath);
tree = fts_open((char *const *)&rootpaths, FTS_NOCHDIR|FTS_PHYSICAL, NULL);
... ... @@ -141,12 +109,12 @@ int fasync_walk_fanotifyset(int fanotify_d, const char *dirpath, rule_t *rules)
switch(node->fts_info) {
case FTS_DP: // Duplicates:
continue;
case FTS_D: // To sync:
case FTS_F:
case FTS_DEFAULT:
case FTS_SL:
case FTS_SLNONE:
case FTS_F:
continue;
case FTS_D: // To sync:
case FTS_DOT:
break;
case FTS_ERR: // Error cases:
... ... @@ -189,14 +157,8 @@ int fasync_walk_fanotifyset(int fanotify_d, const char *dirpath, rule_t *rules)
continue;
}
printf_dd("Debug2: fa-marking \"%s\" (depth %u)\n", node->fts_path, node->fts_level);
if((fanotify_mark(fanotify_d, FAN_MARK_ADD | FAN_MARK_DONT_FOLLOW,
FANOTIFY_MARKMASK, AT_FDCWD, node->fts_accpath)) == -1)
{
printf_e("Error: Cannot fanotify_mark() on \"%s\": %s (errno: %i).\n",
node->fts_path, strerror(errno), errno);
return errno;
}
printf_dd("Debug2: marking \"%s\" (depth %u)\n", node->fts_path, node->fts_level);
sync_notify_mark(notify_d, options, node->fts_accpath);
}
if(errno) {
... ... @@ -208,24 +170,31 @@ int fasync_walk_fanotifyset(int fanotify_d, const char *dirpath, rule_t *rules)
printf_e("Error: Got error while fts_close(): %s (errno: %i).\n", strerror(errno), errno);
return errno;
}
return 0;
}
int fasync_run(const char *path, const char *actfpath, rule_t *rules) {
int ret;
int sync_notify_init(struct options *options) {
switch(options->notifyengine) {
case NE_FANOTIFY: {
int fanotify_d = fanotify_init(FANOTIFY_FLAGS, FANOTIFY_EVFLAGS);
if(fanotify_d == -1) {
printf_e("Error: cannot fanotify_init(%i, %i): %s (errno: %i).\n", FANOTIFY_FLAGS, FANOTIFY_EVFLAGS, strerror(errno), errno);
return -1;
}
int fanotify_d = fanotify_init(FANOTIFY_FLAGS, FANOTIFY_EVFLAGS);
if(fanotify_d == -1) {
printf_e("Error: cannot fanotify_init(%i, %i): %s (errno: %i).\n", FANOTIFY_FLAGS, FANOTIFY_EVFLAGS, strerror(errno), errno);
return errno;
return fanotify_d;
}
case NE_INOTIFY: {
return -1;
}
}
printf_e("Error: unknown notify-engine: %i\n", options->notifyengine);
errno = EINVAL;
return -1;
}
ret = fasync_walk_fanotifyset(fanotify_d, path, rules);
if(ret) return ret;
ret = fasync_initialsync(path, actfpath);
if(ret) return ret;
int sync_fanotify_loop(int fanotify_d, struct options *options) {
struct fanotify_event_metadata buf[BUFSIZ/sizeof(struct fanotify_event_metadata) + 1];
int running=1;
while(running) {
... ... @@ -237,28 +206,53 @@ int fasync_run(const char *path, const char *actfpath, rule_t *rules) {
return errno;
}
while(FAN_EVENT_OK(metadata, len)) {
printf_dd("Debug2: metadata->pid: %i\n", metadata->pid);
printf_dd("Debug2: metadata->pid: %i; metadata->fd: %i\n", metadata->pid, metadata->fd);
if (metadata->fd != FAN_NOFD) {
if (metadata->fd >= 0) {
char lpath[PATH_MAX];
if (metadata->mask & FAN_CLOSE_WRITE) {
printf("FAN_CLOSE_WRITE: ");
}
sprintf(lpath, "/proc/self/fd/%d", metadata->fd);
size_t path_len = readlink(lpath, lpath, sizeof(path) - 1);
if (path_len > 0) {
lpath[path_len] = 0x00;
printf("File %s", lpath);
}
close(metadata->fd);
char *fpath = fd2fpath_malloc(metadata->fd);
printf_dd("Debug2: Event %i on \"%s\".\n", metadata->mask, fpath);
free(fpath);
}
printf("\n");
}
close(metadata->fd);
metadata = FAN_EVENT_NEXT(metadata, len);
}
}
close(fanotify_d);
return 0;
}
int sync_inotify_loop(int inotify_d, struct options *options) {
return 0;
}
int sync_notify_loop(int notify_d, struct options *options) {
switch(options->notifyengine) {
case NE_FANOTIFY:
return sync_fanotify_loop(notify_d, options);
case NE_INOTIFY:
return sync_inotify_loop (notify_d, options);
}
printf_e("Error: unknown notify-engine: %i\n", options->notifyengine);
errno = EINVAL;
return -1;
}
int sync_run(struct options *options, rule_t *rules) {
int ret;
int notify_d = sync_notify_init(options);
if(notify_d == -1) return errno;
ret = sync_walk_notifymark(notify_d, options, options->watchdir, rules);
if(ret) return ret;
ret = sync_initialsync(options->watchdir, options->actfpath);
if(ret) return ret;
ret = sync_notify_loop(notify_d, options);
if(ret) return ret;
close(notify_d);
return 0;
}
... ...
/*
clsync - file tree sync utility based on fanotify and inotify
Copyright (C) 2013 Dmitry Yu Okunev <xai@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 sync_run(struct options *options, rule_t *rules);
... ...