Giter Site home page Giter Site logo

gregghz / watcher Goto Github PK

View Code? Open in Web Editor NEW
242.0 19.0 129.0 51 KB

Watcher is a daemon that watches specified files/folders for changes and fires commands in response to those changes. It is similar to incron, however, configuration uses a simpler to read yaml file instead of a plain text file. It's also written in Python, making it easier to hack.

License: MIT License

Python 100.00%

watcher's Introduction

See jobs.yml for proper configuration syntax

Dependencies: python, python-pyinotify, python-yaml

In Ubuntu (and Debian):

sudo apt-get install python python-pyinotify python-yaml

make sure watcher.py is marked as executable

chmod +x watcher.py

start the daemon with:

./watcher.py start

stop it with:

./watcher.py stop

restart it with:

./watcher.py restart

The first time you start it (if you haven't done it yourself) it will create ~/.watcher and ~/.watcher/jobs.yml and then it will yell at you. You need to edit ~/.watcher/jobs.yml to setup folders to watch. You'll find a jobs.yml in the same directory as this README. Use that as an example. It should be pretty simple.

If you edit ~/.watcher/jobs.yml you must restart the daemon for it to reload the configuration file. It'd make sense for me to set up watcher to watch the config file. That'll be coming soon.

Problems? [email protected]

Have fun.

watcher's People

Contributors

anthonytedjamulia avatar benoitvidis avatar gregghz avatar jrubenc avatar splitbrain avatar webjay avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

watcher's Issues

STDErr not captured

Folowing our previous conversation, things printed in stderr are not captured.

watcher.py fails to create pid file on Ubuntu 9.10

watcher.py fails to create /tmp/watcher.pid when './watcher.py start' is executed on Ubuntu 9.10. The log file, /tmp/watcher_out, is created containing a timestamp and the job name, job1. No exceptions are reported and no processes appear to be forked and running when the script exits.

Ubuntu 9.10
Python 2.6.4
Pyinotify 0.8.6
Python YAML 3.09
libyaml 0.1.2 (don't know if this matters)

~/.watcher/jobs.yml (path has been changed for public posting)
job1:
label: Watch /path/to/watch for added or removed files
watch: /path/to/watch
events: ['write_close', 'create']
options: []
recursive: true
command: ls $filename

pyinotify ERROR with watched Directory

We experience this error while moving a directory from one directory to another inside a watched directory:
[2015-02-28 17:14:47,643 pyinotify ERROR] The pathname '/data/folder/New Folder' of this watch <Watch wd=1 path=/data/folder/New Folder mask=2496 proc_fun=None auto_add=False exclude_filter=<pyinotify.ExcludeFilter object at 0x13c3ed0> dir=True > has probably changed and couldn't be updated, so it cannot be trusted anymore. To fix this error move directories/files only between watched parents directories, in this case e.g. put a watch on '/data/folder'.

Events are: events: ['create', 'self_move', 'move']

The folder moves correctly, so no direct problem occurs. Should we accept the message? Some ideas?

AttributeError: 'str' object has no attribute 'iteritems'

Hello,

I would try watcher in Debian Wheezy so when I launch ./watcher.py debug I have this error :
2013-10-23 15:44:29.731466
<open file '/root/.watcher/jobs.yml', mode 'r' at 0x1a598a0>
'str' object has no attribute 'iteritems'
Traceback (most recent call last):
File "./watcher.py", line 407, in
daemon.run()
File "./watcher.py", line 287, in run
for job in jobs.iteritems():
AttributeError: 'str' object has no attribute 'iteritems'

In /root/.watcher/jobs.yml I have :

; ----------------------
; General Settings
; ----------------------
[DEFAULT]

; where to store output
logfile=/var/log/watcher.log

; where to save the PID file
pidfile=/var/run/watcher.pid

; ----------------------
; Job Setups
; ----------------------

[job1]
watch=/home/ftp
events=create
recursive=true
command=/root/ajout_fichier.sh $filename

Thanks for your help
And in

ValueError: No closing quotation

Hi,

Watcher fails with the following log file.

2019-10-16 18:55:25.665159 <open file '/home/will/.watcher/jobs.yml', mode 'r' at 0x7ff62c653f60> job1 Exception in thread Thread-1: Traceback (most recent call last): File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner self.run() File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 1505, in run self.loop() File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 1491, in loop self.process_events() File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 1287, in process_events self._default_proc_fun(revent) File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 924, in __call__ return _ProcessEvent.__call__(self, event) File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 644, in __call__ return meth(event) File "/home/will/Watcher/watcher.py", line 268, in process_IN_MOVED_TO self.runCommand(event, False) File "/home/will/Watcher/watcher.py", line 212, in runCommand subprocess.call(shlex.split(command)) File "/usr/lib/python2.7/shlex.py", line 279, in split return list(lex) File "/usr/lib/python2.7/shlex.py", line 269, in next token = self.get_token() File "/usr/lib/python2.7/shlex.py", line 96, in get_token raw = self.read_token() File "/usr/lib/python2.7/shlex.py", line 172, in read_token raise ValueError, "No closing quotation" ValueError: No closing quotation Moved to: /mnt/IcyBox/Videos/RecordedTVShows/World's Deadliest Drivers (2019)/World's Deadliest Drivers (2019) - 2019-10-16 17 30 00 - World's Deadliest Drivers.ts

The jobs.yml file is as shown below:

job1: label: Rename Postprocessed Recorded TV Shows watch: /mnt/IcyBox/Videos/RecordedTVShows exclude: ['/mnt/IcyBox/Videos/RecordedTVShows/.grab', '/mnt/IcyBox/Videos/RecordedTVShows/.stignore'] events: ['create', 'move_to'] options: [] recursive: true command: echo $filename

As you can see All I was trying to do was echo the filename. I did this because the script I was running kept failing so I switched to echo as a debug.
Anything you can do to help with this would be appreicated. Is my jobs.yml incorrect or is this a bug in watcher?

$dest_file no longer works as expected

Hi,

I have noticed that the most recent version/commit of this daemon has an issue with the $dest_file in the configuration file, or at the very least, the behaviour has changed.

The $dest_file placeholder used to represent the file name picked up by an event, just the file name, not the file name and the entire path. Now it behaves in a similar way to $filename itself, which I'm not sure is intended.

I just wanted to raise this with you in case it's not meant to have changed.

Thanks for an awesome tool! :)

Working Python3 version :)

#!/usr/bin/env python3
# Copyright (c) 2010 Greggory Hernandez

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

### BEGIN INIT INFO
# Provides:          watcher.py
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Monitor directories for file changes
# Description:       Monitor directories specified in /etc/watcher.ini for
#                    changes using the Kernel's inotify mechanism and run
#                    jobs when files or directories change
### END INIT INFO

import sys, os, time, atexit
from signal import SIGTERM
import pyinotify
import sys, os
import datetime
import subprocess
from types import *
from string import Template
import configparser
import argparse

class Daemon:
    """
    A generic daemon class

    Usage: subclass the Daemon class and override the run method
    """
    def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null'):
        self.stdin = stdin
        self.stdout = stdout
        self.stderr = stderr
        self.pidfile = pidfile

    def daemonize(self):
        """
        do the UNIX double-fork magic, see Stevens' "Advanced Programming in the
        UNIX Environment" for details (ISBN 0201563177)
        http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
        """
        try:
            pid = os.fork()
            if pid > 0:
                #exit first parent
                sys.exit(0)
        except OSError as e:
            sys.stderr.write("fork #1 failed: %d (%s)\n" % (e.errno, e.strerror))
            sys.exit(1)

        # decouple from parent environment
        os.chdir("/")
        os.setsid()
        os.umask(0)

        # do second fork
        try:
            pid = os.fork()
            if pid > 0:
                # exit from second parent
                sys.exit(0)
        except OSError as e:
            sys.stderr.write("fork #2 failed: %d (%s)\n" % (e.errno, e.strerror))
            sys.exit(1)

        #redirect standard file descriptors
        sys.stdout.flush()
        sys.stderr.flush()
        si = open(self.stdin, 'r')
        so = open(self.stdout, 'wb')
        se = open(self.stderr, 'wb', 0)
        os.dup2(si.fileno(), sys.stdin.fileno())
        os.dup2(so.fileno(), sys.stdout.fileno())
        os.dup2(se.fileno(), sys.stderr.fileno())

        #write pid file
        atexit.register(self.delpid)
        pid = str(os.getpid())
        open(self.pidfile, 'w+').write("%s\n" % pid)

    def delpid(self):
        os.remove(self.pidfile)

    def start(self):
        """
        Start the daemon
        """
        # Check for a pidfile to see if the daemon already runs
        try:
            pf = open(self.pidfile, 'r')
            pid = int(pf.read().strip())
            pf.close()
        except IOError:
            pid = None

        if pid:
            message = "pidfile %s already exists. Daemon already running?\n"
            sys.stderr.write(message % self.pidfile)
            sys.exit(1)

        # Start the Daemon
        self.daemonize()
        self.run()

    def stop(self):
        """
        Stop the daemon
        """
        # get the pid from the pidfile
        try:
            pf = open(self.pidfile, 'r')
            pid = int(pf.read().strip())
            pf.close()
        except IOError:
            pid = None

        if not pid:
            message = "pidfile %s does not exist. Daemon not running?\n"
            sys.stderr.write(message % self.pidfile)
            return # not an error in a restart

        # Try killing the daemon process
        try:
            while 1:
                os.kill(pid, SIGTERM)
                time.sleep(0.1)
        except OSError as err:
            err = str(err)
            if err.find("No such process") > 0:
                if os.path.exists(self.pidfile):
                    os.remove(self.pidfile)
            else:
                print(str(err))
                sys.exit(1)

    def restart(self):
        """
        Restart the daemon
        """
        self.stop()
        self.start()

    def status(self):
        try:
            pf = open(self.pidfile, 'r')
            pid = int(pf.read().strip())
            pf.close()
        except IOError:
            pid = None
            
        if pid:
            print("service running")
            sys.exit(0)
        if not pid:
            print("service not running")
            sys.exit(3)

    def run(self):
        """
        You should override this method when you subclass Daemon. It will be called after the process has been
        daemonized by start() or restart().
        """

class EventHandler(pyinotify.ProcessEvent):
    def __init__(self, command):
        pyinotify.ProcessEvent.__init__(self)
        self.command = command

    # from http://stackoverflow.com/questions/35817/how-to-escape-os-system-calls-in-python
    def shellquote(self,s):
        s = str(s)
        return "'" + s.replace("'", "'\\''") + "'"

    def runCommand(self, event):
        t = Template(self.command)
        command = t.substitute(watched=self.shellquote(event.path),
                               filename=self.shellquote(event.pathname),
                               tflags=self.shellquote(event.maskname),
                               nflags=self.shellquote(event.mask),
                               cookie=self.shellquote(event.cookie if hasattr(event, "cookie") else 0))
        try:
            os.system(command)
        except OSError as err:
            print("Failed to run command '%s' %s" % (command, str(err)))

    def process_IN_ACCESS(self, event):
        print("Access: ", event.pathname)
        self.runCommand(event)

    def process_IN_ATTRIB(self, event):
        print("Attrib: ", event.pathname)
        self.runCommand(event)

    def process_IN_CLOSE_WRITE(self, event):
        print("Close write: ", event.pathname)
        self.runCommand(event)

    def process_IN_CLOSE_NOWRITE(self, event):
        print("Close nowrite: ", event.pathname)
        self.runCommand(event)

    def process_IN_CREATE(self, event):
        print("Creating: ", event.pathname)
        self.runCommand(event)

    def process_IN_DELETE(self, event):
        print("Deleteing: ", event.pathname)
        self.runCommand(event)

    def process_IN_MODIFY(self, event):
        print("Modify: ", event.pathname)
        self.runCommand(event)

    def process_IN_MOVE_SELF(self, event):
        print("Move self: ", event.pathname)
        self.runCommand(event)

    def process_IN_MOVED_FROM(self, event):
        print("Moved from: ", event.pathname)
        self.runCommand(event)

    def process_IN_MOVED_TO(self, event):
        print("Moved to: ", event.pathname)
        self.runCommand(event)

    def process_IN_OPEN(self, event):
        print("Opened: ", event.pathname)
        self.runCommand(event)

class WatcherDaemon(Daemon):

    def __init__(self, config):
        self.stdin   = '/dev/null'
        self.stdout  = config.get('DEFAULT','logfile')
        self.stderr  = config.get('DEFAULT','logfile')
        self.pidfile = config.get('DEFAULT','pidfile')
        self.config  = config

    def run(self):
        log('Daemon started')
        wdds      = []
        notifiers = []

        # read jobs from config file
        for section in self.config.sections():
            log(section + ": " + self.config.get(section,'watch'))
            # get the basic config info
            mask      = self._parseMask(self.config.get(section,'events').split(','))
            folder    = self.config.get(section,'watch')
            recursive = self.config.getboolean(section,'recursive')
            autoadd   = self.config.getboolean(section,'autoadd')
            excluded  = self.config.get(section,'excluded')
            command   = self.config.get(section,'command')

            # Exclude directories right away if 'excluded' regexp is set
            # Example https://github.com/seb-m/pyinotify/blob/master/python2/examples/exclude.py
            if excluded.strip() == '':   # if 'excluded' is empty or whitespaces only
                excl = None
            else:
                excl = pyinotify.ExcludeFilter(excluded.split(','))

            wm = pyinotify.WatchManager()
            handler = EventHandler(command)

            wdds.append(wm.add_watch(folder, mask, rec=recursive, auto_add=autoadd, exclude_filter=excl))

            # BUT we need a new ThreadNotifier so I can specify a different
            # EventHandler instance for each job
            # this means that each job has its own thread as well (I think)
            notifiers.append(pyinotify.ThreadedNotifier(wm, handler))

        # now we need to start ALL the notifiers.
        # TODO: load test this ... is having a thread for each a problem?
        for notifier in notifiers:
            notifier.start()


    def _parseMask(self, masks):
        ret = False;

        for mask in masks:
            mask = mask.strip()

            if 'access' == mask:
                ret = self._addMask(pyinotify.IN_ACCESS, ret)
            elif 'attribute_change' == mask:
                ret = self._addMask(pyinotify.IN_ATTRIB, ret)
            elif 'write_close' == mask:
                ret = self._addMask(pyinotify.IN_CLOSE_WRITE, ret)
            elif 'nowrite_close' == mask:
                ret = self._addMask(pyinotify.IN_CLOSE_NOWRITE, ret)
            elif 'create' == mask:
                ret = self._addMask(pyinotify.IN_CREATE, ret)
            elif 'delete' == mask:
                ret = self._addMask(pyinotify.IN_DELETE, ret)
            elif 'self_delete' == mask:
                ret = self._addMask(pyinotify.IN_DELETE_SELF, ret)
            elif 'modify' == mask:
                ret = self._addMask(pyinotify.IN_MODIFY, ret)
            elif 'self_move' == mask:
                ret = self._addMask(pyinotify.IN_MOVE_SELF, ret)
            elif 'move_from' == mask:
                ret = self._addMask(pyinotify.IN_MOVED_FROM, ret)
            elif 'move_to' == mask:
                ret = self._addMask(pyinotify.IN_MOVED_TO, ret)
            elif 'open' == mask:
                ret = self._addMask(pyinotify.IN_OPEN, ret)
            elif 'all' == mask:
                m = pyinotify.IN_ACCESS | pyinotify.IN_ATTRIB | pyinotify.IN_CLOSE_WRITE | \
                    pyinotify.IN_CLOSE_NOWRITE | pyinotify.IN_CREATE | pyinotify.IN_DELETE | \
                    pyinotify.IN_DELETE_SELF | pyinotify.IN_MODIFY | pyinotify.IN_MOVE_SELF | \
                    pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO | pyinotify.IN_OPEN
                ret = self._addMask(m, ret)
            elif 'move' == mask:
                ret = self._addMask(pyinotify.IN_MOVED_FROM | pyinotify.IN_MOVED_TO, ret)
            elif 'close' == mask:
                ret = self._addMask(pyinotify.IN_CLOSE_WRITE | pyinotify.IN_CLOSE_NOWRITE, ret)

        return ret

    def _addMask(self, new_option, current_options):
        if not current_options:
            return new_option
        else:
            return current_options | new_option



def log(msg):
    sys.stdout.write("%s %s\n" % ( str(datetime.datetime.now()), msg ))


if __name__ == "__main__":
    # Parse commandline arguments
    parser = argparse.ArgumentParser(
                description='A daemon to monitor changes within specified directories and run commands on these changes.',
             )
    parser.add_argument('-c','--config',
                        action='store',
                        help='Path to the config file (default: %(default)s)')
    parser.add_argument('command',
                        action='store',
                        choices=['start','stop','restart','status','debug'],
                        help='What to do. Use debug to start in the foreground')
    args = parser.parse_args()

    # Parse the config file
    config = configparser.ConfigParser()
    if(args.config):
        confok = config.read(args.config)
    else:
        confok = config.read(['/etc/watcher.ini', os.path.expanduser('~/.watcher.ini')]);

    if(not confok):
        sys.stderr.write("Failed to read config file. Try -c parameter\n")
        sys.exit(4);

    # Initialize the daemon
    daemon = WatcherDaemon(config)

    # Execute the command
    if 'start' == args.command:
        daemon.start()
    elif 'stop' == args.command:
        daemon.stop()
    elif 'restart' == args.command:
        daemon.restart()
    elif 'status' == args.command:
        daemon.status()
    elif 'debug' == args.command:
        daemon.run()
    else:
        print("Unkown Command")
        sys.exit(2)
    sys.exit(0)

Notifications for .swpx, .swp, .lck files. Also, how to monitor a directory and its sub directories only (not files)?

First of all, a great script. Very useful. Thanks for your work on this.

Everything is working as expected for me. The watcher notifies me about file deletions alright. But, it tends to notify me about .swp, .swpx (vim temp files) and .lck files, is there a way to exclude the notifications for these temp files. Also, while I am researching into it myself, was anyone able to monitor a directory and its sub directories only (not files) using the script?

For e.g.,

2015-02-13 20:11:23 /abc/xyz/.test.swpx was deleted
2015-02-13 20:11:24 /abc/xyz/.test.swp was deleted
2015-02-13 20:12:50 /abc/xyz/.test1234.swpx was deleted
2015-02-13 20:12:50 /abc/xyz/.test1234.swp was deleted
2015-02-13 20:12:56 /abc/xyz/.test1234.swp was deleted
2015-02-14 22:38:23 /abc/xyz/.stfs.lck was deleted
2015-02-14 22:38:28 /abc/xyz/xyz.pdf was deleted
2015-02-14 22:40:37 /abc/xyz/xyz.pdf was deleted

Feature: add exclude/include matching to files/dirs

When using recursive mode i bump into a problem that could be solved if you have a include/exclude matching logic

scenario:
i have /repo for my yum repo
whenever i add a file i want it to run createrepo -update $watched

downside, it creates a .repodata when running and then a repodata dir when done.
then watcher will run createrepo on those dirs and we end up in a never ending loop.
by adding some type of exclude: ['repodata', '.repodata'] or perhaps include: ['.rpm', '.xml']
we can make watcher to only look at desired files in a dir and act on that, and/or ignore events in exclude.

would be great if a feature like this could be implemented because i need it really bad.

Only one job running.

When I define more than one job in jobs.yml - only the last defined of them actually runs.

If I tail -f on /tmp/watcher_out and restart the watcher process I get:

Traceback (most recent call last):
File "./watcher.py", line 369, in
daemon.restart()
File "./watcher.py", line 154, in restart
self.start()
File "./watcher.py", line 116, in start
self.run()
File "./watcher.py", line 268, in run
self.addWatch(mask, folder, recursive, command)
File "./watcher.py", line 276, in addWatch
wm = pyinotify.WatchManager()
File "/usr/lib/python2.7/dist-packages/pyinotify.py", line 1593, in init
raise OSError(err)
OSError: Cannot initialize new instance of inotify Errno=Too many open files (EMFILE)

$datetime needed, trying to implement but if someone has the solution...

Hi all,

I need to be able to ask for a $datetime output in the jobs commands.
I'm trying to implement it.
If someone has an idea on how to do it the best way, it would be nice to share, I would then implement it that way, making tests and share the code again.

Ok found it...will push my code soon, just looking at the more relevant way to export date/time, using system time or tzdata.

Recursive endless loops when...

(Not really an issue, but...)

Problem: Need to watch for attribute_change and when event occours, I need to change attributes. This logically ends up in an endless event/react loop.

So we need something (a switch ect) that detects when a watcher-command changes watched events.

Thanks in advance

ModuleNotFoundError: No module named 'pyinotify'

I'm always getting this error on Mac OS and Py3.
Can you please me out to rectify this ? i did my reasech but no luck

Traceback (most recent call last):
  File "watcher.py", line 24, in <module>
    import pyinotify
ModuleNotFoundError: No module named 'pyinotify'

Running watcher.py returns SyntaxError on Python 3.7.3

On running ./watcher.py start, python is returning the following SyntaxError:

  File "./watcher.py", line 62
    except OSError, e:
                  ^
SyntaxError: invalid syntax

Python version: 3.7.3
python-pyinotify version: 0.9.6
python-yaml version: 5.1

Watcher - No output information as expected

Hi all,

I'm trying to get working your watcher's script on my xubuntu. I've moved the file watcher.py to /bin/watcher as root so these are its properties:

1574797 -rwxr-xr-x 1 root root 14469 feb 5 21:55 watcher.py

I run the script with my local user (david) and it creates the pid file on ~/.watcher:

1970263 -rw-rw-r-- 1 david david 99 feb 5 22:08 watcher.log
1970265 -rw-rw-rw- 1 david david 4525 feb 5 22:03 jobs.yml
1970264 -rw-rw-rw- 1 david david 6 feb 5 22:03 watcher.pid

jobs.yml is the current example file that you uploaded to github. I've also checked that the daemon is running fine. This is the ps -aux | grep watcher output:

david 20901 0.0 0.2 133984 10628 ? Sl 22:03 0:00 python ./watcher.py start
david 21180 0.0 0.0 18952 916 pts/0 S+ 22:24 0:00 grep --color=auto watcher

I made some tests in /var/www and checked watcher.log:

2015-02-05 22:08:22 /var/www/test.lala IN_DELETE
2015-02-05 22:08:41 /var/www/piopio.txt IN_CREATE

The script seems to be watching all the changes however I can't see the echo output defined:

command: echo $datetime $filename $tflags # $src_path

I don't see anything echoed. I've also tried to run the script as root but I can't see any information on the screen. However, watcher.log is logging the events

Am I doing something wrong?
Thank you in advance!

OSError: Cannot initialize new instance of inotify, Errno=Too many open files (EMFILE)

Hello!

Have got such multiple trace in server's log:

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib64/python2.7/threading.py", line 812, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/site-packages/pyinotify.py", line 1511, in run
    self.loop()
  File "/usr/lib/python2.7/site-packages/pyinotify.py", line 1497, in loop
    self.process_events()
  File "/usr/lib/python2.7/site-packages/pyinotify.py", line 1295, in process_events
    self._default_proc_fun(revent)
  File "/usr/lib/python2.7/site-packages/pyinotify.py", line 937, in __call__
    return _ProcessEvent.__call__(self, event)
  File "/usr/lib/python2.7/site-packages/pyinotify.py", line 662, in __call__
    return meth(event)
  File "/root/Watcher/watcher.py", line 247, in process_IN_CREATE
    self.runCommand(event)
  File "/root/Watcher/watcher.py", line 227, in runCommand
    prefix)
  File "/root/Watcher/watcher.py", line 300, in addWatch
    wm = pyinotify.WatchManager()
  File "/usr/lib/python2.7/site-packages/pyinotify.py", line 1747, in __init__
    raise OSError(err % self._inotify_wrapper.str_errno())
OSError: Cannot initialize new instance of inotify, Errno=Too many open files (EMFILE)

Need some hint on further investigation here. Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.