diff --git a/edbob/commands.py b/edbob/commands.py
index 9d19cc1..f9a3701 100644
--- a/edbob/commands.py
+++ b/edbob/commands.py
@@ -386,12 +386,15 @@ class DatabaseCommand(Subcommand):
class FileMonitorCommand(Subcommand):
"""
- Interacts with the file monitor Windows service; called as ``edbob
- filemon``. This command expects a subcommand; one of the following:
+ Interacts with the file monitor service; called as ``edbob filemon``. This
+ command expects a subcommand; one of the following:
- * ``edbob filemon install``
* ``edbob filemon start``
* ``edbob filemon stop``
+
+ On Windows platforms, the following additional subcommands are available:
+
+ * ``edbob filemon install``
* ``edbob filemon uninstall``
.. note::
@@ -399,28 +402,46 @@ class FileMonitorCommand(Subcommand):
``cmd.exe`` as an Administrator in order to have sufficient rights to
run the above commands.
+ .. todo::
+ Verify the previous statement... (Maybe test with/out UAC?)
+
See :doc:`howto.use_filemon` for more information.
"""
name = 'filemon'
- description = "Manage the file monitor service on Windows"
+ description = "Manage the file monitor service"
def add_parser_args(self, parser):
subparsers = parser.add_subparsers(title='subcommands')
- install = subparsers.add_parser('install',
- help="Install (register) service")
- install.set_defaults(subcommand='install')
- uninstall = subparsers.add_parser('uninstall',
- help="Uninstall (unregister) service")
- uninstall.set_defaults(subcommand='remove')
+
start = subparsers.add_parser('start', help="Start service")
start.set_defaults(subcommand='start')
stop = subparsers.add_parser('stop', help="Stop service")
stop.set_defaults(subcommand='stop')
+ if sys.platform == 'win32':
+ install = subparsers.add_parser('install',
+ help="Install (register) service")
+ install.set_defaults(subcommand='install')
+ uninstall = subparsers.add_parser('uninstall',
+ help="Uninstall (unregister) service")
+ uninstall.set_defaults(subcommand='remove')
+
def run(self, args):
- from edbob.filemon import exec_server_command
- exec_server_command(args.subcommand)
+ if sys.platform == 'linux2':
+ if args.subcommand == 'start':
+ from edbob.filemon.linux import start_daemon
+ start_daemon()
+ elif args.subcommand == 'stop':
+ from edbob.filemon.linux import stop_daemon
+ stop_daemon()
+
+ elif sys.platform == 'win32':
+ from edbob.filemon.win32 import exec_server_command
+ exec_server_command(args.subcommand)
+
+ else:
+ print "Sorry, file monitor is not supported on platform %s." % sys.platform
class ShellCommand(Subcommand):
diff --git a/edbob/filemon/__init__.py b/edbob/filemon/__init__.py
new file mode 100644
index 0000000..c3293b9
--- /dev/null
+++ b/edbob/filemon/__init__.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+################################################################################
+#
+# edbob -- Pythonic Software Framework
+# Copyright © 2010-2012 Lance Edgar
+#
+# This file is part of edbob.
+#
+# edbob is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with edbob. If not, see .
+#
+################################################################################
+
+"""
+``edbob.filemon`` -- File Monitoring Service
+"""
+
+import edbob
+from edbob.exceptions import ConfigError
+
+
+class MonitorProfile(object):
+ """
+ This is a simple profile class, used to represent configuration of the file
+ monitor service.
+ """
+
+ def __init__(self, key):
+ self.key = key
+ self.dirs = eval(edbob.config.require('edbob.filemon', '%s.dirs' % key))
+ if not self.dirs:
+ raise ConfigError('edbob.filemon', '%s.dirs' % key)
+ self.actions = eval(edbob.config.require('edbob.filemon', '%s.actions' % key))
+ if not self.actions:
+ raise ConfigError('edbob.filemon', '%s.actions' % key)
diff --git a/edbob/filemon/linux.py b/edbob/filemon/linux.py
new file mode 100644
index 0000000..4715a58
--- /dev/null
+++ b/edbob/filemon/linux.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+################################################################################
+#
+# edbob -- Pythonic Software Framework
+# Copyright © 2010-2012 Lance Edgar
+#
+# This file is part of edbob.
+#
+# edbob is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Affero General Public License as published by the Free
+# Software Foundation, either version 3 of the License, or (at your option)
+# any later version.
+#
+# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with edbob. If not, see .
+#
+################################################################################
+
+"""
+``edbob.filemon.linux`` -- File Monitor for Linux
+"""
+
+import sys
+import os
+import os.path
+import signal
+import pyinotify
+
+import edbob
+from edbob.filemon import MonitorProfile
+
+
+class EventHandler(pyinotify.ProcessEvent):
+ """
+ Event processor for file monitor daemon.
+ """
+
+ def my_init(self, actions=[], **kwargs):
+ self.actions = actions
+
+ def process_IN_CREATE(self, event):
+ self.perform_actions(event.pathname)
+
+ def process_IN_MOVED_TO(self, event):
+ self.perform_actions(event.pathname)
+
+ def perform_actions(self, path):
+ for action in self.actions:
+ if isinstance(action, tuple):
+ func = action[0]
+ args = action[1:]
+ else:
+ func = action
+ args = []
+ func = edbob.load_spec(func)
+ func(path, *args)
+
+
+def get_pid_path():
+ """
+ Returns the path to the PID file for the file monitor daemon.
+ """
+
+ basename = os.path.basename(sys.argv[0])
+ return '/tmp/%s_filemon.pid' % basename
+
+
+def start_daemon():
+ """
+ Starts the file monitor daemon.
+ """
+
+ pid_path = get_pid_path()
+ if os.path.exists(pid_path):
+ print "File monitor is already running"
+ return
+
+ wm = pyinotify.WatchManager()
+ notifier = pyinotify.Notifier(wm)
+
+ monitored = {}
+ keys = edbob.config.require('edbob.filemon', 'monitored')
+ keys = keys.split(',')
+ for key in keys:
+ key = key.strip()
+ monitored[key] = MonitorProfile(key)
+
+ mask = pyinotify.IN_CREATE | pyinotify.IN_MOVED_TO
+ for profile in monitored.itervalues():
+ for path in profile.dirs:
+ wm.add_watch(path, mask, proc_fun=EventHandler(actions=profile.actions))
+
+ notifier.loop(daemonize=True, pid_file=pid_path)
+
+
+def stop_daemon():
+ """
+ Stops the file monitor daemon.
+ """
+
+ pid_path = get_pid_path()
+ if not os.path.exists(pid_path):
+ print "File monitor is not running"
+ return
+
+ f = open(pid_path)
+ pid = f.read().strip()
+ f.close()
+ if not pid.isdigit():
+ print "Hm, found bogus PID:", pid
+ return
+
+ os.kill(int(pid), signal.SIGKILL)
+ os.remove(pid_path)
diff --git a/edbob/filemon.py b/edbob/filemon/win32.py
similarity index 89%
rename from edbob/filemon.py
rename to edbob/filemon/win32.py
index 38f1d1c..0b063b3 100644
--- a/edbob/filemon.py
+++ b/edbob/filemon/win32.py
@@ -23,7 +23,7 @@
################################################################################
"""
-``edbob.filemon`` -- File Monitoring Service
+``edbob.filemon.win32`` -- File Monitor for Windows
"""
# Much of the Windows monitoring code below was borrowed from Tim Golden:
@@ -67,22 +67,6 @@ def exec_server_command(command):
subprocess.call([sys.executable, server_path, command])
-class MonitorProfile(object):
- """
- This is a simple profile class, used to represent configuration of the file
- monitor service.
- """
-
- def __init__(self, key):
- self.key = key
- self.dirs = eval(edbob.config.require('edbob.filemon', '%s.dirs' % key))
- if not self.dirs:
- raise ConfigError('edbob.filemon', '%s.dirs' % key)
- self.actions = eval(edbob.config.require('edbob.filemon', '%s.actions' % key))
- if not self.actions:
- raise ConfigError('edbob.filemon', '%s.actions' % key)
-
-
def monitor_win32(path, include_subdirs=False):
"""
This is the workhorse of file monitoring on the Windows platform. It is a
diff --git a/edbob/filemon_server.py b/edbob/filemon/win32_server.py
similarity index 97%
rename from edbob/filemon_server.py
rename to edbob/filemon/win32_server.py
index 8071a51..8e5c150 100644
--- a/edbob/filemon_server.py
+++ b/edbob/filemon/win32_server.py
@@ -32,9 +32,9 @@ import time
import Queue
import edbob
+from edbob.filemon import MonitorProfile
+from edbob.filemon.win32 import WatcherWin32, ACTION_CREATE, ACTION_UPDATE
from edbob.win32 import file_is_free
-from edbob.filemon import (MonitorProfile, WatcherWin32,
- ACTION_CREATE, ACTION_UPDATE)
import sys
if sys.platform == 'win32': # docs should build for everyone