Commits

Mohammad Ebrahim Mohammadi Panah  committed 9f55ef3

In the name of God
+ Initial release of inorun based on inosync
+ Version set to 0.1.0

  • Participants
  • Parent commits ac8bc7e

Comments (0)

Files changed (6)

-Copyright (c) 2007-2008 Benedikt Böhm <bb@xnull.de>
+Copyright (c) 2007-2008 Benedikt Böhm <bb@xnull.de>,
+Copyright (c) 2011 Mohammad Ebrahim Mohammadi Panah <ebrahim@mohammadi.ir>
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 =======
-inosync
+inorun
 =======
 
-:Author: `Benedikt Böhm <bb@xnull.de>`_
-:Version: 0.2.3
-:Web: http://github.com/hollow/inosync
-:Git: ``git clone https://github.com/hollow/inosync.git``
-:Download: http://github.com/hollow/inosync/downloads
+:Authors: - `Benedikt Böhm <bb@xnull.de>`
+          - `Mohammad Ebrahim Mohammadi Panah <ebrahim@mohammadi.ir>`
+:Version: 0.1.0
+:Web: http://github.com/ebrahim/inorun
+:Git: ``git clone https://github.com/ebrahim/inorun.git``
+:Download: http://github.com/ebrahim/inorun/downloads
 
 Rationale
 =========
 
-System administrators have relied on cron+rsync for years to constantly
-synchronize files and directories to remote machines. However, this technique
-has a huge disadvantage for content distribution with near real-time
-requirements, e.g. podcasts and blogging.
+Wanna run some command on some file system event? inorun is for you!
 
-It is not feasible to let authors wait for their content to get synchronized
-every x hours with regard to the enormous pace of articles and
-podcasts nowadays.
-
-The inosync daemon leverages the inotify service available in recent linux
-kernels to monitor and synchronize changes within directories to remote nodes.
+The inorun daemon leverages the inotify service available in recent linux
+kernels to monitor file system events and run your desired command on them.
 
 
 Usage
 =====
 
+inorun calls your desired commands on your desired events with three arguments:
+
+1. Remote address
+2. Watched path
+3. Relative path to the file under watched path on which the event has occurred
+
+inorun comes with an init script named inorun which runs an inorun daemon using
+the default config file located at ``/etc/inorun/default.py``.
+
 ::
 
-  inosync [OPTIONS]
+  inorun [OPTIONS]
 
     -c FILE     load configuration from FILE
     -d          daemonize (fork to background)
-    -p          do not actually call rsync
+    -p          do not actually call rrun
     -v          print debugging information
     --version   show program's version number and exit
     -h, --help  show this help message and exit
 Configuration
 =============
 
-Configuration files are simple python scripts, merely declaring necessary
-variables. Below is an example configuration to synchronize ``/var/www``
-except ``/var/www/localhost`` to 3 remote locations:
+Remeber to increase maximum allowed user watched if you're going to watch huge
+directory trees:
+
 ::
 
-  # directory that should be watched for changes
-  wpath = "/var/www/"
+    # sysctl -w fs.inotify.max_user_watches=262144
 
-  # exclude list for rsync
-  rexcludes = [
-  	"/localhost",
-  ]
 
-  # common remote path
-  rpath = "/var/www/"
+This is an example config file for syncing an Ubuntu repository to 10.0.0.2:
 
-  # remote locations in rsync syntax
-  rnodes = [
-  	"a.mirror.com:" + rpath,
-  	"b.mirror.com:" + rpath,
-  	"c.mirror.com:" + rpath,
-  ]
+::
 
-  # limit remote sync speed (in KB/s, 0 = no limit)
-  #rspeed = 0
+    # directory that should be watched for changes
+    wpath = "/repo/ubuntu"
 
-  # event mask (only sync on these events)
-  #emask = [
-  #	"IN_CLOSE_WRITE",
-  #	"IN_CREATE",
-  #	"IN_DELETE",
-  #	"IN_MOVED_FROM",
-  #	"IN_MOVED_TO",
-  #]
+    rnodes = [ "10.0.0.2" ]
 
-  # event delay in seconds (this prevents huge
-  # amounts of syncs, but dicreases the 
-  # realtime side of things)
-  #edelay = 10
+    # event mask (only sync on these events)
+    emask = {
+        "IN_CLOSE_WRITE": "/usr/local/bin/sync-copy",
+        "IN_CREATE":      "/usr/local/bin/sync-copy",
+        "IN_MOVED_TO":    "/usr/local/bin/sync-copy",
+        "IN_DELETE":      "/usr/local/bin/sync-remove",
+        "IN_MOVED_FROM":  "/usr/local/bin/sync-remove",
+    }
 
-  # rsync binary path
-  #rsync = "/usr/bin/rsync"
+    # event delay in seconds (prevents huge amounts of syncs, but dicreases the
+    # realtime side of things)
+    # Default: 1
+    edelay = 10
+
+    # initial event to raise virtually on watched directory, maybe to start an
+    # initial full copy
+    #initevent = "IN_CREATE"
 
 
 Bugs
 ====
 
-There are no known bugs currently, however, due to the design of inosync, there
-are several shortcomings:
-
-- inosync cannot parse rsync excludes and therefore calls rsync on changes in
-  excluded directories as well. (`of course rsync still excludes these
-  directories`)
-- It is easily possible to flood the daemon with huge amounts of change events,
-  potentially resulting in enormous bandwidth and connection usage.
+inorun has many bugs, none of which I know at the moment. Report them please.
 
 Requirements
 ============
 Related Software
 ================
 
-inosync is similar to `lsyncd <http://www.pri.univie.ac.at/index.php?c=show&CEWebS_what=Lsyncd>`_,
-but uses a lot less (nasty) magic to parse rsync excludes and shared www
-directories. Additionally inosync has no limitation on filename size and number
-of active watchpoints.
-
-A comparision to other approaches like DRBD, incron and FUSE can be found at
-lsyncds project page, mentioned above.
+inorun is based on `inosync <https://github.com/hollow/inosync>`_.
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides:          inorun
+# Required-Start:    $remote_fs $syslog
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: inorun initscript
+# Description:       Runs programs on file system events.
+### END INIT INFO
+
+# Author: Mohammad Ebrahim Mohammadi Panah <ebrahim at mohammadi dot ir>
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="file system events monitoring and reaction daemon"
+NAME=inorun
+DAEMON=/usr/bin/$NAME
+DAEMON_ARGS="-c /etc/inorun/default.py"
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+	# Return
+	#   0 if daemon has been started
+	#   1 if daemon was already running
+	#   2 if daemon could not be started
+	start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
+		|| return 1
+	start-stop-daemon --start --quiet --background --make-pidfile --pidfile $PIDFILE --exec $DAEMON -- \
+		$DAEMON_ARGS \
+		|| return 2
+	# Add code here, if necessary, that waits for the process to be ready
+	# to handle requests from services started subsequently which depend
+	# on this one.  As a last resort, sleep for some time.
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+	# Return
+	#   0 if daemon has been stopped
+	#   1 if daemon was already stopped
+	#   2 if daemon could not be stopped
+	#   other if a failure occurred
+	start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
+	RETVAL="$?"
+	[ "$RETVAL" = 2 ] && return 2
+	# Wait for children to finish too if this is a daemon that forks
+	# and if the daemon is only ever run from this initscript.
+	# If the above conditions are not satisfied then add some other code
+	# that waits for the process to drop all resources that could be
+	# needed by services started subsequently.  A last resort is to
+	# sleep for some time.
+	start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
+	[ "$?" = 2 ] && return 2
+	# Many daemons don't delete their pidfiles when they exit.
+	rm -f $PIDFILE
+	return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+	#
+	# If the daemon can reload its configuration without
+	# restarting (for example, when it is sent a SIGHUP),
+	# then implement that here.
+	#
+	start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME
+	return 0
+}
+
+case "$1" in
+  start)
+	[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+	do_start
+	case "$?" in
+		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+	esac
+	;;
+  stop)
+	[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+	do_stop
+	case "$?" in
+		0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+		2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+	esac
+	;;
+  status)
+       status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+       ;;
+  #reload|force-reload)
+	#
+	# If do_reload() is not implemented then leave this commented out
+	# and leave 'force-reload' as an alias for 'restart'.
+	#
+	#log_daemon_msg "Reloading $DESC" "$NAME"
+	#do_reload
+	#log_end_msg $?
+	#;;
+  restart|force-reload)
+	#
+	# If the "reload" option is implemented then remove the
+	# 'force-reload' alias
+	#
+	log_daemon_msg "Restarting $DESC" "$NAME"
+	do_stop
+	case "$?" in
+	  0|1)
+		do_start
+		case "$?" in
+			0) log_end_msg 0 ;;
+			1) log_end_msg 1 ;; # Old process is still running
+			*) log_end_msg 1 ;; # Failed to start
+		esac
+		;;
+	  *)
+	  	# Failed to stop
+		log_end_msg 1
+		;;
+	esac
+	;;
+  *)
+	#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+	echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+	exit 3
+	;;
+esac
+
+:
+#!/usr/bin/python
+# vim: set fileencoding=utf-8 ts=2 sw=2 expandtab :
+
+import os,sys
+from optparse import OptionParser,make_option
+from time import sleep
+from syslog import *
+from pyinotify import *
+
+__author__ = "Benedikt Böhm, Mohammad Ebrahim Mohammadi Panah"
+__copyright__ = "Copyright (c) 2007-2008 Benedikt Böhm <bb@xnull.de>, \
+Copyright (c) 2011 Mohammad Ebrahim Mohammadi Panah <ebrahim@mohammadi.ir>"
+__version__ = 0,1,0
+
+OPTION_LIST = [
+  make_option(
+      "-c", dest = "config",
+      default = "/etc/inorun/default.py",
+      metavar = "FILE",
+      help = "load configuration from FILE"),
+  make_option(
+      "-d", dest = "daemonize",
+      action = "store_true",
+      default = False,
+      help = "daemonize inorun"),
+  make_option(
+      "-p", dest = "pretend",
+      action = "store_true",
+      default = False,
+      help = "do not actually run programs"),
+  make_option(
+      "-v", dest = "verbose",
+      action = "store_true",
+      default = False,
+      help = "print debugging information"),
+]
+
+DEFAULT_EVENTS = {
+    "IN_CLOSE_WRITE": "/bin/true",
+    "IN_CREATE":      "/bin/true",
+    "IN_DELETE":      "/bin/true",
+    "IN_MOVED_FROM":  "/bin/true",
+    "IN_MOVED_TO":    "/bin/true"
+}
+
+class RsyncEvent(ProcessEvent):
+  pretend = None
+
+  def __init__(self, pretend=False):
+    self.pretend = pretend
+
+  def sync(self, pathname, maskname):
+    args = [config.emask[maskname]]
+    #if config.logfile:
+    #  args.append("--log-file=%s" % config.logfile)
+    #if "excludes" in dir(config):
+    #  for exclude in config.excludes:
+    #    args.append("--exclude=%s" % exclude)
+    args.append("%s")
+    args.append(config.wpath)
+    args.append(pathname[len(config.wpath)+1:])
+    cmd = " ".join(args)
+    for node in config.rnodes:
+      if self.pretend:
+        syslog("would execute `%s'" % (cmd % node))
+      else:
+        syslog(LOG_DEBUG, "executing %s" % (cmd % node))
+        proc = os.popen(cmd % node)
+        for line in proc:
+          syslog(LOG_DEBUG, "[inorun] %s" % line.strip())
+
+  def process_default(self, event):
+    syslog(LOG_DEBUG, "caught %s on %s" % \
+        (event.maskname, os.path.join(event.path, event.name)))
+    self.sync(event.pathname, event.maskname)
+
+def daemonize():
+  try:
+    pid = os.fork()
+  except OSError, e:
+    raise Exception, "%s [%d]" % (e.strerror, e.errno)
+
+  if (pid == 0):
+    os.setsid()
+    try:
+      pid = os.fork()
+    except OSError, e:
+      raise Exception, "%s [%d]" % (e.strerror, e.errno)
+    if (pid == 0):
+      os.chdir('/')
+      os.umask(0)
+    else:
+      os._exit(0)
+  else:
+    os._exit(0)
+
+  os.open("/dev/null", os.O_RDWR)
+  os.dup2(0, 1)
+  os.dup2(0, 2)
+
+  return 0
+
+def load_config(filename):
+  if not os.path.isfile(filename):
+    raise RuntimeError, "configuration file does not exist: %s" % filename
+
+  configdir  = os.path.dirname(filename)
+  configfile = os.path.basename(filename)
+
+  if configfile.endswith(".py"):
+    configfile = configfile[0:-3]
+
+  sys.path.append(configdir)
+  exec("import %s as __config__" % configfile)
+  sys.path.remove(configdir)
+
+  global config
+  config = __config__
+
+  if not "wpath" in dir(config):
+    raise RuntimeError, "no watch path given"
+  if not os.path.isdir(config.wpath):
+    raise RuntimeError, "watch path does not exist: %s" % config.wpath
+  if not os.path.isabs(config.wpath):
+    config.wpath = os.path.abspath(config.wpath)
+  config.wpath = os.path.normpath(config.wpath)
+
+  if not "rnodes" in dir(config) or len(config.rnodes) < 1:
+    raise RuntimeError, "no remote nodes given"
+
+  if not "emask" in dir(config):
+    config.emask = DEFAULT_EVENTS
+  for event in config.emask:
+    if not event in EventsCodes.ALL_FLAGS.keys():
+      raise RuntimeError, "invalid inotify event: %s" % event
+
+  if not "edelay" in dir(config):
+    config.edelay = 1
+  if config.edelay < 0:
+    raise RuntimeError, "event delay needs to be greater or equal to 0"
+
+  if not "logfile" in dir(config):
+    config.logfile = None
+
+  for program in config.emask.values():
+    if not os.path.isabs(program):
+      raise RuntimeError, "program path needs to be absolute"
+    if not os.path.isfile(program):
+      raise RuntimeError, "program binary does not exist: %s" % program
+
+  if not "initevent" in dir(config):
+    config.initevent = None
+  elif not config.initevent in conf.emask.keys():
+    raise RuntimeError, "invalid inotify event set as initevent: %s" % event
+
+def main():
+  version = ".".join(map(str, __version__))
+  parser = OptionParser(option_list=OPTION_LIST,version="inorun " + version)
+  (options, args) = parser.parse_args()
+
+  if len(args) > 0:
+    parser.error("too many arguments")
+
+  logopt = LOG_PID|LOG_CONS
+  if not options.daemonize:
+    logopt |= LOG_PERROR
+  openlog("inorun", logopt, LOG_DAEMON)
+  if options.verbose:
+    setlogmask(LOG_UPTO(LOG_DEBUG))
+  else:
+    setlogmask(LOG_UPTO(LOG_INFO))
+
+  load_config(options.config)
+
+  if options.daemonize:
+    daemonize()
+
+  wm = WatchManager()
+  ev = RsyncEvent(options.pretend)
+  notifier = AsyncNotifier(wm, ev, read_freq=config.edelay)
+  mask = reduce(lambda x,y: x|y, [EventsCodes.ALL_FLAGS[e] for e in config.emask])
+  wds = wm.add_watch(config.wpath, mask, rec=True, auto_add=True)
+
+  if config.initevent:
+    syslog(LOG_DEBUG, "starting initial synchronization on %s" % config.wpath)
+    ev.sync(config.wpath, config.initevent)
+    syslog(LOG_DEBUG, "initial synchronization on %s done" % config.wpath)
+
+  syslog("resuming normal operations on %s" % config.wpath)
+  try:
+    asyncore.loop()
+  except KeyboardInterrupt:
+    syslog("exiting")
+  sys.exit(0)
+
+if __name__ == "__main__":
+  main()

File inosync.py

-#!/usr/bin/python
-# vim: set fileencoding=utf-8 ts=2 sw=2 expandtab :
-
-import os,sys
-from optparse import OptionParser,make_option
-from time import sleep
-from syslog import *
-from pyinotify import *
-
-__author__ = "Benedikt Böhm"
-__copyright__ = "Copyright (c) 2007-2008 Benedikt Böhm <bb@xnull.de>"
-__version__ = 0,2,3
-
-OPTION_LIST = [
-  make_option(
-      "-c", dest = "config",
-      default = "/etc/inosync/default.py",
-      metavar = "FILE",
-      help = "load configuration from FILE"),
-  make_option(
-      "-d", dest = "daemonize",
-      action = "store_true",
-      default = False,
-      help = "daemonize %prog"),
-  make_option(
-      "-p", dest = "pretend",
-      action = "store_true",
-      default = False,
-      help = "do not actually call rsync"),
-  make_option(
-      "-v", dest = "verbose",
-      action = "store_true",
-      default = False,
-      help = "print debugging information"),
-]
-
-DEFAULT_EVENTS = [
-    "IN_CLOSE_WRITE",
-    "IN_CREATE",
-    "IN_DELETE",
-    "IN_MOVED_FROM",
-    "IN_MOVED_TO"
-]
-
-class RsyncEvent(ProcessEvent):
-  pretend = None
-
-  def __init__(self, pretend=False):
-    self.pretend = pretend
-
-  def sync(self):
-    args = [config.rsync, "-ltrp", "--delete"]
-    args.append("--bwlimit=%s" % config.rspeed)
-    if config.logfile:
-      args.append("--log-file=%s" % config.logfile)
-    if "rexcludes" in dir(config):
-      for rexclude in config.rexcludes:
-        args.append("--exclude=%s" % rexclude)
-    args.append(config.wpath)
-    args.append("%s")
-    cmd = " ".join(args)
-    for node in config.rnodes:
-      if self.pretend:
-        syslog("would execute `%s'" % (cmd % node))
-      else:
-        syslog(LOG_DEBUG, "executing %s" % (cmd % node))
-        proc = os.popen(cmd % node)
-        for line in proc:
-          syslog(LOG_DEBUG, "[rsync] %s" % line.strip())
-
-  def process_default(self, event):
-    syslog(LOG_DEBUG, "caught %s on %s" % \
-        (event.maskname, os.path.join(event.path, event.name)))
-    self.sync()
-
-def daemonize():
-  try:
-    pid = os.fork()
-  except OSError, e:
-    raise Exception, "%s [%d]" % (e.strerror, e.errno)
-
-  if (pid == 0):
-    os.setsid()
-    try:
-      pid = os.fork()
-    except OSError, e:
-      raise Exception, "%s [%d]" % (e.strerror, e.errno)
-    if (pid == 0):
-      os.chdir('/')
-      os.umask(0)
-    else:
-      os._exit(0)
-  else:
-    os._exit(0)
-
-  os.open("/dev/null", os.O_RDWR)
-  os.dup2(0, 1)
-  os.dup2(0, 2)
-
-  return 0
-
-def load_config(filename):
-  if not os.path.isfile(filename):
-    raise RuntimeError, "configuration file does not exist: %s" % filename
-
-  configdir  = os.path.dirname(filename)
-  configfile = os.path.basename(filename)
-
-  if configfile.endswith(".py"):
-    configfile = configfile[0:-3]
-
-  sys.path.append(configdir)
-  exec("import %s as __config__" % configfile)
-  sys.path.remove(configdir)
-
-  global config
-  config = __config__
-
-  if not "wpath" in dir(config):
-    raise RuntimeError, "no watch path given"
-  if not os.path.isdir(config.wpath):
-    raise RuntimeError, "watch path does not exist: %s" % config.wpath
-  if not os.path.isabs(config.wpath):
-    config.wpath = os.path.abspath(config.wpath)
-
-  if not "rnodes" in dir(config) or len(config.rnodes) < 1:
-    raise RuntimeError, "no remote nodes given"
-
-  if not "rspeed" in dir(config) or config.rspeed < 0:
-    config.rspeed = 0
-
-  if not "emask" in dir(config):
-    config.emask = DEFAULT_EVENTS
-  for event in config.emask:
-    if not event in EventsCodes.ALL_FLAGS.keys():
-      raise RuntimeError, "invalid inotify event: %s" % event
-
-  if not "edelay" in dir(config):
-    config.edelay = 10
-  if config.edelay < 0:
-    raise RuntimeError, "event delay needs to be greater or equal to 0"
-
-  if not "logfile" in dir(config):
-    config.logfile = None
-
-  if not "rsync" in dir(config):
-    config.rsync = "/usr/bin/rsync"
-  if not os.path.isabs(config.rsync):
-    raise RuntimeError, "rsync path needs to be absolute"
-  if not os.path.isfile(config.rsync):
-    raise RuntimeError, "rsync binary does not exist: %s" % config.rsync
-
-def main():
-  version = ".".join(map(str, __version__))
-  parser = OptionParser(option_list=OPTION_LIST,version="%prog " + version)
-  (options, args) = parser.parse_args()
-
-  if len(args) > 0:
-    parser.error("too many arguments")
-
-  logopt = LOG_PID|LOG_CONS
-  if not options.daemonize:
-    logopt |= LOG_PERROR
-  openlog("inosync", logopt, LOG_DAEMON)
-  if options.verbose:
-    setlogmask(LOG_UPTO(LOG_DEBUG))
-  else:
-    setlogmask(LOG_UPTO(LOG_INFO))
-
-  load_config(options.config)
-
-  if options.daemonize:
-    daemonize()
-
-  wm = WatchManager()
-  ev = RsyncEvent(options.pretend)
-  notifier = AsyncNotifier(wm, ev, read_freq=config.edelay)
-  mask = reduce(lambda x,y: x|y, [EventsCodes.ALL_FLAGS[e] for e in config.emask])
-  wds = wm.add_watch(config.wpath, mask, rec=True, auto_add=True)
-
-  syslog(LOG_DEBUG, "starting initial synchronization on %s" % config.wpath)
-  ev.sync()
-  syslog(LOG_DEBUG, "initial synchronization on %s done" % config.wpath)
-
-  syslog("resuming normal operations on %s" % config.wpath)
-  asyncore.loop()
-  sys.exit(0)
-
-if __name__ == "__main__":
-  main()

File sample_config.py

 # directory that should be watched for changes
-wpath = "/var/www/"
+wpath = "/repo/ubuntu"
 
+## Not implemented yet:
 # exclude list for rsync
-rexcludes = [
-	"/localhost",
-]
+#rexcludes = [
+#	"/localhost",
+#]
 
-# common remote path
-rpath = "/var/www/"
-
-# remote locations in rsync syntax
-rnodes = [
-	"a.mirror.com:" + rpath,
-	"b.mirror.com:" + rpath,
-	"c.mirror.com:" + rpath,
-]
-
-# limit remote sync speed (in KB/s, 0 = no limit)
-#rspeed = 0
+rnodes = [ "10.0.0.2" ]
 
 # event mask (only sync on these events)
-#emask = [
-#	"IN_CLOSE_WRITE",
-#	"IN_CREATE",
-#	"IN_DELETE",
-#	"IN_MOVED_FROM",
-#	"IN_MOVED_TO",
-#]
+emask = {
+	"IN_CLOSE_WRITE": "/usr/local/bin/sync-copy",
+	"IN_CREATE":      "/usr/local/bin/sync-copy",
+	"IN_MOVED_TO":    "/usr/local/bin/sync-copy",
+	"IN_DELETE":      "/usr/local/bin/sync-remove",
+	"IN_MOVED_FROM":  "/usr/local/bin/sync-remove",
+}
 
 # event delay in seconds (prevents huge amounts of syncs, but dicreases the
 # realtime side of things)
+# Default: 1
 #edelay = 10
 
+## Not implemented yet:
 # rsync log file for updates
-#logfile = /var/log/inosync.log
+#logfile = "/var/log/inorun.log"
 
-# rsync binary path
-#rsync = "/usr/bin/rsync"
+# initial event to raise virtually on watched directory, maybe to start an
+# initial full copy
+#initevent = "IN_CREATE"