2008-12-05 23:37:38 +00:00
|
|
|
/**
|
2008-10-04 08:22:28 +00:00
|
|
|
* lsyncd.c Live (Mirror) Syncing Demon
|
|
|
|
*
|
|
|
|
* License: GPLv2 (see COPYING) or any later version
|
|
|
|
*
|
|
|
|
* Authors: Axel Kittenberger <axel.kittenberger@univie.ac.at>
|
|
|
|
* Eugene Sanivsky <eugenesan@gmail.com>
|
|
|
|
*/
|
2008-08-06 13:04:01 +00:00
|
|
|
#include "config.h"
|
2008-10-06 20:35:52 +00:00
|
|
|
#define _GNU_SOURCE
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/wait.h>
|
2010-05-22 15:25:31 +00:00
|
|
|
#include <sys/times.h>
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
#ifdef HAVE_SYS_INOTIFY_H
|
|
|
|
# include <sys/inotify.h>
|
|
|
|
#else
|
|
|
|
# include "inotify-nosys.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2008-11-03 13:13:29 +00:00
|
|
|
#include <limits.h>
|
2008-08-06 13:04:01 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <getopt.h>
|
2008-10-04 08:49:54 +00:00
|
|
|
#include <assert.h>
|
2009-02-28 15:58:06 +00:00
|
|
|
#include <syslog.h>
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
|
|
|
#include <libxml/parser.h>
|
|
|
|
#include <libxml/tree.h>
|
|
|
|
#endif
|
|
|
|
|
2010-08-02 17:11:56 +00:00
|
|
|
/**
|
2010-08-03 12:15:37 +00:00
|
|
|
* Number of inotifies to read at once from the kernel.
|
2010-08-02 17:11:56 +00:00
|
|
|
*/
|
2010-08-03 12:15:37 +00:00
|
|
|
#define INOTIFY_BUF_LEN (64 * (sizeof(struct inotify_event) + 16))
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2010-08-02 17:11:56 +00:00
|
|
|
/**
|
|
|
|
* Initial size of vectors
|
|
|
|
*/
|
|
|
|
#define VECT_INIT_SIZE 2
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
/**
|
|
|
|
* Defaults values
|
|
|
|
*/
|
|
|
|
#define DEFAULT_BINARY "/usr/bin/rsync"
|
|
|
|
#define DEFAULT_CONF_FILENAME "/etc/lsyncd.conf.xml"
|
|
|
|
|
2010-05-24 12:32:34 +00:00
|
|
|
/**
|
|
|
|
* Macros to compare times() values
|
|
|
|
* (borrowed from linux/jiffies.h)
|
|
|
|
*
|
|
|
|
* time_after(a,b) returns true if the time a is after time b.
|
|
|
|
*/
|
|
|
|
#define time_after(a,b) ((long)(b) - (long)(a) < 0)
|
|
|
|
#define time_before(a,b) time_after(b,a)
|
|
|
|
#define time_after_eq(a,b) ((long)(a) - (long)(b) >= 0)
|
|
|
|
#define time_before_eq(a,b) time_after_eq(b,a)
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
/**
|
|
|
|
* Importance of log messages
|
|
|
|
*/
|
2009-02-28 15:58:06 +00:00
|
|
|
enum log_code {
|
|
|
|
DEBUG = 1,
|
|
|
|
NORMAL = 2,
|
|
|
|
ERROR = 3,
|
|
|
|
};
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
/**
|
2010-08-03 12:15:37 +00:00
|
|
|
* Possible exit codes for this application
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2009-02-28 15:58:06 +00:00
|
|
|
enum lsyncd_exit_code {
|
2008-11-20 21:38:58 +00:00
|
|
|
LSYNCD_SUCCESS = 0,
|
2008-12-05 23:54:13 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/* out-of memory */
|
|
|
|
LSYNCD_OUTOFMEMORY = 1,
|
2008-12-05 23:54:13 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/* file was not found, or failed to write */
|
|
|
|
LSYNCD_FILENOTFOUND = 2,
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/* execution somehow failed */
|
|
|
|
LSYNCD_EXECFAIL = 3,
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/* command-line arguments given to lsyncd are bad */
|
|
|
|
LSYNCD_BADPARAMETERS = 4,
|
2008-12-05 23:54:13 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/* Too many excludes files were specified */
|
|
|
|
LSYNCD_TOOMANYDIRECTORYEXCLUDES = 5,
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/* something wrong with the config file */
|
|
|
|
LSYNCD_BADCONFIGFILE = 6,
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-12-13 22:14:58 +00:00
|
|
|
/* cannot open inotify instance */
|
|
|
|
LSYNCD_NOINOTIFY = 7,
|
|
|
|
|
|
|
|
/* something internal went really wrong */
|
2008-11-20 21:38:58 +00:00
|
|
|
LSYNCD_INTERNALFAIL = 255,
|
|
|
|
};
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
/**
|
2008-11-20 21:38:58 +00:00
|
|
|
* An option paramater for the action call can either be:
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2008-11-20 21:38:58 +00:00
|
|
|
enum call_option_kind {
|
|
|
|
CO_EOL, // end of list,
|
|
|
|
CO_TEXT, // specified by text,
|
|
|
|
CO_EXCLUDE, // be the exclude file
|
|
|
|
CO_SOURCE, // be the source of the operation
|
|
|
|
CO_DEST, // be the destination of the operation
|
|
|
|
};
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/*--------------------------------------------------------------------------*
|
|
|
|
* Structure definitions
|
|
|
|
*--------------------------------------------------------------------------*/
|
2008-10-08 04:08:52 +00:00
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2008-11-20 21:38:58 +00:00
|
|
|
* An option parameter for the call.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2008-11-20 21:38:58 +00:00
|
|
|
struct call_option {
|
|
|
|
/**
|
|
|
|
* The kind of this option.
|
|
|
|
*/
|
|
|
|
enum call_option_kind kind;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The text if its text.
|
|
|
|
*/
|
|
|
|
char *text;
|
|
|
|
};
|
2008-11-03 13:09:40 +00:00
|
|
|
|
2008-11-05 15:37:21 +00:00
|
|
|
/**
|
2008-11-20 21:38:58 +00:00
|
|
|
* A configurated directory to sync (including subdirectories)
|
|
|
|
*
|
|
|
|
* In case of beeing called with simple argument without a config file
|
|
|
|
* there will be exactly one sync_directory.
|
2008-11-05 15:37:21 +00:00
|
|
|
*/
|
2008-11-20 21:38:58 +00:00
|
|
|
struct dir_conf {
|
|
|
|
/**
|
|
|
|
* Source dir to watch (no default value)
|
|
|
|
*/
|
|
|
|
char * source;
|
|
|
|
|
|
|
|
/**
|
2008-12-05 22:27:03 +00:00
|
|
|
* NULL terminated list of targets to rsync to (no default value).
|
2008-11-20 21:38:58 +00:00
|
|
|
*/
|
2008-12-05 22:27:03 +00:00
|
|
|
char ** targets;
|
2008-11-20 21:38:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* binary to call (defaults to global default setting)
|
|
|
|
*/
|
|
|
|
char * binary;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* the options to call the binary (defaults to global default setting)
|
|
|
|
*/
|
|
|
|
struct call_option * callopts;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* the exclude-file to pass to rsync (defaults to global default setting)
|
|
|
|
* TODO, Currently ignored!
|
|
|
|
*/
|
|
|
|
char * exclude_file;
|
|
|
|
};
|
2008-11-05 15:37:21 +00:00
|
|
|
|
|
|
|
/**
|
2010-08-03 12:15:37 +00:00
|
|
|
* Structure to store the directory watches.
|
2008-11-05 15:37:21 +00:00
|
|
|
*/
|
2008-08-06 13:04:01 +00:00
|
|
|
struct dir_watch {
|
2008-08-12 11:51:46 +00:00
|
|
|
/**
|
2008-10-04 08:22:28 +00:00
|
|
|
* The watch descriptor returned by kernel.
|
|
|
|
*/
|
2008-08-12 11:51:46 +00:00
|
|
|
int wd;
|
|
|
|
|
|
|
|
/**
|
2008-10-04 08:22:28 +00:00
|
|
|
* The name of the directory.
|
|
|
|
* In case of the root dir to be watched, it is a full path
|
2010-08-03 12:15:37 +00:00
|
|
|
* and parent == -1. Otherwise its just the name of the
|
2008-10-04 08:22:28 +00:00
|
|
|
* directory and parent points to the parent directory thats
|
|
|
|
* also watched.
|
|
|
|
*/
|
2008-08-12 11:51:46 +00:00
|
|
|
char * dirname;
|
|
|
|
|
|
|
|
/**
|
2008-10-04 08:22:28 +00:00
|
|
|
* Points to the index of the parent.
|
|
|
|
* -1 if no parent
|
|
|
|
*/
|
2008-08-12 11:51:46 +00:00
|
|
|
int parent;
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
/**
|
|
|
|
* On a delay to be handled.
|
|
|
|
*/
|
|
|
|
bool tackled;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Point in time when rsync should be called.
|
|
|
|
*/
|
|
|
|
clock_t alarm;
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/**
|
|
|
|
* The applicable configuration for this directory
|
|
|
|
*/
|
2010-08-02 13:07:27 +00:00
|
|
|
struct dir_conf *dir_conf;
|
2008-11-20 21:38:58 +00:00
|
|
|
};
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/**
|
2010-08-03 12:15:37 +00:00
|
|
|
* Global options relevant for logging.
|
|
|
|
* Part of struct global_options.
|
2008-11-20 21:38:58 +00:00
|
|
|
*/
|
2010-08-02 13:07:27 +00:00
|
|
|
struct log {
|
|
|
|
/**
|
|
|
|
* Global Option: The loglevel is how eloquent lsyncd will be.
|
|
|
|
*/
|
|
|
|
int loglevel;
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: if true, do not detach and log to stdout/stderr.
|
|
|
|
*/
|
|
|
|
int flag_nodaemon;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If not NULL, this file will be accessed directly to write log messages to.
|
|
|
|
* If NULL, syslog will be used.
|
|
|
|
*
|
|
|
|
* Not as difference that the output of child processes (rsync) will be redirected
|
|
|
|
* to the logfile if specified, if syslogging the child-output will run into /dev/null.
|
|
|
|
*
|
|
|
|
* If flag_nodaemon is present stdout/stderr will be used.
|
|
|
|
*/
|
|
|
|
char * logfile;
|
|
|
|
};
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-08-02 17:11:56 +00:00
|
|
|
/**
|
|
|
|
* Global variables
|
|
|
|
*/
|
2010-08-02 13:07:27 +00:00
|
|
|
struct global_options {
|
|
|
|
/**
|
|
|
|
* Options relevant for logging.
|
|
|
|
*/
|
|
|
|
struct log log;
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: if true no action will actually be called.
|
|
|
|
*/
|
|
|
|
int flag_dryrun;
|
2008-12-13 17:30:52 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: if true, ignore rsync errors on startup.
|
|
|
|
* (during normal operations they have to be ignored eitherway,
|
|
|
|
* since rsync may also fail due e.g. the directory already
|
|
|
|
* beeing deleted when lsyncd wants to sync it.)
|
|
|
|
*/
|
|
|
|
int flag_stubborn;
|
2010-07-27 13:00:32 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: if true, lsyncd will not perform the startup sync.
|
|
|
|
*/
|
|
|
|
int flag_nostartup;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Global Option: pidfile, which holds the PID of the running daemon process.
|
|
|
|
*/
|
|
|
|
char *pidfile;
|
2008-11-20 21:38:58 +00:00
|
|
|
|
|
|
|
#ifdef XML_CONFIG
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: the filename to read config from.
|
|
|
|
*/
|
|
|
|
char *conf_filename;
|
2008-11-20 21:38:58 +00:00
|
|
|
#endif
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: this binary is used if no other specified in dir_conf.
|
|
|
|
*/
|
|
|
|
char *default_binary;
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* Global Option: default exclude file
|
|
|
|
*/
|
|
|
|
char *default_exclude_file;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Global Option: default options to call the binary with.
|
|
|
|
*
|
|
|
|
* TODO copy on init.
|
|
|
|
*/
|
|
|
|
struct call_option *default_callopts;
|
|
|
|
|
2010-08-03 12:21:21 +00:00
|
|
|
/**
|
|
|
|
* Seconds of delay between event and action
|
|
|
|
*/
|
|
|
|
clock_t delay;
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* The configuratiton for dirs to synchronize
|
|
|
|
*/
|
|
|
|
struct dir_conf *dir_confs;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The number of configurated dirs to sync.
|
|
|
|
*/
|
|
|
|
int dir_conf_n;
|
|
|
|
};
|
2008-11-20 21:38:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Standard default options to call the binary with.
|
|
|
|
*/
|
|
|
|
struct call_option standard_callopts[] = {
|
|
|
|
{ CO_TEXT, "-lt%r" },
|
|
|
|
{ CO_TEXT, "--delete" },
|
|
|
|
{ CO_EXCLUDE, NULL },
|
|
|
|
{ CO_SOURCE, NULL },
|
|
|
|
{ CO_DEST, NULL },
|
|
|
|
{ CO_EOL, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
2010-08-03 12:15:37 +00:00
|
|
|
* General purpose growable vector of integers.
|
2008-11-20 21:38:58 +00:00
|
|
|
*/
|
2010-08-02 17:11:56 +00:00
|
|
|
struct ivector {
|
|
|
|
/**
|
|
|
|
* data
|
|
|
|
*/
|
|
|
|
int *data;
|
2008-11-24 18:40:44 +00:00
|
|
|
|
2010-08-02 17:11:56 +00:00
|
|
|
/**
|
|
|
|
* allocated mem
|
|
|
|
*/
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* length used
|
|
|
|
*/
|
|
|
|
size_t len;
|
|
|
|
};
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
/**
|
|
|
|
* General purpose growable vector of pointers.
|
|
|
|
*/
|
|
|
|
//struct pvector {
|
|
|
|
// /**
|
|
|
|
// * data
|
|
|
|
// */
|
|
|
|
// void *data;
|
|
|
|
//
|
|
|
|
// /**
|
|
|
|
// * allocated mem
|
|
|
|
// */
|
|
|
|
// size_t size;
|
|
|
|
//
|
|
|
|
// /**
|
|
|
|
// * length used
|
|
|
|
// */
|
|
|
|
// size_t len;
|
|
|
|
//};
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
/**
|
|
|
|
* List of directories on a delay.
|
|
|
|
*/
|
2010-08-02 17:30:06 +00:00
|
|
|
struct ivector tackles_obj = {0, };
|
|
|
|
struct ivector *tackles = &tackles_obj;
|
2010-05-22 15:25:31 +00:00
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Structure to store strings for the diversve inotfy masked events.
|
|
|
|
* Used for comfortable log messages only.
|
|
|
|
*/
|
|
|
|
struct inotify_mask_text {
|
|
|
|
int mask;
|
|
|
|
char const * text;
|
|
|
|
};
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
|
|
|
* A constant that assigns every inotify mask a printable string.
|
|
|
|
* Used for debugging.
|
|
|
|
*/
|
|
|
|
struct inotify_mask_text mask_texts[] = {
|
2008-08-12 11:51:46 +00:00
|
|
|
{ IN_ACCESS, "ACCESS" },
|
|
|
|
{ IN_ATTRIB, "ATTRIB" },
|
|
|
|
{ IN_CLOSE_WRITE, "CLOSE_WRITE" },
|
|
|
|
{ IN_CLOSE_NOWRITE, "CLOSE_NOWRITE" },
|
|
|
|
{ IN_CREATE, "CREATE" },
|
|
|
|
{ IN_DELETE, "DELETE" },
|
|
|
|
{ IN_DELETE_SELF, "DELETE_SELF" },
|
2008-11-04 21:41:07 +00:00
|
|
|
{ IN_IGNORED, "IGNORED" },
|
2008-08-12 11:51:46 +00:00
|
|
|
{ IN_MODIFY, "MODIFY" },
|
|
|
|
{ IN_MOVE_SELF, "MOVE_SELF" },
|
|
|
|
{ IN_MOVED_FROM, "MOVED_FROM" },
|
|
|
|
{ IN_MOVED_TO, "MOVED_TO" },
|
|
|
|
{ IN_OPEN, "OPEN" },
|
|
|
|
{ 0, "" },
|
|
|
|
};
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
/**
|
2010-08-03 12:15:37 +00:00
|
|
|
* Holds all directories being watched.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 12:15:37 +00:00
|
|
|
struct dir_watch_vector {
|
|
|
|
struct dir_watch *data;
|
|
|
|
size_t size;
|
|
|
|
size_t len;
|
|
|
|
};
|
|
|
|
struct dir_watch_vector dir_watches_obj = {0, };
|
|
|
|
struct dir_watch_vector *dir_watches = &dir_watches_obj;
|
2008-10-04 08:22:28 +00:00
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2008-08-12 11:51:46 +00:00
|
|
|
* Array of strings of directory names to include.
|
|
|
|
* This is limited to MAX_EXCLUDES.
|
2008-08-06 13:04:01 +00:00
|
|
|
* It's not worth to code a dynamic size handling...
|
|
|
|
*/
|
|
|
|
#define MAX_EXCLUDES 256
|
2008-10-04 08:22:28 +00:00
|
|
|
char * exclude_dirs[MAX_EXCLUDES] = {NULL, };
|
2008-08-06 13:04:01 +00:00
|
|
|
int exclude_dir_n = 0;
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
/**
|
|
|
|
* (Re)sets global options to default values.
|
|
|
|
*
|
|
|
|
* TODO memfree's
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
reset_options(struct global_options *opts) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->log.loglevel = NORMAL;
|
|
|
|
opts->log.flag_nodaemon = 0;
|
|
|
|
opts->log.logfile = NULL;
|
|
|
|
|
|
|
|
opts->flag_dryrun = 0;
|
|
|
|
opts->flag_stubborn = 0;
|
|
|
|
opts->flag_nostartup = 0;
|
|
|
|
opts->pidfile = NULL;
|
|
|
|
#ifdef XML_CONFIG
|
|
|
|
opts->conf_filename = DEFAULT_CONF_FILENAME;
|
|
|
|
#endif
|
|
|
|
opts->default_binary = DEFAULT_BINARY;
|
|
|
|
opts->default_exclude_file = NULL;
|
|
|
|
opts->default_callopts = standard_callopts;
|
2010-08-03 12:21:21 +00:00
|
|
|
opts->delay = 5;
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->dir_confs = NULL;
|
|
|
|
opts->dir_conf_n = 0;
|
|
|
|
};
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/*--------------------------------------------------------------------------*
|
|
|
|
* Small generic helper routines.
|
|
|
|
* (signal catching, memory fetching, message output)
|
|
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set to 0 in signal handler, when lsyncd should TERMinate nicely.
|
|
|
|
*/
|
2008-08-06 13:04:01 +00:00
|
|
|
volatile sig_atomic_t keep_going = 1;
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/**
|
|
|
|
* Called (out-of-order) when signals arrive
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
catch_alarm(int sig)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
keep_going = 0;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-02-28 15:58:06 +00:00
|
|
|
* Just like exit, but logs the exit.
|
|
|
|
* Does not return!
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
terminate(const struct log *log, int status)
|
2009-02-28 15:58:06 +00:00
|
|
|
{
|
2010-08-02 13:07:27 +00:00
|
|
|
if (log && !log->flag_nodaemon) {
|
|
|
|
if (log->logfile) {
|
2009-02-28 15:58:06 +00:00
|
|
|
FILE * flog;
|
2010-08-02 13:07:27 +00:00
|
|
|
flog = fopen(log->logfile, "a");
|
2009-02-28 15:58:06 +00:00
|
|
|
if (flog) {
|
|
|
|
fprintf(flog, "exit!");
|
|
|
|
fclose(flog);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
syslog(LOG_ERR, "exit!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
exit(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Prints a message to either the log stream, preceding a timestamp or
|
|
|
|
* forwards a message to syslogd.
|
|
|
|
*
|
2008-08-06 13:04:01 +00:00
|
|
|
* Otherwise it behaves like printf();
|
2009-02-28 15:58:06 +00:00
|
|
|
*
|
|
|
|
* It will also always produce error messages on stderr.
|
|
|
|
* So when startup fails, the message will be logged
|
|
|
|
* _and_ displayed on screen. If lsyncd daemonized already,
|
|
|
|
* stderr will be run into the void of /dev/null.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
printlogf(const struct log *log, int level, const char *fmt, ...)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
char * ct;
|
|
|
|
time_t mtime;
|
2009-02-28 15:58:06 +00:00
|
|
|
FILE * flog1 = NULL, * flog2 = NULL;
|
|
|
|
int sysp = 0;
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (log && level < log->loglevel) {
|
2008-08-12 11:51:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (log && !log->flag_nodaemon && log->logfile) {
|
|
|
|
flog1 = fopen(log->logfile, "a");
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
if (flog1 == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
fprintf(stderr, "cannot open logfile [%s]!\n", log->logfile);
|
|
|
|
terminate(log, LSYNCD_FILENOTFOUND);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
time(&mtime);
|
|
|
|
ct = ctime(&mtime);
|
2009-02-28 15:58:06 +00:00
|
|
|
ct[strlen(ct) - 1] = 0; // cut trailing linefeed
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
switch (level) {
|
2009-02-28 15:58:06 +00:00
|
|
|
case DEBUG :
|
|
|
|
sysp = LOG_DEBUG;
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!log || log->flag_nodaemon) {
|
2009-02-28 15:58:06 +00:00
|
|
|
flog2 = stdout;
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
case NORMAL :
|
|
|
|
sysp = LOG_NOTICE;
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!log || log->flag_nodaemon) {
|
2009-02-28 15:58:06 +00:00
|
|
|
flog2 = stdout;
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
break;
|
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
case ERROR :
|
|
|
|
sysp = LOG_ERR;
|
|
|
|
// write on stderr even when daemon.
|
|
|
|
flog2 = stderr;
|
2008-08-12 11:51:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
// write time on fileoutput
|
|
|
|
if (flog1) {
|
|
|
|
fprintf(flog1, "%s: ", ct);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (level == ERROR) {
|
|
|
|
if (flog1) {
|
|
|
|
fprintf(flog1, "ERROR: ");
|
|
|
|
}
|
|
|
|
if (flog2) {
|
|
|
|
fprintf(flog2, "ERROR: ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (flog1) {
|
2010-07-09 06:40:53 +00:00
|
|
|
va_start(ap, fmt);
|
2009-02-28 15:58:06 +00:00
|
|
|
vfprintf(flog1, fmt, ap);
|
2010-07-09 06:40:53 +00:00
|
|
|
va_end(ap);
|
2009-02-28 15:58:06 +00:00
|
|
|
} else {
|
2010-07-09 06:40:53 +00:00
|
|
|
va_start(ap, fmt);
|
2009-02-28 15:58:06 +00:00
|
|
|
vsyslog(sysp, fmt, ap);
|
2010-07-09 06:40:53 +00:00
|
|
|
va_end(ap);
|
2009-02-28 15:58:06 +00:00
|
|
|
}
|
|
|
|
if (flog2) {
|
2010-07-09 06:40:53 +00:00
|
|
|
va_start(ap, fmt);
|
2009-02-28 15:58:06 +00:00
|
|
|
vfprintf(flog2, fmt, ap);
|
2010-07-09 06:40:53 +00:00
|
|
|
va_end(ap);
|
2009-02-28 15:58:06 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
if (flog1) {
|
|
|
|
fprintf(flog1, "\n");
|
|
|
|
}
|
|
|
|
if (flog2) {
|
|
|
|
fprintf(flog2, "\n");
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
if (flog1) {
|
|
|
|
fclose(flog1);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2008-08-12 11:51:46 +00:00
|
|
|
* "secured" malloc, meaning the deamon shall kill itself
|
|
|
|
* in case of out of memory.
|
2008-08-06 13:04:01 +00:00
|
|
|
*
|
2008-08-12 11:51:46 +00:00
|
|
|
* On linux systems, which is actually the only system this
|
2008-08-06 13:04:01 +00:00
|
|
|
* deamon will run at, due to the use of inotify, this is
|
2008-08-12 11:51:46 +00:00
|
|
|
* an "academic" cleaness only, linux will never return out
|
|
|
|
* memory, but kill a process to ensure memory will be
|
2008-08-06 13:04:01 +00:00
|
|
|
* available.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void *
|
|
|
|
s_malloc(const struct log *log, size_t size)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
void *r = malloc(size);
|
|
|
|
|
|
|
|
if (r == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Out of memory!");
|
|
|
|
terminate(log, LSYNCD_OUTOFMEMORY);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "secured" calloc.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void *
|
|
|
|
s_calloc(const struct log *log, size_t nmemb, size_t size)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
void *r = calloc(nmemb, size);
|
|
|
|
|
|
|
|
if (r == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Out of memory!");
|
|
|
|
terminate(log, LSYNCD_OUTOFMEMORY);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "secured" realloc.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void *
|
|
|
|
s_realloc(const struct log *log, void *ptr, size_t size)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
void *r = realloc(ptr, size);
|
|
|
|
|
|
|
|
if (r == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Out of memory!");
|
|
|
|
terminate(log, LSYNCD_OUTOFMEMORY);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2008-10-06 20:35:52 +00:00
|
|
|
/**
|
|
|
|
* "secured" strdup.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
char *
|
|
|
|
s_strdup(const struct log *log, const char *src)
|
2008-10-06 20:35:52 +00:00
|
|
|
{
|
|
|
|
char *s = strdup(src);
|
|
|
|
|
|
|
|
if (s == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Out of memory!");
|
|
|
|
terminate(log, LSYNCD_OUTOFMEMORY);
|
2008-10-06 20:35:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-08-03 13:14:37 +00:00
|
|
|
* Returns the canonicalized path of a directory with a final '/'.
|
2008-10-06 20:35:52 +00:00
|
|
|
* Makes sure it is a directory.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
char *
|
|
|
|
realdir(const struct log *log, const char *dir)
|
2008-10-06 20:35:52 +00:00
|
|
|
{
|
2010-08-02 13:07:27 +00:00
|
|
|
char* cs = s_malloc(log, PATH_MAX+1);
|
2008-10-07 06:30:40 +00:00
|
|
|
cs = realpath(dir, cs);
|
2008-10-06 20:35:52 +00:00
|
|
|
|
|
|
|
if (cs == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-11-03 13:13:29 +00:00
|
|
|
if (strlen(cs) + 1 >= PATH_MAX) {
|
2008-10-06 20:35:52 +00:00
|
|
|
// at systems maxpath already, we cannot add a '/' anyway.
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct stat st;
|
|
|
|
stat(cs, &st);
|
|
|
|
if (!S_ISDIR(st.st_mode)) {
|
|
|
|
free(cs);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-10-06 20:40:28 +00:00
|
|
|
strcat(cs, "/");
|
2008-10-06 20:35:52 +00:00
|
|
|
return cs;
|
|
|
|
}
|
|
|
|
|
2010-08-02 17:11:56 +00:00
|
|
|
/**
|
|
|
|
* Appends one value on an integer vector.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
2010-08-02 17:11:56 +00:00
|
|
|
ivector_push(const struct log *log, struct ivector *ivect, int val){
|
|
|
|
if (ivect->size > ivect->len + 1) {
|
|
|
|
ivect->data[ivect->len++] = val;
|
|
|
|
} else if (!ivect->data) {
|
|
|
|
ivect->data = s_calloc(log, VECT_INIT_SIZE, sizeof(int));
|
|
|
|
ivect->size = VECT_INIT_SIZE;
|
|
|
|
ivect->len = 1;
|
|
|
|
ivect->data[0] = val;
|
|
|
|
} else {
|
|
|
|
ivect->size *= 2;
|
|
|
|
ivect->data = s_realloc(log, ivect->data, ivect->size * sizeof(int));
|
|
|
|
ivect->data[ivect->len++] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/*--------------------------------------------------------------------------*
|
2010-08-03 13:14:37 +00:00
|
|
|
* Per directory configuration handling.
|
2008-11-20 21:38:58 +00:00
|
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* (re)allocates space for a new dir_config and sets all values to 0/Null.
|
|
|
|
*
|
|
|
|
* (Yes we know, its a bit unoptimal, since when 6 dir_confs are given
|
|
|
|
* in the config file, lsyncd will reallocate dir_confs 6 times. Well we
|
|
|
|
* can live with that.)
|
|
|
|
*
|
|
|
|
* @return the pointer to the newly allocated dir_conf
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
struct dir_conf *
|
|
|
|
new_dir_conf(struct global_options *opts) {
|
2010-08-02 13:07:27 +00:00
|
|
|
const struct log* log = &opts->log;
|
|
|
|
|
|
|
|
if (opts->dir_conf_n > 0) {
|
|
|
|
// enhance allocated space by 1.
|
|
|
|
opts->dir_conf_n++;
|
|
|
|
opts->dir_confs = s_realloc(log, opts->dir_confs, opts->dir_conf_n * sizeof(struct dir_conf));
|
|
|
|
memset(opts->dir_confs + opts->dir_conf_n - 1, 0, sizeof(struct dir_conf));
|
|
|
|
// creates targets NULL terminator (no targets yet)
|
|
|
|
opts->dir_confs[opts->dir_conf_n - 1].targets = s_calloc(log, 1, sizeof(char *));
|
|
|
|
return opts->dir_confs + opts->dir_conf_n - 1;
|
|
|
|
} else {
|
|
|
|
// create the memory.
|
|
|
|
opts->dir_conf_n = 1;
|
|
|
|
opts->dir_confs = s_calloc(log, opts->dir_conf_n, sizeof(struct dir_conf));
|
2008-12-05 22:27:03 +00:00
|
|
|
// creates targets NULL terminator (no targets yet)
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->dir_confs[0].targets = s_calloc(log, 1, sizeof(char *));
|
|
|
|
return opts->dir_confs;
|
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
|
2008-12-05 22:27:03 +00:00
|
|
|
/**
|
2010-08-02 13:07:27 +00:00
|
|
|
* Adds a target to a dir_conf.target.
|
|
|
|
* *target string will duped.
|
2008-12-05 22:27:03 +00:00
|
|
|
*
|
|
|
|
* @param dir_conf dir_conf to add the target to.
|
|
|
|
* @param target target to add.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
dir_conf_add_target(const struct log *log, struct dir_conf *dir_conf, char *target)
|
2008-12-05 22:27:03 +00:00
|
|
|
{
|
|
|
|
char **t;
|
|
|
|
int target_n = 0;
|
|
|
|
|
2010-07-27 13:23:17 +00:00
|
|
|
// count current targets
|
2008-12-05 22:27:03 +00:00
|
|
|
for (t = dir_conf->targets; *t; ++t) {
|
|
|
|
target_n++;
|
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
dir_conf->targets = s_realloc(log, dir_conf->targets, (target_n + 2) * sizeof(char *));
|
|
|
|
dir_conf->targets[target_n] = s_strdup(log, target);
|
2008-12-05 22:27:03 +00:00
|
|
|
dir_conf->targets[target_n + 1] = NULL;
|
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
/*--------------------------------------------------------------------------*
|
|
|
|
* Tackle list handling.
|
|
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a directory on the tackle len (on a delay)
|
|
|
|
*
|
|
|
|
* @param watch the index in dir_watches to the directory.
|
|
|
|
* @param alarm times() when the directory should be acted.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
2010-08-02 17:30:06 +00:00
|
|
|
append_tackle(const struct log *log, int watch, clock_t alarm) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "add tackle(%d)", watch);
|
2010-08-03 12:15:37 +00:00
|
|
|
if (dir_watches->data[watch].tackled) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "ignored since already tackled.", watch);
|
2010-05-22 15:25:31 +00:00
|
|
|
return false;
|
|
|
|
}
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->data[watch].tackled = true;
|
|
|
|
dir_watches->data[watch].alarm = alarm;
|
2010-05-22 15:25:31 +00:00
|
|
|
|
2010-08-02 17:30:06 +00:00
|
|
|
ivector_push(log, tackles, watch);
|
2010-05-22 15:25:31 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the first directory on the tackle list.
|
|
|
|
*/
|
2010-08-02 17:30:06 +00:00
|
|
|
void
|
|
|
|
remove_first_tackle() {
|
|
|
|
int tw = tackles->data[0];
|
|
|
|
memmove(tackles->data, tackles->data + 1, (--tackles->len) * sizeof(int));
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->data[tw].tackled = false;
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/*--------------------------------------------------------------------------*
|
|
|
|
* ToSync Stack handling.
|
|
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses an option text, replacing all '%' specifiers with
|
|
|
|
* elaborated stuff. duh, currently there is only one, so this
|
|
|
|
* fuction is a bit overkill but oh well :-)
|
|
|
|
*
|
|
|
|
* @param text string to parse.
|
|
|
|
* @param recursive info for replacements.
|
|
|
|
*
|
|
|
|
* @return a newly allocated string.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
char *
|
|
|
|
parse_option_text(const struct log *log, char *text, bool recursive)
|
2008-11-20 21:38:58 +00:00
|
|
|
{
|
2010-08-02 13:07:27 +00:00
|
|
|
char * str = s_strdup(log, text);
|
2008-11-20 21:38:58 +00:00
|
|
|
char * chr; // search result for %.
|
|
|
|
|
|
|
|
// replace all '%' specifiers with there special meanings
|
|
|
|
for(chr = strchr(str, '%'); chr; chr = strchr(str, '%')) {
|
|
|
|
char *p;
|
|
|
|
// chr points now to the '%' thing to be replaced
|
|
|
|
switch (chr[1]) {
|
|
|
|
case 'r' : // replace %r with 'r' when recursive or 'd' when not.
|
|
|
|
chr[0] = recursive ? 'r' : 'd';
|
|
|
|
for(p = chr + 1; *p != 0; p++) {
|
|
|
|
p[0] = p[1];
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0: // wtf, '%' was at the end of the string!
|
|
|
|
default : // unknown char
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR,
|
2008-11-20 21:38:58 +00:00
|
|
|
"don't know how to handle '\%' specifier in \"%s\"!", *text);
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(log, LSYNCD_BADPARAMETERS);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return str;
|
2008-11-05 15:37:21 +00:00
|
|
|
}
|
|
|
|
|
2010-07-12 07:03:01 +00:00
|
|
|
/**
|
2010-07-27 13:31:55 +00:00
|
|
|
* Creates one string with all arguments concated.
|
|
|
|
*
|
2010-07-12 07:03:01 +00:00
|
|
|
* @param argv the arguments
|
|
|
|
* @param argc number of arguments
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
char *
|
|
|
|
get_arg_str(const struct log *log, char **argv, int argc) {
|
2010-07-12 07:03:01 +00:00
|
|
|
int i;
|
|
|
|
int len = 0;
|
|
|
|
char * str;
|
|
|
|
|
|
|
|
// calc length
|
|
|
|
for (i = 0; i < argc; i++) {
|
|
|
|
len += strlen(argv[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// alloc
|
2010-08-02 13:07:27 +00:00
|
|
|
str = s_malloc(log, len + 2 * argc + 1);
|
2010-07-12 07:03:01 +00:00
|
|
|
|
|
|
|
str[0] = 0;
|
|
|
|
for(i = 0; i < argc; i++) {
|
|
|
|
if (i > 0) {
|
|
|
|
strcat(str, ", ");
|
|
|
|
}
|
|
|
|
strcat(str, argv[i]);
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2008-11-20 21:38:58 +00:00
|
|
|
* Calls the specified action (most likely rsync) to sync from src to dest.
|
|
|
|
* Returns after the forked process has finished.
|
2008-08-06 13:04:01 +00:00
|
|
|
*
|
2008-11-20 21:38:58 +00:00
|
|
|
* @param dir_conf The config the is applicatable for this dir.
|
2008-08-06 13:04:01 +00:00
|
|
|
* @param src Source string.
|
|
|
|
* @param dest Destination string,
|
2008-11-03 13:09:40 +00:00
|
|
|
* @param recursive If true -r will be handled on, -d (single directory) otherwise
|
2008-10-13 02:06:19 +00:00
|
|
|
* @return true if successful, false if not.
|
2010-08-02 13:07:27 +00:00
|
|
|
*
|
|
|
|
* TODO change dir_conf and src pointer simply to index offset.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
action(const struct global_options *opts,
|
|
|
|
struct dir_conf * dir_conf,
|
|
|
|
char const * src,
|
|
|
|
const char * dest,
|
|
|
|
bool recursive)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
pid_t pid;
|
2008-11-03 13:09:40 +00:00
|
|
|
int status;
|
|
|
|
const int MAX_ARGS = 100;
|
|
|
|
char * argv[MAX_ARGS];
|
2010-07-12 06:49:53 +00:00
|
|
|
int argc = 0;
|
2008-11-03 13:09:40 +00:00
|
|
|
int i;
|
2008-11-20 21:38:58 +00:00
|
|
|
struct call_option* optp;
|
2010-08-02 13:07:27 +00:00
|
|
|
const struct log* log = &opts->log;
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
optp = dir_conf->callopts ? dir_conf->callopts : opts->default_callopts;
|
|
|
|
|
|
|
|
// makes a copy of all call parameters
|
|
|
|
// step 1 binary itself
|
|
|
|
argv[argc++] = s_strdup(log, dir_conf->binary ? dir_conf->binary : opts->default_binary);
|
|
|
|
// now all other parameters
|
2010-07-12 06:49:53 +00:00
|
|
|
for(; optp->kind != CO_EOL; optp++) {
|
2008-11-20 21:38:58 +00:00
|
|
|
switch (optp->kind) {
|
|
|
|
case CO_TEXT :
|
2010-08-02 13:07:27 +00:00
|
|
|
argv[argc++] = parse_option_text(log, optp->text, recursive);
|
2008-11-20 21:38:58 +00:00
|
|
|
continue;
|
|
|
|
case CO_EXCLUDE :
|
|
|
|
// --exclude-from and the exclude file
|
|
|
|
// insert only when the exclude file is present otherwise skip it.
|
2010-08-02 13:07:27 +00:00
|
|
|
if (dir_conf->exclude_file == NULL && opts->default_exclude_file == NULL) {
|
2008-11-20 21:38:58 +00:00
|
|
|
continue;
|
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
argv[argc++] = s_strdup(log, "--exclude-from");
|
|
|
|
argv[argc++] = s_strdup(log, dir_conf->exclude_file ? dir_conf->exclude_file : opts->default_exclude_file);
|
2008-11-20 21:38:58 +00:00
|
|
|
continue;
|
|
|
|
case CO_SOURCE :
|
2010-08-02 13:07:27 +00:00
|
|
|
argv[argc++] = s_strdup(log, src);
|
2008-11-20 21:38:58 +00:00
|
|
|
continue;
|
|
|
|
case CO_DEST :
|
2010-08-02 13:07:27 +00:00
|
|
|
argv[argc++] = s_strdup(log, dest);
|
2008-11-20 21:38:58 +00:00
|
|
|
continue;
|
|
|
|
default:
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
if (argc >= MAX_ARGS) {
|
|
|
|
/* check for error condition */
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR,
|
2008-11-20 21:38:58 +00:00
|
|
|
"Internal error: too many (>%i) options passed", argc);
|
|
|
|
return false;
|
|
|
|
}
|
2008-11-03 13:09:40 +00:00
|
|
|
}
|
|
|
|
argv[argc++] = NULL;
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts->flag_dryrun) {
|
2010-07-12 07:03:01 +00:00
|
|
|
// just make a nice log message
|
2010-08-02 13:07:27 +00:00
|
|
|
char * binary = dir_conf->binary ? dir_conf->binary : opts->default_binary;
|
|
|
|
char * argall = get_arg_str(log, argv, argc);
|
|
|
|
printlogf(log, NORMAL, "dry run: would call %s(%s)", binary, argall);
|
2010-07-12 07:03:01 +00:00
|
|
|
free(argall);
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
|
|
if (argv[i]) {
|
|
|
|
free(argv[i]);
|
|
|
|
}
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pid = fork();
|
2008-08-06 13:04:01 +00:00
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
if (pid == 0) {
|
2010-08-02 13:07:27 +00:00
|
|
|
char * binary = dir_conf->binary ? dir_conf->binary : opts->default_binary;
|
|
|
|
if (!log->flag_nodaemon && log->logfile) {
|
|
|
|
if (!freopen(log->logfile, "a", stdout)) {
|
|
|
|
printlogf(log, ERROR, "cannot redirect stdout to [%s].", log->logfile);
|
2008-12-13 22:14:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!freopen(log->logfile, "a", stderr)) {
|
|
|
|
printlogf(log, ERROR, "cannot redirect stderr to [%s].", log->logfile);
|
2008-12-13 22:14:58 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-07-27 13:31:55 +00:00
|
|
|
execv(binary, argv);
|
|
|
|
// in a sane world execv does not return!
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Failed executing [%s]", binary);
|
|
|
|
terminate(log, LSYNCD_INTERNALFAIL);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-07-27 13:31:55 +00:00
|
|
|
// free the memory from the arguments.
|
2010-07-12 06:49:53 +00:00
|
|
|
for (i = 0; i < argc; ++i) {
|
2008-11-20 21:38:58 +00:00
|
|
|
if (argv[i]) {
|
2008-11-03 13:09:40 +00:00
|
|
|
free(argv[i]);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2008-11-03 13:09:40 +00:00
|
|
|
}
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
waitpid(pid, &status, 0);
|
2008-10-04 08:49:54 +00:00
|
|
|
assert(WIFEXITED(status));
|
2010-07-12 06:49:53 +00:00
|
|
|
if (WEXITSTATUS(status) == LSYNCD_INTERNALFAIL){
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR,
|
2008-11-20 21:38:58 +00:00
|
|
|
"Fork exit code of %i, execv failure",
|
|
|
|
WEXITSTATUS(status));
|
2008-11-05 11:37:26 +00:00
|
|
|
return false;
|
|
|
|
} else if (WEXITSTATUS(status)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL,
|
2008-11-20 21:38:58 +00:00
|
|
|
"Forked binary process returned non-zero return code: %i",
|
2008-12-05 23:54:13 +00:00
|
|
|
WEXITSTATUS(status));
|
2008-10-13 02:06:19 +00:00
|
|
|
return false;
|
2008-10-04 08:49:54 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "Rsync of [%s] -> [%s] finished", src, dest);
|
2008-08-12 11:51:46 +00:00
|
|
|
return true;
|
|
|
|
}
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
/**
|
2008-12-05 23:37:38 +00:00
|
|
|
* Adds a directory to watch.
|
2008-08-06 13:04:01 +00:00
|
|
|
*
|
2010-08-03 13:14:37 +00:00
|
|
|
* @param log logging information.
|
|
|
|
* @param inotify_fd inotify file descriptor.
|
|
|
|
* @param pathname the absolute path of the directory to watch.
|
|
|
|
* @param dirname the name of the directory only (yes this is a bit redudant, but oh well)
|
|
|
|
* @param parent if not -1 the index to the parent directory that is already watched
|
|
|
|
* @param dir_conf the applicateable configuration
|
2008-08-06 13:04:01 +00:00
|
|
|
*
|
|
|
|
* @return index to dir_watches of the new dir, -1 on error.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
int
|
|
|
|
add_watch(const struct log *log,
|
|
|
|
int inotify_fd,
|
|
|
|
char const * pathname,
|
|
|
|
char const * dirname,
|
|
|
|
int parent,
|
|
|
|
struct dir_conf * dir_conf)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
2010-08-03 12:15:37 +00:00
|
|
|
int wd; // kernels inotify descriptor
|
|
|
|
int newdw; // position to insert this watch into the watch vector
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-03 13:14:37 +00:00
|
|
|
wd = inotify_add_watch(inotify_fd, pathname,
|
2008-11-20 21:38:58 +00:00
|
|
|
IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE |
|
|
|
|
IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM |
|
|
|
|
IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR);
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
if (wd == -1) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Cannot add watch %s (%d:%s)",
|
2008-11-20 21:38:58 +00:00
|
|
|
pathname, errno, strerror(errno));
|
2008-08-12 11:51:46 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// look if an unused slot can be found.
|
2010-08-03 12:15:37 +00:00
|
|
|
for (newdw = 0; newdw < dir_watches->len; newdw++) {
|
|
|
|
if (dir_watches->data[newdw].wd < 0) {
|
2008-08-12 11:51:46 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
if (newdw == dir_watches->len) {
|
|
|
|
// TODO move this
|
|
|
|
if (dir_watches->len + 1 >= dir_watches->size) {
|
|
|
|
dir_watches->size *= 2;
|
|
|
|
dir_watches->data = s_realloc(log, dir_watches->data,
|
|
|
|
dir_watches->size * sizeof(struct dir_watch));
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->len++;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->data[newdw].wd = wd;
|
|
|
|
dir_watches->data[newdw].parent = parent;
|
|
|
|
dir_watches->data[newdw].dirname = s_strdup(log, dirname);
|
|
|
|
dir_watches->data[newdw].dir_conf = dir_conf;
|
|
|
|
dir_watches->data[newdw].alarm = 0; // not needed, just to be save
|
|
|
|
dir_watches->data[newdw].tackled = false;
|
2008-08-12 11:51:46 +00:00
|
|
|
return newdw;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2008-12-05 23:37:38 +00:00
|
|
|
/**
|
|
|
|
* Writes the path of a watched directory into pathname.
|
|
|
|
*
|
|
|
|
* @param pathname path to write to
|
|
|
|
* @param pathsize size of the pathname buffer
|
|
|
|
* @param watch index of watched dir to build path for
|
|
|
|
* @param prefix replace root dir with this (as target)
|
|
|
|
*
|
|
|
|
* @return -1 if pathname buffer was too small
|
|
|
|
* contents of pathname will be garbled then.
|
|
|
|
* strlen(pathname) if successful
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
int
|
|
|
|
builddir(char *pathname, int pathsize, int watch, char const * prefix)
|
2008-12-05 23:37:38 +00:00
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
if (watch == -1) {
|
|
|
|
// When is this called this way???
|
|
|
|
char const * p = prefix ? prefix : "";
|
|
|
|
len = strlen(p);
|
|
|
|
if (pathsize <= len) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy(pathname, p);
|
2010-08-03 12:15:37 +00:00
|
|
|
} else if (dir_watches->data[watch].parent == -1) {
|
2008-12-05 23:37:38 +00:00
|
|
|
// this is a watch root.
|
2010-08-03 12:15:37 +00:00
|
|
|
char const * p = prefix ? prefix : dir_watches->data[watch].dirname;
|
2008-12-05 23:37:38 +00:00
|
|
|
len = strlen(p);
|
|
|
|
if (pathsize <= len) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
strcpy(pathname, p);
|
|
|
|
} else {
|
|
|
|
// this is some sub dir
|
2010-08-03 12:15:37 +00:00
|
|
|
len = builddir(pathname, pathsize, dir_watches->data[watch].parent, prefix); /* recurse */
|
|
|
|
len += strlen(dir_watches->data[watch].dirname);
|
2008-12-05 23:37:38 +00:00
|
|
|
if (pathsize <= len) {
|
|
|
|
return -1;
|
|
|
|
}
|
2010-08-03 12:15:37 +00:00
|
|
|
strcat(pathname, dir_watches->data[watch].dirname);
|
2008-12-05 23:37:38 +00:00
|
|
|
}
|
2010-08-03 12:15:37 +00:00
|
|
|
// add the trailing slash if it is missing
|
2008-12-05 23:37:38 +00:00
|
|
|
if (*pathname && pathname[strlen(pathname)-1] != '/') {
|
|
|
|
strcat(pathname, "/");
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
2008-10-04 08:22:28 +00:00
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2010-05-22 12:05:31 +00:00
|
|
|
* Builds the abolute path name of a given directory beeing
|
|
|
|
* watched from the dir_watches information.
|
2008-08-06 13:04:01 +00:00
|
|
|
*
|
|
|
|
* @param pathname destination buffer to store the result to.
|
|
|
|
* @param pathsize max size of this buffer
|
|
|
|
* @param watch the index in dir_watches to the directory.
|
2008-10-13 02:06:19 +00:00
|
|
|
* @param dirname if not NULL it is added at the end of pathname
|
|
|
|
* @param prefix if not NULL it is added at the beginning of pathname
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
buildpath(const struct log *log,
|
|
|
|
char *pathname,
|
|
|
|
int pathsize,
|
|
|
|
int watch,
|
|
|
|
const char *dirname,
|
|
|
|
const char *prefix)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
2008-12-05 23:37:38 +00:00
|
|
|
int len = builddir(pathname, pathsize, watch, prefix);
|
|
|
|
if (len < 0) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "path too long!");
|
2008-12-05 23:37:38 +00:00
|
|
|
return false;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2008-10-13 02:06:19 +00:00
|
|
|
if (dirname) {
|
2008-12-05 23:37:38 +00:00
|
|
|
if (pathsize < len + strlen(dirname) + 1) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "path too long!");
|
2008-08-12 11:51:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
2008-10-13 02:06:19 +00:00
|
|
|
strcat(pathname, dirname);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, " BUILDPATH(%d, %s, %s) -> %s", watch, dirname, prefix, pathname);
|
2008-08-12 11:51:46 +00:00
|
|
|
return true;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2008-11-05 15:37:21 +00:00
|
|
|
/**
|
|
|
|
* Syncs a directory.
|
2010-05-22 15:25:31 +00:00
|
|
|
* TODO: make better error handling (differ between
|
|
|
|
* directory gone away, and thus cannot work, or network
|
|
|
|
* failed)
|
2008-11-05 15:37:21 +00:00
|
|
|
*
|
2010-05-22 15:25:31 +00:00
|
|
|
* @param watch the index in dir_watches to the directory.
|
2008-12-05 22:27:03 +00:00
|
|
|
*
|
|
|
|
* @returns true when all targets were successful.
|
2008-11-05 15:37:21 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
rsync_dir(const struct global_options *opts, int watch)
|
2008-11-05 15:37:21 +00:00
|
|
|
{
|
|
|
|
char pathname[PATH_MAX+1];
|
|
|
|
char destname[PATH_MAX+1];
|
2008-12-05 22:27:03 +00:00
|
|
|
bool status = true;
|
|
|
|
char ** target;
|
2010-08-02 13:07:27 +00:00
|
|
|
const struct log *log = &opts->log;
|
2008-11-05 15:37:21 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!buildpath(log, pathname, sizeof(pathname), watch, NULL, NULL)) {
|
2008-11-05 15:37:21 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
for (target = dir_watches->data[watch].dir_conf->targets; *target; target++) {
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!buildpath(log, destname, sizeof(destname), watch, NULL, *target)) {
|
2008-12-05 22:27:03 +00:00
|
|
|
status = false;
|
|
|
|
continue;
|
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL, "rsyncing %s --> %s", pathname, destname);
|
2008-11-05 15:37:21 +00:00
|
|
|
|
2008-12-05 22:27:03 +00:00
|
|
|
// call rsync to propagate changes in the directory
|
2010-08-03 12:15:37 +00:00
|
|
|
if (!action(opts, dir_watches->data[watch].dir_conf, pathname, destname, false)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Rsync from %s to %s failed", pathname, destname);
|
2008-12-05 22:27:03 +00:00
|
|
|
status = false;
|
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2008-12-05 22:27:03 +00:00
|
|
|
return status;
|
2008-11-05 15:37:21 +00:00
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
/**
|
|
|
|
* Puts a directory on the TO-DO list. Waiting for its delay
|
|
|
|
* to be actually executed.
|
|
|
|
*
|
|
|
|
* Directly calls rsync_dir if delay == 0;
|
|
|
|
*
|
|
|
|
* @param watch the index in dir_watches to the directory.
|
|
|
|
* @param alarm times() when the directory handling should be fired.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
tackle_dir(const struct global_options *opts, int watch, clock_t alarm)
|
2010-05-22 15:25:31 +00:00
|
|
|
{
|
|
|
|
char pathname[PATH_MAX+1];
|
2010-08-02 13:07:27 +00:00
|
|
|
const struct log *log = &opts->log;
|
2010-05-22 15:25:31 +00:00
|
|
|
|
2010-08-03 12:21:21 +00:00
|
|
|
if (opts->delay == 0) {
|
2010-08-02 13:07:27 +00:00
|
|
|
rsync_dir(opts, watch);
|
2010-05-22 16:02:23 +00:00
|
|
|
return;
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!buildpath(log, pathname, sizeof(pathname), watch, NULL, NULL)) {
|
2010-05-22 15:25:31 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (append_tackle(log, watch, alarm)) {
|
|
|
|
printlogf(log, NORMAL, "Putted %s on a delay", pathname);
|
2010-05-22 15:25:31 +00:00
|
|
|
} else {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL, "Not acted on %s already on delay", pathname);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2010-08-03 14:16:12 +00:00
|
|
|
* Adds a directory including all subdirectories to watch.
|
|
|
|
* Puts the directory with all subdirectories on the tackle FIFO.
|
2008-08-06 13:04:01 +00:00
|
|
|
*
|
2010-08-03 14:16:12 +00:00
|
|
|
* @param opts global options
|
2010-08-03 13:14:37 +00:00
|
|
|
* @param inotify_fd inotify file descriptor.
|
|
|
|
* @param dirname The name or absolute path of the directory to watch.
|
|
|
|
* @param parent If not -1, the index in dir_watches to the parent directory already watched.
|
|
|
|
* Must have absolute path if parent == -1.
|
|
|
|
* @param dir_conf ??? TODO
|
2008-11-05 15:37:21 +00:00
|
|
|
*
|
2010-08-02 13:07:27 +00:00
|
|
|
* @returns the index in dir_watches of the directory or -1 on fail.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 14:16:12 +00:00
|
|
|
int
|
|
|
|
add_dirwatch(const struct global_options *opts,
|
2010-08-03 13:14:37 +00:00
|
|
|
int inotify_fd,
|
|
|
|
char const *dirname,
|
|
|
|
int parent,
|
|
|
|
struct dir_conf *dir_conf)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
2010-08-03 14:16:12 +00:00
|
|
|
const struct log *log = &opts->log;
|
2008-08-12 11:51:46 +00:00
|
|
|
DIR *d;
|
|
|
|
|
2010-08-03 14:16:12 +00:00
|
|
|
int dw;
|
2008-11-03 13:13:29 +00:00
|
|
|
char pathname[PATH_MAX+1];
|
2008-10-04 08:22:28 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "add_dirwatch(%s, p->dirname:%s, ...)",
|
2008-12-05 23:37:38 +00:00
|
|
|
dirname,
|
2010-08-03 12:15:37 +00:00
|
|
|
parent >= 0 ? dir_watches->data[parent].dirname : "NULL");
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!buildpath(log, pathname, sizeof(pathname), parent, dirname, NULL)) {
|
2008-11-05 15:37:21 +00:00
|
|
|
return -1;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-08-03 14:16:12 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < exclude_dir_n; i++) {
|
|
|
|
if (!strcmp(pathname, exclude_dirs[i])) {
|
|
|
|
printlogf(log, NORMAL, "Excluded %s", pathname);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
printlogf(log, DEBUG, "comparing %s with %s not an exclude so far.", pathname, exclude_dirs[i]);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-03 14:16:12 +00:00
|
|
|
// watch this directory
|
2010-08-03 13:14:37 +00:00
|
|
|
dw = add_watch(log, inotify_fd, pathname, dirname, parent, dir_conf);
|
2008-08-12 11:51:46 +00:00
|
|
|
if (dw == -1) {
|
2008-11-05 15:37:21 +00:00
|
|
|
return -1;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-08-03 14:16:12 +00:00
|
|
|
// put this directory on list to be synced ASAP.
|
|
|
|
tackle_dir(opts, dw, times(NULL));
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
if (strlen(pathname) + strlen(dirname) + 2 > sizeof(pathname)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "pathname too long %s//%s", pathname, dirname);
|
2008-11-05 15:37:21 +00:00
|
|
|
return -1;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
d = opendir(pathname);
|
|
|
|
if (d == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "cannot open dir %s.", dirname);
|
2008-11-05 15:37:21 +00:00
|
|
|
return -1;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-05-22 12:09:57 +00:00
|
|
|
while (keep_going) { // terminate early on KILL signal
|
2008-11-21 09:47:28 +00:00
|
|
|
bool isdir;
|
2010-08-03 14:16:12 +00:00
|
|
|
struct dirent *de = readdir(d);
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-05-22 12:09:57 +00:00
|
|
|
if (de == NULL) { // finished reading the directory
|
2008-10-04 08:22:28 +00:00
|
|
|
break;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-05-22 12:09:57 +00:00
|
|
|
// detemine if an entry is a directory or file
|
2008-11-21 09:47:28 +00:00
|
|
|
if (de->d_type == DT_DIR) {
|
|
|
|
isdir = true;
|
|
|
|
} else if (de->d_type == DT_UNKNOWN) {
|
|
|
|
// in case of reiserfs, d_type will be UNKNOWN, how evil! :-(
|
|
|
|
// use traditional means to determine if its a directory.
|
2010-08-03 14:16:12 +00:00
|
|
|
char subdir[PATH_MAX+1];
|
|
|
|
struct stat st;
|
2010-08-02 13:07:27 +00:00
|
|
|
isdir = buildpath(log, subdir, sizeof(subdir), dw, de->d_name, NULL) &&
|
2008-11-21 09:47:28 +00:00
|
|
|
!stat(subdir, &st) &&
|
2008-12-05 23:54:13 +00:00
|
|
|
S_ISDIR(st.st_mode);
|
2008-11-21 09:47:28 +00:00
|
|
|
} else {
|
|
|
|
isdir = false;
|
|
|
|
}
|
2010-05-22 12:09:57 +00:00
|
|
|
|
|
|
|
// add watches if its a directory and not . or ..
|
2008-11-21 09:47:28 +00:00
|
|
|
if (isdir && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) {
|
2010-08-03 13:14:37 +00:00
|
|
|
// recurse into subdir
|
2010-08-03 14:16:12 +00:00
|
|
|
int ndw = add_dirwatch(opts, inotify_fd, de->d_name, dw, dir_conf);
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL,
|
2010-05-20 16:44:13 +00:00
|
|
|
"found new directory: %s in %s -- %s",
|
2010-08-03 14:16:12 +00:00
|
|
|
de->d_name, dirname, ndw >= 0 ? "will be synced" : "ignored it");
|
2008-10-04 08:22:28 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(d);
|
2008-11-05 15:37:21 +00:00
|
|
|
return dw;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a watched dir, including recursevily all subdirs.
|
|
|
|
*
|
2010-08-03 13:14:37 +00:00
|
|
|
* @param opts global options.
|
|
|
|
* @param inotify_fd inotify file descriptor.
|
|
|
|
* @param name optionally. If not NULL, the directory name
|
|
|
|
* to remove which is a child of parent.
|
|
|
|
* @param parent the index to the parent directory of the
|
|
|
|
* directory 'name' to remove, or to be removed
|
|
|
|
* itself if name == NULL.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 14:16:12 +00:00
|
|
|
bool
|
2010-08-03 13:14:37 +00:00
|
|
|
remove_dirwatch(const struct global_options *opts,
|
|
|
|
int inotify_fd,
|
|
|
|
const char * name,
|
|
|
|
int parent)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
2010-08-03 12:21:21 +00:00
|
|
|
const struct log *log = &opts->log;
|
2010-08-03 12:15:37 +00:00
|
|
|
int dw; // the directory index to remove.
|
2008-08-12 11:51:46 +00:00
|
|
|
if (name) {
|
2010-08-03 12:15:37 +00:00
|
|
|
int i;
|
2008-08-12 11:51:46 +00:00
|
|
|
// look for the child with the name
|
2010-08-03 12:15:37 +00:00
|
|
|
for (i = 0; i < dir_watches->len; i++) {
|
|
|
|
struct dir_watch *p = dir_watches->data + i;
|
|
|
|
if (p->wd >= 0 && p->parent == parent && !strcmp(name, p->dirname)) {
|
2008-08-12 11:51:46 +00:00
|
|
|
dw = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
if (i >= dir_watches->len) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Cannot find entry for %s:/:%s :-(",
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->data[parent].dirname, name);
|
2008-08-12 11:51:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dw = parent;
|
|
|
|
}
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
{
|
|
|
|
// recurse into all subdirectories removing them.
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < dir_watches->len; i++) {
|
|
|
|
if (dir_watches->data[i].wd >= 0 && dir_watches->data[i].parent == dw) {
|
2010-08-03 13:14:37 +00:00
|
|
|
remove_dirwatch(opts, inotify_fd, NULL, i);
|
2010-08-03 12:15:37 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-03 13:14:37 +00:00
|
|
|
inotify_rm_watch(inotify_fd, dir_watches->data[dw].wd);
|
2010-08-03 12:15:37 +00:00
|
|
|
// mark this entry invalid (cannot remove, since indexes point into this vector)
|
|
|
|
// TODO from where?
|
|
|
|
dir_watches->data[dw].wd = -1;
|
|
|
|
|
|
|
|
free(dir_watches->data[dw].dirname);
|
|
|
|
dir_watches->data[dw].dirname = NULL;
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-05-22 16:21:42 +00:00
|
|
|
// remove a possible tackle
|
|
|
|
// (this dir is on the delay list)
|
2010-08-03 12:21:21 +00:00
|
|
|
if (opts->delay > 0 && dir_watches->data[dw].tackled) {
|
2010-08-03 12:15:37 +00:00
|
|
|
int i;
|
|
|
|
for(i = 0; i < tackles->len; i++) {
|
2010-08-02 17:30:06 +00:00
|
|
|
if (tackles->data[i] == dw) {
|
2010-05-22 16:21:42 +00:00
|
|
|
// move the list up.
|
2010-08-03 12:15:37 +00:00
|
|
|
// TODO move own logic
|
2010-08-02 17:30:06 +00:00
|
|
|
memmove(tackles->data + i, tackles->data + i + 1, (tackles->len - i - 1) * sizeof(int));
|
|
|
|
tackles->len--;
|
2010-05-22 16:21:42 +00:00
|
|
|
break;
|
2010-08-03 12:15:37 +00:00
|
|
|
}
|
2010-05-22 16:21:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
return true;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2008-10-13 02:06:19 +00:00
|
|
|
/**
|
|
|
|
* Find the matching dw entry from wd (watch descriptor), and return
|
|
|
|
* the offset in the table.
|
|
|
|
*
|
|
|
|
* @param wd The wd (watch descriptor) given by inotify
|
|
|
|
* @return offset, or -1 if not found
|
|
|
|
*/
|
2010-08-03 14:16:12 +00:00
|
|
|
int
|
2010-08-03 13:14:37 +00:00
|
|
|
get_dirwatch_offset(int wd) {
|
2008-10-13 02:06:19 +00:00
|
|
|
int i;
|
2010-08-03 12:15:37 +00:00
|
|
|
for (i = 0; i < dir_watches->len; i++) {
|
|
|
|
if (dir_watches->data[i].wd == wd) {
|
|
|
|
return i;
|
2008-10-13 02:06:19 +00:00
|
|
|
}
|
|
|
|
}
|
2010-08-03 12:15:37 +00:00
|
|
|
return -1;
|
2008-10-13 02:06:19 +00:00
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
|
|
|
* Handles an inotify event.
|
2010-08-02 13:07:27 +00:00
|
|
|
*
|
2010-08-03 13:14:37 +00:00
|
|
|
* @param opts global options
|
|
|
|
* @param inotify_fd inotify file descriptor
|
|
|
|
* @param event the event to handle
|
|
|
|
* @param alarm times() moment when it should fire
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
handle_event(const struct global_options *opts,
|
|
|
|
int inotify_fd,
|
|
|
|
struct inotify_event *event,
|
|
|
|
clock_t alarm)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
char masktext[255] = {0,};
|
|
|
|
int mask = event->mask;
|
2008-11-20 21:38:58 +00:00
|
|
|
int i, watch;
|
|
|
|
int subwatch = -1;
|
2008-08-12 11:51:46 +00:00
|
|
|
struct inotify_mask_text *p;
|
2010-08-02 13:07:27 +00:00
|
|
|
const struct log *log = &opts->log;
|
2008-12-05 23:54:13 +00:00
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
// creates a string for logging that shows which flags
|
|
|
|
// were raised in the event
|
2008-08-12 11:51:46 +00:00
|
|
|
for (p = mask_texts; p->mask; p++) {
|
|
|
|
if (mask & p->mask) {
|
|
|
|
if (strlen(masktext) + strlen(p->text) + 3 >= sizeof(masktext)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "bufferoverflow in handle_event");
|
2008-08-12 11:51:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*masktext) {
|
|
|
|
strcat(masktext, ", ");
|
|
|
|
}
|
|
|
|
|
|
|
|
strcat(masktext, p->text);
|
|
|
|
}
|
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "inotfy event: %s:%s", masktext, event->name);
|
2008-11-04 21:41:07 +00:00
|
|
|
|
|
|
|
if (IN_IGNORED & event->mask) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
// TODO, is this needed?
|
2008-11-04 21:41:07 +00:00
|
|
|
for (i = 0; i < exclude_dir_n; i++) {
|
|
|
|
if (!strcmp(event->name, exclude_dirs[i])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
watch = get_dirwatch_offset(event->wd);
|
|
|
|
if (watch == -1) {
|
2010-05-22 15:25:31 +00:00
|
|
|
// this should not happen!
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR,
|
2008-11-20 21:38:58 +00:00
|
|
|
"received an inotify event that doesnt match any watched directory :-(%d,%d)",
|
|
|
|
event->mask, event->wd);
|
2008-08-12 11:51:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
// in case of a new directory create new watches
|
2008-10-04 08:22:28 +00:00
|
|
|
if (((IN_CREATE | IN_MOVED_TO) & event->mask) && (IN_ISDIR & event->mask)) {
|
2010-08-03 14:16:12 +00:00
|
|
|
subwatch = add_dirwatch(opts, inotify_fd, event->name, watch, dir_watches->data[watch].dir_conf);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
// in case of a removed directory remove watches
|
2008-08-12 11:51:46 +00:00
|
|
|
if (((IN_DELETE | IN_MOVED_FROM) & event->mask) && (IN_ISDIR & event->mask)) {
|
2010-08-03 13:14:37 +00:00
|
|
|
remove_dirwatch(opts, inotify_fd, event->name, watch);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2008-11-05 15:37:21 +00:00
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
// call the binary if something changed
|
2009-04-05 14:49:27 +00:00
|
|
|
if ((IN_ATTRIB | IN_CREATE | IN_CLOSE_WRITE | IN_DELETE |
|
2008-11-20 21:38:58 +00:00
|
|
|
IN_MOVED_TO | IN_MOVED_FROM) & event->mask
|
|
|
|
) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL, "event %s:%s triggered.", masktext, event->name);
|
2010-05-22 15:25:31 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
tackle_dir(opts, watch, alarm);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "... ignored this event.");
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
return true;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The control loop waiting for inotify events.
|
2010-08-02 13:07:27 +00:00
|
|
|
*
|
2010-08-03 13:14:37 +00:00
|
|
|
* @param opts global options
|
|
|
|
* @param inotify_fd inotify file descriptor
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
master_loop(const struct global_options *opts,
|
|
|
|
int inotify_fd)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
char buf[INOTIFY_BUF_LEN];
|
|
|
|
int len, i = 0;
|
2010-05-22 15:25:31 +00:00
|
|
|
long clocks_per_sec = sysconf(_SC_CLK_TCK);
|
|
|
|
|
|
|
|
struct timeval tv;
|
|
|
|
fd_set readfds;
|
|
|
|
clock_t now;
|
|
|
|
clock_t alarm;
|
2010-08-02 13:07:27 +00:00
|
|
|
const struct log *log = &opts->log;
|
2010-05-22 15:25:31 +00:00
|
|
|
|
|
|
|
FD_ZERO(&readfds);
|
2010-08-03 13:14:37 +00:00
|
|
|
FD_SET(inotify_fd, &readfds);
|
2010-05-22 15:25:31 +00:00
|
|
|
|
2010-08-03 12:21:21 +00:00
|
|
|
if (opts->delay > 0) {
|
2010-05-22 15:25:31 +00:00
|
|
|
if (clocks_per_sec <= 0) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Clocks per seoond invalid! %d", printlogf);
|
|
|
|
terminate(log, LSYNCD_INTERNALFAIL);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
while (keep_going) {
|
2010-08-03 12:15:37 +00:00
|
|
|
int ready;
|
2010-08-03 12:21:21 +00:00
|
|
|
if (opts->delay > 0 && tackles->len > 0) {
|
2010-05-22 15:25:31 +00:00
|
|
|
// use select() to determine what happens first
|
|
|
|
// a new event or "alarm" of an event to actually
|
2010-08-03 12:15:37 +00:00
|
|
|
// call its binary. The tackle with the index 0
|
|
|
|
// should have the nearest alarm time.
|
|
|
|
alarm = dir_watches->data[tackles->data[0]].alarm;
|
2010-05-22 15:25:31 +00:00
|
|
|
now = times(NULL);
|
|
|
|
tv.tv_sec = (alarm - now) / clocks_per_sec;
|
|
|
|
tv.tv_usec = (alarm - now) * 1000000 / clocks_per_sec % 1000000;
|
2010-08-03 12:21:21 +00:00
|
|
|
if (tv.tv_sec > opts->delay) {
|
2010-05-24 12:32:34 +00:00
|
|
|
// security boundary in case of times() wrap around.
|
2010-08-03 12:21:21 +00:00
|
|
|
tv.tv_sec = opts->delay;
|
2010-05-24 12:32:34 +00:00
|
|
|
tv.tv_usec = 0;
|
|
|
|
}
|
2010-08-03 13:14:37 +00:00
|
|
|
ready = select(inotify_fd + 1, &readfds, NULL, NULL, &tv);
|
2010-05-22 15:25:31 +00:00
|
|
|
} else {
|
|
|
|
// if nothing to wait for, enter a blocking read
|
|
|
|
// (sorry this variable is named a bit confusing
|
|
|
|
// in this case)
|
|
|
|
ready = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ready) {
|
2010-08-03 13:14:37 +00:00
|
|
|
len = read (inotify_fd, buf, INOTIFY_BUF_LEN);
|
2010-05-22 15:25:31 +00:00
|
|
|
} else {
|
|
|
|
len = 0;
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
if (len < 0) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "failed to read from inotify (%d:%s)", errno, strerror(errno));
|
2008-08-12 11:51:46 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
now = times(NULL);
|
2010-08-03 12:21:21 +00:00
|
|
|
alarm = now + opts->delay * clocks_per_sec;
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
// first handle all events that might have happened
|
2008-08-12 11:51:46 +00:00
|
|
|
i = 0;
|
|
|
|
while (i < len) {
|
|
|
|
struct inotify_event *event = (struct inotify_event *) &buf[i];
|
2010-08-03 13:14:37 +00:00
|
|
|
handle_event(opts, inotify_fd, event, alarm);
|
2008-08-12 11:51:46 +00:00
|
|
|
i += sizeof(struct inotify_event) + event->len;
|
|
|
|
}
|
2010-05-22 15:25:31 +00:00
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
// Then pull of directories from the top of the tackle stack
|
|
|
|
// until one item is found whose expiry time has not yet come
|
|
|
|
// or the stack is empty.
|
|
|
|
while (tackles->len > 0 && time_after(times(NULL), dir_watches->data[tackles->data[0]].alarm)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, DEBUG, "time for %d arrived.", tackles[0]);
|
2010-08-02 17:30:06 +00:00
|
|
|
rsync_dir(opts, tackles->data[0]);
|
2010-05-22 15:25:31 +00:00
|
|
|
remove_first_tackle();
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-10-08 04:08:56 +00:00
|
|
|
/**
|
|
|
|
* Utility function to check file exists. Print out error message and die.
|
|
|
|
*
|
|
|
|
* @param filename filename to check
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
check_file_exists(const struct log* log, const char* filename, const char *errmsg)
|
2008-10-08 04:08:56 +00:00
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
if (-1==stat(filename, &st)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "%s [%s] does not exist.\n", filename);
|
|
|
|
terminate(log, LSYNCD_FILENOTFOUND);
|
2008-10-08 04:08:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Utility function to check given path is absolute path.
|
|
|
|
*
|
2010-08-02 13:07:27 +00:00
|
|
|
* @param filename Filename to check
|
|
|
|
* @param errmsg Filetype text to prepend to the error message.
|
2008-10-08 04:08:56 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
check_absolute_path(const struct log* log, const char* filename, const char *filetype)
|
2008-10-08 04:08:56 +00:00
|
|
|
{
|
|
|
|
if (filename[0] != '/') {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "%s [%s] has do be an absolute path.\n", filetype, filename);
|
|
|
|
terminate(log, LSYNCD_FILENOTFOUND);
|
2008-10-08 04:08:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
/**
|
2008-08-06 13:04:01 +00:00
|
|
|
* Prints the help text and exits 0.
|
2008-08-12 11:51:46 +00:00
|
|
|
*
|
2008-08-06 13:04:01 +00:00
|
|
|
* @param arg0 argv[0] to show what lsyncd was called with.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
print_help(char *arg0)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
|
|
|
printf("\n");
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
2008-12-05 22:27:03 +00:00
|
|
|
printf("USAGE: %s [OPTION]... [SOURCE] [TARGET 1] [TARGET 2] ...\n", arg0);
|
2008-11-20 21:38:58 +00:00
|
|
|
#else
|
2008-12-05 22:27:03 +00:00
|
|
|
printf("USAGE: %s [OPTION]... SOURCE TARGET-1 TARGET-2 ...\n", arg0);
|
2008-11-20 21:38:58 +00:00
|
|
|
#endif
|
2008-08-12 11:51:46 +00:00
|
|
|
printf("\n");
|
|
|
|
printf("SOURCE: a directory to watch and rsync.\n");
|
|
|
|
printf("\n");
|
|
|
|
printf("TARGET: can be any name accepted by rsync. e.g. \"foohost::barmodule/\"\n");
|
|
|
|
printf("\n");
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
|
|
|
printf("When called without SOURCE and TARGET, the\n");
|
|
|
|
printf("configuration will be read from the config file\"\n");
|
|
|
|
#endif
|
|
|
|
printf("\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf("OPTIONS:\n");
|
2010-08-02 13:07:27 +00:00
|
|
|
printf(" --binary FILE Call this binary to sync " "(DEFAULT: %s)\n", DEFAULT_BINARY);
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
|
|
|
printf(" --conf FILE Load configuration from this file\n");
|
2010-08-02 13:07:27 +00:00
|
|
|
printf(" (DEFAULT: %s if called without SOURCE/TARGET)\n", DEFAULT_CONF_FILENAME);
|
2008-11-20 21:38:58 +00:00
|
|
|
#endif
|
2008-08-06 13:04:01 +00:00
|
|
|
printf(" --debug Log debug messages\n");
|
2010-05-22 15:25:31 +00:00
|
|
|
printf(" --delay SECS Delay between event and action\n");
|
2008-11-20 21:38:58 +00:00
|
|
|
printf(" --dryrun Do not call any actions, run dry only\n");
|
|
|
|
printf(" --exclude-from FILE Exclude file handled to rsync (DEFAULT: None)\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf(" --help Print this help text and exit.\n");
|
2009-02-28 15:58:06 +00:00
|
|
|
printf(" --logfile FILE Put log here (DEFAULT: uses syslog if not specified)\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf(" --no-daemon Do not detach, log to stdout/stderr\n");
|
2010-07-27 13:00:32 +00:00
|
|
|
printf(" --no-startup Do not execute a startup sync (disadviced, know what you doing)\n");
|
2008-10-08 04:08:52 +00:00
|
|
|
printf(" --pidfile FILE Create a file containing pid of the daemon\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf(" --scarce Only log errors\n");
|
2008-12-13 17:30:52 +00:00
|
|
|
printf(" --stubborn Ignore rsync errors on startup.\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf(" --version Print version an exit.\n");
|
2008-08-12 11:51:46 +00:00
|
|
|
printf("\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf("EXCLUDE FILE: \n");
|
|
|
|
printf(" The exclude file may have either filebased general masks like \"*.php\" without directory specifications,\n");
|
|
|
|
printf(" or exclude complete directories like \"Data/\". lsyncd will recognize directory excludes by the trailing '/'\n");
|
|
|
|
printf(" and will not add watches of directories of exactly such name including sub-directories of them.\n");
|
2008-08-12 11:51:46 +00:00
|
|
|
printf(" Please do not try to use more sophisticated exclude masks like \"Data/*.dat\" or \"Da*a/\", \"Data/Volatile/\" etc.\n");
|
2008-08-06 13:04:01 +00:00
|
|
|
printf(" This will not work like you would expect it to.\n");
|
2008-08-12 11:51:46 +00:00
|
|
|
printf("\n");
|
|
|
|
printf("LICENSE\n");
|
|
|
|
printf(" GPLv2 or any later version. See COPYING\n");
|
|
|
|
printf("\n");
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifndef XML_CONFIG
|
|
|
|
printf("(this lsyncd binary was not compiled to be able to read config files)\n");
|
|
|
|
#endif
|
2008-08-12 11:51:46 +00:00
|
|
|
exit(0);
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
|
|
|
/*--------------------------------------------------------------------------*
|
|
|
|
* config file parsing
|
|
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses <callopts>
|
|
|
|
*
|
|
|
|
* @return the allocated and filled calloptions structure
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
struct call_option *
|
|
|
|
parse_callopts(struct global_options *opts, xmlNodePtr node) {
|
2008-11-20 21:38:58 +00:00
|
|
|
xmlNodePtr cnode;
|
|
|
|
xmlChar *xc;
|
|
|
|
int opt_n = 0;
|
|
|
|
struct call_option * asw;
|
|
|
|
|
|
|
|
// count how many options are there
|
|
|
|
for (cnode = node->children; cnode; cnode = cnode->next) {
|
|
|
|
if (cnode->type != XML_ELEMENT_NODE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (xmlStrcmp(cnode->name, BAD_CAST "option") &&
|
|
|
|
xmlStrcmp(cnode->name, BAD_CAST "exclude-file") &&
|
|
|
|
xmlStrcmp(cnode->name, BAD_CAST "source") &&
|
|
|
|
xmlStrcmp(cnode->name, BAD_CAST "destination")
|
|
|
|
) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error unknown call option type \"%s\"", cnode->name);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
opt_n++;
|
|
|
|
}
|
|
|
|
opt_n++;
|
2010-08-02 13:07:27 +00:00
|
|
|
asw = (struct call_option *) s_calloc(NULL, opt_n, sizeof(struct call_option));
|
2008-11-20 21:38:58 +00:00
|
|
|
|
|
|
|
// fill in the answer
|
|
|
|
opt_n = 0;
|
|
|
|
for (cnode = node->children; cnode; cnode = cnode->next) {
|
|
|
|
if (cnode->type != XML_ELEMENT_NODE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
asw[opt_n].text = NULL;
|
|
|
|
if (!xmlStrcmp(cnode->name, BAD_CAST "option")) {
|
|
|
|
xc = xmlGetProp(cnode, BAD_CAST "text");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: text attribute missing from <option/>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
asw[opt_n].kind = CO_TEXT;
|
2010-08-02 13:07:27 +00:00
|
|
|
asw[opt_n].text = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(cnode->name, BAD_CAST "exclude-file")) {
|
|
|
|
asw[opt_n].kind = CO_EXCLUDE;
|
|
|
|
} else if (!xmlStrcmp(cnode->name, BAD_CAST "source")) {
|
|
|
|
asw[opt_n].kind = CO_SOURCE;
|
|
|
|
} else if (!xmlStrcmp(cnode->name, BAD_CAST "destination")) {
|
|
|
|
asw[opt_n].kind = CO_DEST;
|
|
|
|
} else {
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
opt_n++;
|
|
|
|
}
|
|
|
|
asw[opt_n].text = NULL;
|
|
|
|
asw[opt_n].kind = CO_EOL;
|
|
|
|
return asw;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses <diretory>
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
parse_directory(struct global_options *opts, xmlNodePtr node) {
|
2008-11-20 21:38:58 +00:00
|
|
|
xmlNodePtr dnode;
|
|
|
|
xmlChar *xc;
|
2010-08-02 13:07:27 +00:00
|
|
|
struct dir_conf * dc = new_dir_conf(opts);
|
2008-11-20 21:38:58 +00:00
|
|
|
for (dnode = node->children; dnode; dnode = dnode->next) {
|
|
|
|
if (dnode->type != XML_ELEMENT_NODE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!xmlStrcmp(dnode->name, BAD_CAST "source")) {
|
|
|
|
xc = xmlGetProp(dnode, BAD_CAST "path");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute path missing from <source>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-21 09:41:42 +00:00
|
|
|
}
|
|
|
|
if (dc->source) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: cannot have more than one source in one <directory>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2008-12-05 23:37:38 +00:00
|
|
|
// TODO: use realdir() on xc
|
2010-08-02 13:07:27 +00:00
|
|
|
dc->source = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(dnode->name, BAD_CAST "target")) {
|
|
|
|
xc = xmlGetProp(dnode, BAD_CAST "path");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute path missing from <target>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-21 09:41:42 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
dir_conf_add_target(NULL, dc, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(dnode->name, BAD_CAST "binary")) {
|
|
|
|
xc = xmlGetProp(dnode, BAD_CAST "filename");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute filename missing from <binary>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
dc->binary = s_strdup(NULL, (char *) xc);
|
2010-05-22 12:05:31 +00:00
|
|
|
} else if (!xmlStrcmp(dnode->name, BAD_CAST "exclude-from")) {
|
|
|
|
xc = xmlGetProp(dnode, BAD_CAST "filename");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute filename missing from <exclude-from>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2010-05-22 12:05:31 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
dc->exclude_file = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(dnode->name, BAD_CAST "callopts")) {
|
|
|
|
if (dc->callopts) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: there is more than one <callopts> in a <directory>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
dc->callopts = parse_callopts(opts, dnode);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else {
|
2010-05-20 12:42:58 +00:00
|
|
|
// TODO missing sourcespecific exclude files?
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: unknown node in <directory> \"%s\"\n", dnode->name);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
2008-11-21 09:41:42 +00:00
|
|
|
if (!dc->source) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: source missing from <directory>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-21 09:41:42 +00:00
|
|
|
}
|
2008-12-05 22:27:03 +00:00
|
|
|
if (dc->targets[0] == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: target missing from <directory>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-21 09:41:42 +00:00
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses <settings>
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
parse_settings(struct global_options *opts, xmlNodePtr node) {
|
2008-11-20 21:38:58 +00:00
|
|
|
xmlNodePtr snode;
|
|
|
|
xmlChar *xc;
|
|
|
|
|
|
|
|
for (snode = node->children; snode; snode = snode->next) {
|
|
|
|
if (snode->type != XML_ELEMENT_NODE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!xmlStrcmp(snode->name, BAD_CAST "debug")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->log.loglevel = 1;
|
2010-05-22 15:25:31 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "delay")) {
|
|
|
|
char *p;
|
|
|
|
xc = xmlGetProp(snode, BAD_CAST "value");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute value missing from <delay/>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
2010-08-03 12:21:21 +00:00
|
|
|
opts->delay = strtol((char *) xc, &p, 10);
|
2010-05-22 15:25:31 +00:00
|
|
|
if (*p) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "<delay> value %s is not an integer.\n", xc);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
2010-08-03 12:21:21 +00:00
|
|
|
if (opts->delay < 0) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "<delay> value may not be negative.\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "dryrun")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->flag_dryrun = 1;
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "exclude-from")) {
|
|
|
|
xc = xmlGetProp(snode, BAD_CAST "filename");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute filename missing from <exclude-from/>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->default_exclude_file = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "logfile")) {
|
|
|
|
xc = xmlGetProp(snode, BAD_CAST "filename");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute filename missing from <logfile/>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->log.logfile = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "binary")) {
|
|
|
|
xc = xmlGetProp(snode, BAD_CAST "filename");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute filename missing from <binary/>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->default_binary = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "pidfile")) {
|
|
|
|
xc = xmlGetProp(snode, BAD_CAST "filename");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: attribute filename missing from <pidfile/>\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->pidfile = s_strdup(NULL, (char *) xc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "callopts")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->default_callopts = parse_callopts(opts, snode);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "scarce")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->log.loglevel = 3;
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "no-daemon")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->log.flag_nodaemon = 1;
|
2010-07-27 13:00:32 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "no-startup")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->flag_nostartup = 1;
|
2008-12-13 17:30:52 +00:00
|
|
|
} else if (!xmlStrcmp(snode->name, BAD_CAST "stubborn")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->flag_stubborn = 1;
|
2008-11-20 21:38:58 +00:00
|
|
|
} else {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error unknown node in <settings> \"%s\"", snode->name);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the config file specified in the global variable
|
|
|
|
* conf_filename, fills the global options value according
|
|
|
|
* to the config file.
|
|
|
|
*
|
|
|
|
* @param fullparse if false only read globals.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
parse_config(struct global_options *opts, bool fullparse) {
|
2008-11-20 21:38:58 +00:00
|
|
|
LIBXML_TEST_VERSION
|
|
|
|
xmlDoc *doc = NULL;
|
|
|
|
xmlNode *root_element = NULL;
|
|
|
|
xmlNodePtr node;
|
|
|
|
xmlChar *xc;
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
doc = xmlReadFile(opts->conf_filename, NULL, 0);
|
2008-11-20 21:38:58 +00:00
|
|
|
if (doc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error: could not parse config file \"%s\"\n", opts->conf_filename);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
root_element = xmlDocGetRootElement(doc);
|
|
|
|
|
|
|
|
// check version specifier
|
|
|
|
if (xmlStrcmp(root_element->name, BAD_CAST "lsyncd")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: root node is not \"lsyncd\".\n");
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
xc = xmlGetProp(root_element, BAD_CAST "version");
|
|
|
|
if (xc == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: version specifier missing in \"%s\",\n", opts->conf_filename);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2009-01-05 13:45:12 +00:00
|
|
|
if (xmlStrcmp(xc, BAD_CAST "1") && xmlStrcmp(xc, BAD_CAST "1.25")) { //1.25, backward stuff
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: expected a \"1\" versioned file, found \"%s\"\n", xc);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (node = root_element->children; node; node = node->next) {
|
|
|
|
if (node->type != XML_ELEMENT_NODE) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!xmlStrcmp(node->name, BAD_CAST "settings")) {
|
2010-08-02 13:07:27 +00:00
|
|
|
parse_settings(opts, node);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (!xmlStrcmp(node->name, BAD_CAST "directory")) {
|
|
|
|
if (fullparse) {
|
2010-08-02 13:07:27 +00:00
|
|
|
parse_directory(opts, node);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(NULL, ERROR, "error in config file: unknown node in <lsyncd> \"%s\"\n", node->name);
|
|
|
|
terminate(NULL, LSYNCD_BADCONFIGFILE);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-13 18:06:11 +00:00
|
|
|
|
|
|
|
xmlFreeDoc(doc);
|
|
|
|
xmlCleanupParser();
|
2008-11-20 21:38:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
|
|
|
* Parses the command line options.
|
2008-12-13 22:14:58 +00:00
|
|
|
*
|
2009-02-28 15:58:06 +00:00
|
|
|
* terminates in some cases of badparameters, or on
|
2008-12-13 22:14:58 +00:00
|
|
|
* --version or --help
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
parse_options(struct global_options *opts, int argc, char **argv)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
2008-12-05 22:27:03 +00:00
|
|
|
char **target;
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
static struct option long_options[] = {
|
2010-08-02 13:07:27 +00:00
|
|
|
{"binary", 1, NULL, 0},
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
2010-08-02 13:07:27 +00:00
|
|
|
{"conf", 1, NULL, 0},
|
2008-11-20 21:38:58 +00:00
|
|
|
#endif
|
2010-08-02 13:07:27 +00:00
|
|
|
{"debug", 0, NULL, 1},
|
|
|
|
{"delay", 1, NULL, 0},
|
|
|
|
{"dryrun", 0, NULL, 1},
|
|
|
|
{"exclude-from", 1, NULL, 0},
|
|
|
|
{"help", 0, NULL, 0},
|
|
|
|
{"logfile", 1, NULL, 0},
|
|
|
|
{"no-daemon", 0, NULL, 1},
|
|
|
|
{"no-startup", 0, NULL, 1},
|
|
|
|
{"pidfile", 1, NULL, 0},
|
|
|
|
{"scarce", 0, NULL, 3},
|
|
|
|
{"stubborn", 0, NULL, 1},
|
|
|
|
{"version", 0, NULL, 0},
|
|
|
|
{NULL, 0, NULL, 0}
|
2008-08-12 11:51:46 +00:00
|
|
|
};
|
2010-07-27 13:00:32 +00:00
|
|
|
bool read_conf = false;
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
{
|
|
|
|
// replace NULL targets with actual targets
|
|
|
|
// because compiler wont allow to init with them.
|
|
|
|
struct option *o;
|
|
|
|
for(o = long_options; o->name; o++) {
|
|
|
|
if (!strcmp("debug", o->name)) o->flag = &opts->log.loglevel;
|
|
|
|
if (!strcmp("dryrun", o->name)) o->flag = &opts->flag_dryrun;
|
|
|
|
if (!strcmp("no-daemon", o->name)) o->flag = &opts->log.flag_nodaemon;
|
|
|
|
if (!strcmp("no-startup", o->name)) o->flag = &opts->flag_nostartup;
|
|
|
|
if (!strcmp("scarce", o->name)) o->flag = &opts->log.loglevel;
|
|
|
|
if (!strcmp("stubborn", o->name)) o->flag = &opts->flag_stubborn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
2010-07-27 13:00:32 +00:00
|
|
|
// First determine if the config file should be read at all.
|
|
|
|
// If so, read it before parsing all other options in detail,
|
|
|
|
// because command line options should overwrite settings in
|
|
|
|
// the confing file.
|
|
|
|
//
|
2008-11-20 21:38:58 +00:00
|
|
|
// There are 2 conditions in which the conf file is read, either
|
|
|
|
// --conf FILE is given as option, or there isn't a SOURCE and
|
|
|
|
// DESTINATION given, in which getting the config from the conf
|
|
|
|
// file will be the default option.
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
while (1) {
|
|
|
|
int oi = 0;
|
2008-11-20 21:38:58 +00:00
|
|
|
int c = getopt_long_only(argc, argv, "", long_options, &oi);
|
2008-08-12 11:51:46 +00:00
|
|
|
if (c == -1) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (c == '?') {
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_BADPARAMETERS);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
if (c == 0) { // longoption
|
2008-11-20 21:38:58 +00:00
|
|
|
if (!strcmp("conf", long_options[oi].name)) {
|
|
|
|
read_conf = true;
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->conf_filename = s_strdup(NULL, optarg);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
if (!strcmp("help", long_options[oi].name)) {
|
2008-11-20 21:38:58 +00:00
|
|
|
// in case --help do not go further, or else
|
|
|
|
// lsyncd would complain of not being configured ...
|
2008-08-12 11:51:46 +00:00
|
|
|
print_help(argv[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp("version", long_options[oi].name)) {
|
2008-11-20 21:38:58 +00:00
|
|
|
// same here
|
2008-10-13 02:06:12 +00:00
|
|
|
printf("Version: %s\n", VERSION);
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_SUCCESS);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (read_conf) {
|
2010-08-02 13:07:27 +00:00
|
|
|
// parse config file, when additional source/dest parameters are
|
|
|
|
// given on the command line, then the directory settings
|
|
|
|
// in the config file are ignored.
|
|
|
|
parse_config(opts, optind == argc);
|
2008-11-20 21:38:58 +00:00
|
|
|
} else if (optind == argc) {
|
2010-08-02 13:07:27 +00:00
|
|
|
// when no config file is specified and there are also
|
|
|
|
// no source/targets, read the default config file.
|
|
|
|
parse_config(opts, true);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
|
|
|
|
// resets the get option parser
|
2008-11-20 21:38:58 +00:00
|
|
|
optind = 1;
|
|
|
|
#endif
|
2010-07-27 13:00:32 +00:00
|
|
|
|
|
|
|
// now parse all the other options normally.
|
2008-11-20 21:38:58 +00:00
|
|
|
while (1) {
|
|
|
|
int oi = 0;
|
|
|
|
int c = getopt_long_only(argc, argv, "", long_options, &oi);
|
|
|
|
if (c == -1) {
|
|
|
|
break;
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
if (c == '?') {
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_BADPARAMETERS);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
if (c == 0) { // longoption
|
|
|
|
if (!strcmp("binary", long_options[oi].name)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->default_binary = s_strdup(NULL, optarg);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
|
2010-05-22 15:25:31 +00:00
|
|
|
if (!strcmp("delay", long_options[oi].name)) {
|
|
|
|
char *p;
|
2010-08-03 12:21:21 +00:00
|
|
|
opts->delay = strtol(optarg, &p, 10);
|
2010-05-22 15:25:31 +00:00
|
|
|
if (*p) {
|
|
|
|
printf("%s is not an integer.\n", optarg);
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_BADPARAMETERS);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
2010-08-03 12:21:21 +00:00
|
|
|
if (opts->delay < 0) {
|
2010-05-22 15:25:31 +00:00
|
|
|
printf("delay may not be negative.\n");
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_BADPARAMETERS);
|
2010-05-22 15:25:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
if (!strcmp("exclude-from", long_options[oi].name)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->default_exclude_file = s_strdup(NULL, optarg);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2008-10-06 20:35:52 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
if (!strcmp("help", long_options[oi].name)) {
|
|
|
|
print_help(argv[0]);
|
2008-10-06 20:35:52 +00:00
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
if (!strcmp("logfile", long_options[oi].name)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->log.logfile = s_strdup(NULL, optarg);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
|
2008-10-08 04:08:52 +00:00
|
|
|
if (!strcmp("pidfile", long_options[oi].name)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
opts->pidfile = s_strdup(NULL, optarg);
|
2008-10-08 04:08:52 +00:00
|
|
|
}
|
2008-11-03 13:09:40 +00:00
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
if (!strcmp("version", long_options[oi].name)) {
|
|
|
|
printf("Version: %s\n", VERSION);
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_SUCCESS);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
// If the config file specified something to do already
|
|
|
|
// dir_conf_n will already be > 0
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts->dir_conf_n == 0) {
|
2008-11-20 21:38:58 +00:00
|
|
|
struct dir_conf * odc; // dir_conf specified by command line options.
|
2008-12-05 22:27:03 +00:00
|
|
|
bool first_target = true;
|
|
|
|
|
|
|
|
if (optind + 2 > argc) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printf("Error: please specify SOURCE and at least one TARGET (see --help)\n");
|
2008-11-20 21:38:58 +00:00
|
|
|
#ifdef XML_CONFIG
|
2010-08-02 13:07:27 +00:00
|
|
|
printf(" or at least one <directory> entry in the conf file.\n");
|
2008-11-20 21:38:58 +00:00
|
|
|
#endif
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(NULL, LSYNCD_BADPARAMETERS);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
odc = new_dir_conf(opts);
|
2008-11-20 21:38:58 +00:00
|
|
|
/* Resolves relative source path, lsyncd might chdir to / later. */
|
2010-08-02 13:07:27 +00:00
|
|
|
odc->source = realdir(NULL, argv[optind]);
|
2008-11-20 21:38:58 +00:00
|
|
|
if (!odc->source) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printf("Error: Source [%s] not found or not a directory.\n", argv[optind]);
|
|
|
|
terminate(NULL, LSYNCD_FILENOTFOUND);
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
2008-12-05 22:27:03 +00:00
|
|
|
for (target = &argv[optind + 1]; *target; target++) {
|
2010-08-02 13:07:27 +00:00
|
|
|
dir_conf_add_target(NULL, odc, *target);
|
2008-12-05 22:27:03 +00:00
|
|
|
if (first_target) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(&opts->log, NORMAL, "command line options: syncing %s -> %s\n",
|
2008-12-05 23:54:13 +00:00
|
|
|
odc->source, *target);
|
2008-12-05 22:27:03 +00:00
|
|
|
first_target = false;
|
|
|
|
} else {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(&opts->log, NORMAL, " and -> %s\n",
|
2008-12-05 23:54:13 +00:00
|
|
|
*target);
|
2008-12-05 22:27:03 +00:00
|
|
|
}
|
|
|
|
}
|
2008-10-06 20:35:52 +00:00
|
|
|
}
|
|
|
|
|
2010-07-27 13:00:32 +00:00
|
|
|
// some sanity checks
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts->default_exclude_file) {
|
|
|
|
check_absolute_path(NULL, opts->default_exclude_file, "Exclude file");
|
|
|
|
check_file_exists (NULL, opts->default_exclude_file, "Exclude file");
|
2008-10-08 04:08:56 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts->pidfile) {
|
|
|
|
check_absolute_path(NULL, opts->pidfile, "Pid file");
|
2008-10-08 04:08:56 +00:00
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts->flag_stubborn && opts->flag_nostartup) {
|
|
|
|
printlogf(&opts->log, NORMAL, "Warning: specifying 'stubborn' when skipping with 'no-startup' has no effect.");
|
2010-07-27 13:00:32 +00:00
|
|
|
}
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the exclude file looking for directory masks to not watch.
|
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
bool
|
|
|
|
parse_exclude_file(struct log *log, char *filename) {
|
2008-08-12 11:51:46 +00:00
|
|
|
FILE * ef;
|
2008-11-03 13:13:29 +00:00
|
|
|
char line[PATH_MAX+1];
|
2008-08-06 13:04:01 +00:00
|
|
|
int sl;
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
ef = fopen(filename, "r");
|
2008-08-06 13:04:01 +00:00
|
|
|
if (ef == NULL) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Cannot open exclude file '%s'\n", filename);
|
|
|
|
terminate(log, LSYNCD_FILENOTFOUND);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
if (!fgets(line, sizeof(line), ef)) {
|
|
|
|
if (feof(ef)) {
|
|
|
|
fclose(ef);
|
|
|
|
return true;
|
|
|
|
}
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Reading file '%s' (%d:%s)\n",
|
2008-11-20 21:38:58 +00:00
|
|
|
filename, errno, strerror(errno));
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(log, LSYNCD_FILENOTFOUND);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
2010-05-20 12:42:58 +00:00
|
|
|
|
2008-08-12 11:51:46 +00:00
|
|
|
sl = strlen(line);
|
|
|
|
|
|
|
|
if (sl == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
if (line[sl - 1] == '\n') {
|
2008-08-12 11:51:46 +00:00
|
|
|
line[sl - 1] = 0;
|
2008-08-06 13:04:01 +00:00
|
|
|
sl--;
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
if (sl == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (line[sl - 1] == '/') {
|
|
|
|
if (exclude_dir_n + 1 >= MAX_EXCLUDES) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR,
|
2008-11-20 21:38:58 +00:00
|
|
|
"Too many directory excludes, can only have %d at the most",
|
|
|
|
MAX_EXCLUDES);
|
2010-08-02 13:07:27 +00:00
|
|
|
terminate(log, LSYNCD_TOOMANYDIRECTORYEXCLUDES);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
line[sl - 1] = 0;
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
sl--;
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
if (sl == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL, "Excluding directories of the name '%s'", line);
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
exclude_dirs[exclude_dir_n] = s_malloc(log, strlen(line) + 1);
|
2008-08-12 11:51:46 +00:00
|
|
|
strcpy(exclude_dirs[exclude_dir_n], line);
|
|
|
|
exclude_dir_n++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2008-11-20 21:38:58 +00:00
|
|
|
/**
|
2010-08-03 13:14:37 +00:00
|
|
|
* Writes a pid file.
|
2008-11-20 21:38:58 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
void
|
|
|
|
write_pidfile(const struct log *log, const char *pidfile) {
|
2008-10-08 04:08:52 +00:00
|
|
|
FILE* f = fopen(pidfile, "w");
|
|
|
|
if (!f) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Error: cannot write pidfile [%s]\n", pidfile);
|
|
|
|
terminate(log, LSYNCD_FILENOTFOUND);
|
2008-10-08 04:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(f, "%i\n", getpid());
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2008-08-06 13:04:01 +00:00
|
|
|
/**
|
2010-08-03 13:14:37 +00:00
|
|
|
* Main.
|
2008-08-06 13:04:01 +00:00
|
|
|
*/
|
2010-08-03 13:14:37 +00:00
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
2008-08-12 11:51:46 +00:00
|
|
|
{
|
2010-08-03 13:14:37 +00:00
|
|
|
struct global_options opts = {{0,}}; // global options
|
2010-08-02 13:07:27 +00:00
|
|
|
struct log *log = &opts.log;
|
2010-08-03 13:14:37 +00:00
|
|
|
int inotify_fd; // inotify file descriptor
|
2010-08-02 13:07:27 +00:00
|
|
|
|
2009-02-28 15:58:06 +00:00
|
|
|
openlog("lsyncd", LOG_CONS | LOG_PID, LOG_DAEMON);
|
2008-11-20 21:38:58 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
reset_options(&opts);
|
|
|
|
parse_options(&opts, argc, argv);
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts.default_exclude_file) {
|
|
|
|
parse_exclude_file(log, opts.default_exclude_file);
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-08-03 13:14:37 +00:00
|
|
|
inotify_fd = inotify_init();
|
|
|
|
if (inotify_fd == -1) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Cannot create inotify instance! (%d:%s)",
|
2008-11-20 21:38:58 +00:00
|
|
|
errno, strerror(errno));
|
2008-12-13 22:14:58 +00:00
|
|
|
return LSYNCD_NOINOTIFY;
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!opts.log.flag_nodaemon) {
|
2008-11-20 21:38:58 +00:00
|
|
|
// this will make this process child of init
|
|
|
|
// close stdin/stdout/stderr and
|
|
|
|
// chdir to /
|
2008-12-13 22:14:58 +00:00
|
|
|
if (daemon(0, 0)) {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, ERROR, "Cannot daemonize! (%d:%s)",
|
2008-12-13 22:14:58 +00:00
|
|
|
errno, strerror(errno));
|
|
|
|
return LSYNCD_INTERNALFAIL;
|
|
|
|
}
|
2008-08-12 11:51:46 +00:00
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL, "Starting up");
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
if (opts.pidfile) {
|
|
|
|
write_pidfile(log, opts.pidfile);
|
2008-10-08 04:08:52 +00:00
|
|
|
}
|
|
|
|
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->size = 2;
|
|
|
|
dir_watches->data = s_calloc(log, dir_watches->size, sizeof(struct dir_watch));
|
2008-08-12 11:51:46 +00:00
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
{
|
|
|
|
// add all watches
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < opts.dir_conf_n; i++) {
|
|
|
|
printlogf(log, NORMAL, "watching %s", opts.dir_confs[i].source);
|
2010-08-03 14:16:12 +00:00
|
|
|
add_dirwatch(&opts, inotify_fd, opts.dir_confs[i].source, -1, &opts.dir_confs[i]);
|
2010-08-02 13:07:27 +00:00
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
}
|
|
|
|
|
2010-08-03 14:16:12 +00:00
|
|
|
// clears tackle FIFO again, because the startup recursive rsync will
|
|
|
|
// handle it eitherway or if started no-startup it has to be ignored.
|
|
|
|
printlogf(log, DEBUG, "dumped list of stuff to do.");
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < tackles->len; i++) {
|
|
|
|
dir_watches->data[i].tackled = false;
|
|
|
|
dir_watches->data[i].alarm = 0;
|
|
|
|
}
|
|
|
|
tackles->len = 0;
|
|
|
|
}
|
2008-11-20 21:38:58 +00:00
|
|
|
|
|
|
|
// startup recursive sync.
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!opts.flag_nostartup) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < opts.dir_conf_n; i++) {
|
2010-07-27 13:00:32 +00:00
|
|
|
char **target;
|
2010-08-02 13:07:27 +00:00
|
|
|
for (target = opts.dir_confs[i].targets; *target; ++target) {
|
2010-08-03 14:16:12 +00:00
|
|
|
printlogf(log, NORMAL, "Initial recursive sync for %s -> %s", opts.dir_confs[i].source, *target);
|
2010-08-02 13:07:27 +00:00
|
|
|
if (!action(&opts, &opts.dir_confs[i], opts.dir_confs[i].source, *target, true)) {
|
2010-08-03 14:16:12 +00:00
|
|
|
printlogf(log, ERROR, "Initial rsync from %s -> %s failed%s",
|
2010-08-02 13:07:27 +00:00
|
|
|
opts.dir_confs[i].source, *target,
|
|
|
|
opts.flag_stubborn ? ", but continuing because being stubborn." : ".");
|
|
|
|
if (!opts.flag_stubborn) {
|
|
|
|
terminate(log, LSYNCD_EXECFAIL);
|
2010-07-27 13:00:32 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-05 22:27:03 +00:00
|
|
|
}
|
2008-11-05 18:55:44 +00:00
|
|
|
}
|
2010-07-27 13:00:32 +00:00
|
|
|
} else {
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL, "Skipped startup since nostartup flag is turned on.");
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|
|
|
|
|
2010-08-02 13:07:27 +00:00
|
|
|
printlogf(log, NORMAL,
|
2008-11-20 21:38:58 +00:00
|
|
|
"--- Entering normal operation with [%d] monitored directories ---",
|
2010-08-03 12:15:37 +00:00
|
|
|
dir_watches->len);
|
2008-08-06 13:04:01 +00:00
|
|
|
|
|
|
|
signal(SIGTERM, catch_alarm);
|
2008-12-13 22:14:58 +00:00
|
|
|
|
2010-08-03 13:14:37 +00:00
|
|
|
master_loop(&opts, inotify_fd);
|
2008-08-12 11:51:46 +00:00
|
|
|
|
|
|
|
return 0;
|
2008-08-06 13:04:01 +00:00
|
|
|
}
|