├── .gitignore ├── LICENSE ├── README.md ├── requirements.txt ├── setup.py ├── watchman ├── __init__.py ├── cli │ ├── __init__.py │ └── cli.py ├── conf.py ├── configurations.py ├── extension.py ├── pigeon.py ├── watch.py └── worker.py ├── watchman_sync └── watchman_worker /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_STORE 2 | *.pid 3 | *.pyc 4 | *.log 5 | dump.rdb 6 | venv 7 | watchman.egg-info 8 | build 9 | dist 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, SKCRIPT 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Watchman 2 | Ping file system events to any API 3 | 4 | * [Dependencies](#dependencies) 5 | * [Installing](#installing) 6 | * [Running Watchman](#running-watchman) 7 | * [Functions](#functions) 8 | * [YAML Configuration](#yaml-configuration) 9 | * [Extending](#extending) 10 | * [Running Watchman as a Service](#running-watchman-as-a-service) 11 | * [Logs](#logs) 12 | * [License](#license) 13 | 14 | ## Dependencies 15 | * Python 2.7 (Developed and Tested) 16 | * [Redis](http://redis.io/) 17 | * [RQ](http://python-rq.org) 18 | 19 | ## Installing 20 | 1. Download this Repo 21 | 2. Run `python setup.py install` 22 | 3. Configure the app by running `watchman configure`. Or copy the [default YAML file](#yaml-config) to `~`. 23 | 24 | ## Running Watchman 25 | 1. From Terminal, run `watchman sync` 26 | 2. From another Terminal, run `watchman worker` 27 | 28 | ## Functions 29 | * `watchman sync`: Watches over all paths added to `source` in [YAML configuration file](#yaml-config) 30 | * `watchman worker`: Starts the RQ worker to ping all endpoints added to `endpoints` in [YAML configuration file](#yaml-config) 31 | 32 | ## YAML Configuration 33 | This YAML is automatically created in the `~` directory. It holds all the configuration attributes for Watchman. 34 | 35 | ### Attributes 36 | * `source`: Array of all paths Watchman should monitor 37 | * `regexes`: Array of all regexes Watchman should abide by (These regexes are matched with the filepath) 38 | * `endpoints`: Hash of each file/folder action which should ping to an API endpoint 39 | 40 | ### Default File 41 | ``` 42 | source: 43 | - /home/users/skcript 44 | regexes: 45 | - "([a-zA-Z0-9_ -/]*)/home/(\\w+)/uploads/" 46 | endpoints: 47 | file_create: "http://localhost/api/v2/files/create" 48 | folder_create: "http://localhost/api/v2/folders/create" 49 | file_move: "http://localhost/api/v2/files/move" 50 | folder_move: "http://localhost/api/v2/folders/move" 51 | file_destroy: "http://localhost/api/v2/files/destroy" 52 | folder_destroy: "http://localhost/api/v2/folders/destroy" 53 | ``` 54 | 55 | ## Extending 56 | Watchman can ratelimit and selectively prevent your API calls for each file 57 | system event. These are managed in `watchman/extension.py`. Each function defined 58 | in `extension.py` takes path (where the file system event occurred) as input. 59 | 60 | ### Ratelimiting 61 | Watchman can ratelimit your API calls based on a unique string at 100 calls 62 | per second. 63 | 64 | The unique string is used to group your API calls. Modify the `ratelimit()` 65 | function in `watchman/extension.py` to enable this. By default it returns the 66 | root directory of the path Watchman is watching. 67 | 68 | For example, if you are monitoring `/uploads` folder which has directories for 69 | each user, like, 70 | - `/uploads/user1` 71 | - `/uploads/user2` 72 | - `/uploads/user3` 73 | 74 | Modify `ratelimit()` to return the username from the path. This way you can 75 | ratelimit API calls based on each user. 76 | 77 | ### Preventing 78 | Watchman can selectively prevent API calls from being triggered by simply 79 | returning a boolean for each path. Modify the `prevent()` function in 80 | `watchman/extension.py` to enable this. 81 | 82 | By default `prevent()` returns `False` and does not prevent any API calls. 83 | 84 | ## Running Watchman as a Service 85 | Watchman can be run as a service in your production environments. Currently, 86 | only Linux environments are supported. 87 | 88 | 1. Download `watchman_sync` and `watchman_worker` to your in `/etc/init.d` folder 89 | 2. Create folder `/var/run/watchman` to store pids file 90 | 3. Create a `/tmp` if it is not present already 91 | 4. Give appropriate permissions to `/var/run/watchman` and `/tmp` (to whichever user Watchman is running from) 92 | 93 | **To start the service** 94 | ``` 95 | service watchman_sync start 96 | service watchman_worker start 97 | ``` 98 | 99 | **To stop the service** 100 | ``` 101 | service watchman_sync stop 102 | service watchman_worker stop 103 | ``` 104 | 105 | **To restart the service** 106 | ``` 107 | service watchman_sync restart 108 | service watchman_worker restart 109 | ``` 110 | 111 | ## Logs 112 | All Watchman logs are maintained at `/tmp/watchman.log` 113 | 114 | License 115 | -------- 116 | 117 | Copyright 2016 Skcript. 118 | 119 | Licensed under the Apache License, Version 2.0 (the "License"); 120 | you may not use this file except in compliance with the License. 121 | You may obtain a copy of the License at 122 | 123 | http://www.apache.org/licenses/LICENSE-2.0 124 | 125 | Unless required by applicable law or agreed to in writing, software 126 | distributed under the License is distributed on an "AS IS" BASIS, 127 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 128 | See the License for the specific language governing permissions and 129 | limitations under the License. 130 | 131 | About 132 | ----- 133 | 134 | ![Skcript](http://www.skcript.com/static/skcript_norm.png) 135 | 136 | Watchman is maintained and funded by Skcript. The names and logos for 137 | Skcript are properties of Skcript. 138 | 139 | We love open source, and we have been doing quite a bit of contributions to the community. Take a look at them [here][skcriptoss]. Also, encourage people around us to get involved in community [operations][community]. [Join us][hiring], if you'd like to see the world change from our HQ. 140 | 141 | [skcriptoss]: http://skcript.github.io/ 142 | [community]: http://www.skcript.com/community?utm_source=github 143 | [hiring]: http://www.skcript.com/careers?utm_source=github 144 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pyyaml 2 | logging 3 | watchdog 4 | observer 5 | requests 6 | hiredis 7 | rq 8 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from setuptools import setup, find_packages 3 | 4 | with open('README.md') as f: 5 | readme = f.read() 6 | 7 | with open('LICENSE') as f: 8 | license = f.read() 9 | 10 | setup( 11 | name='watchman', 12 | version='0.0.1', 13 | description='Make data smarter', 14 | long_description=readme, 15 | authors=['Swaathi Kakarla', 'Shilpi Agrawal'], 16 | authors_email=['swaathi@skcript.com', 'shilpi@skcript.com'], 17 | url='http://www.skcript.com', 18 | license=license, 19 | packages=find_packages(exclude=('tests', 'docs')), 20 | entry_points={ 21 | 'console_scripts': [ 22 | 'watchman = watchman.cli:main', 23 | ], 24 | }, 25 | install_requires=( 26 | ['pyyaml'], ['logging'], ['watchdog'], ['observer'], ['requests'], 27 | ['rq'], ['redis'] 28 | ) 29 | ) 30 | -------------------------------------------------------------------------------- /watchman/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | import os 3 | import time 4 | import yaml 5 | import logging 6 | import subprocess 7 | from watch import FileWatch 8 | from threading import Thread 9 | 10 | # Modules in Watchman 11 | from conf import LOG_FILENAME, load_regexes, load_config, update_config 12 | from worker import work 13 | import configurations 14 | 15 | logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG) 16 | log = logging.getLogger("watchman.init") 17 | 18 | class Watchman(): 19 | @staticmethod 20 | def configure(): 21 | config = load_config() 22 | 23 | try: 24 | while True: 25 | ans = raw_input("MENU \n 1. Add Source 2.Remove Source 3. View 4. Save \n") 26 | if (int(ans) == 1): 27 | configurations.add_source(config) 28 | elif (int(ans) == 2): 29 | configurations.remove_source(config) 30 | elif (int(ans) == 3): 31 | configurations.view_source(config) 32 | elif (int(ans) == 4): 33 | update_config(config) 34 | else: 35 | print "Huh? That wasn't even an option. -_-" 36 | except KeyboardInterrupt: 37 | log.error("Config cancelled by user") 38 | 39 | @staticmethod 40 | def sync(): 41 | FileWatch(regexes = load_regexes()).start() 42 | 43 | @staticmethod 44 | def work(): 45 | work() 46 | -------------------------------------------------------------------------------- /watchman/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | from .cli import main 3 | -------------------------------------------------------------------------------- /watchman/cli/cli.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | """ 3 | Watchman command line tool 4 | """ 5 | 6 | import click 7 | 8 | from watchman import Watchman 9 | 10 | # Disable the warning that Click displays (as of Click version 5.0) when users 11 | # use unicode_literals in Python 2. 12 | # See http://click.pocoo.org/dev/python3/#unicode-literals for more details. 13 | click.disable_unicode_literals_warning = True 14 | 15 | @click.group() 16 | def main(): 17 | """Watchman command line tool.""" 18 | pass 19 | 20 | @main.command() 21 | def sync(): 22 | """Start watching sources.""" 23 | Watchman.sync() 24 | 25 | @main.command() 26 | def worker(): 27 | """Start watchman worker.""" 28 | Watchman.work() 29 | 30 | @main.command() 31 | def configure(): 32 | """Configure watchman.""" 33 | Watchman.configure() 34 | -------------------------------------------------------------------------------- /watchman/conf.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | import os 3 | import re 4 | import yaml 5 | import redis 6 | import logging 7 | 8 | # Log configs 9 | LOG_FILENAME = '/tmp/watchman.log' 10 | logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG) 11 | log = logging.getLogger("watchman.conf") 12 | 13 | # Queuing system configs 14 | # redis = redis.StrictRedis(host='localhost', port=6379, db=0) 15 | REDIS_HOST = 'localhost' 16 | REDIS_PORT = 6379 17 | REDIS = redis.StrictRedis(host=REDIS_HOST, port=REDIS_PORT, db=0) 18 | QUEUES = ['default', 'filewatcher', 'reports'] 19 | 20 | # RateLimiter 21 | RL_LIMIT = 100 22 | RL_PERIOD = 60 23 | 24 | # Settings file 25 | CONFIG_FILE = os.path.expanduser("~/watchman.yml") 26 | 27 | def load_paths(): 28 | """ Try to load paths. """ 29 | if not os.path.isfile(CONFIG_FILE): 30 | return [] 31 | 32 | paths = yaml.load(open(CONFIG_FILE))['source'] 33 | if isinstance(paths, list): 34 | return paths 35 | else: 36 | return [] 37 | 38 | def load_regexes(): 39 | """ Try to load regexes. """ 40 | if not os.path.isfile(CONFIG_FILE): 41 | return [] 42 | 43 | regexes = yaml.load(open(CONFIG_FILE))['regexes'] 44 | if isinstance(regexes, list): 45 | return regexes 46 | else: 47 | return [] 48 | 49 | def load_endpoints(): 50 | """ Try to load regexes. """ 51 | if not os.path.isfile(CONFIG_FILE): 52 | return {} 53 | 54 | endpoints = yaml.load(open(CONFIG_FILE))['endpoints'] 55 | if isinstance(endpoints, dict): 56 | return endpoints 57 | else: 58 | return {} 59 | 60 | def load_config(config_file=CONFIG_FILE): 61 | """ Try to load YAML config file. """ 62 | config = {'source': [], 'regexes': [], 'endpoints': {}} 63 | if os.path.isfile(config_file): 64 | log.debug("Try loading config file: {0}".format(config_file)) 65 | config = yaml.load(open(config_file)) or config 66 | else: 67 | log.debug("Try creating config file: {0}".format(config_file)) 68 | open(config_file, 'w') 69 | 70 | return config 71 | 72 | def update_config(config, config_file=CONFIG_FILE): 73 | """ Try to update YAML config file. """ 74 | yaml.dump(config, open(config_file, "w"), default_flow_style=False) 75 | log.info("Updated config in %s" % CONFIG_FILE) 76 | -------------------------------------------------------------------------------- /watchman/configurations.py: -------------------------------------------------------------------------------- 1 | def add_source(config): 2 | source = raw_input("What is the source path?: ") 3 | if os.path.exists(source): 4 | print("Adding ({0})...".format(source)) 5 | config['source'].append(source) 6 | 7 | ans = raw_input("Add more paths? (y/n): ") 8 | if ans in {"y", "Y"}: 9 | get_source(config) 10 | else: 11 | print("Path does not exist!") 12 | get_source(config) 13 | 14 | def remove_source(config): 15 | if len(config['source']) == 0: 16 | print "Nothing to remove here." 17 | 18 | else: 19 | for path in config['source']: 20 | ans = raw_input("Remove {0}? (y/n):".format(path)) 21 | if ans in {"y", "Y"}: 22 | config['source'].remove(path) 23 | 24 | def view_source(config): 25 | if len(config['source']) == 0: 26 | print "Nothing to see here." 27 | 28 | else: 29 | for path in config['source']: 30 | print path 31 | ans = raw_input() 32 | -------------------------------------------------------------------------------- /watchman/extension.py: -------------------------------------------------------------------------------- 1 | # COMMON FOR ALL FUNCTIONS DEFINED HERE 2 | # Accepts path as input 3 | # For create, destroy actions, this function is called with src path 4 | # For move actions, this function is called twice for src and dest path 5 | 6 | def ratelimit(path): 7 | "Ratelimits API calls by returning a unique string based on path" 8 | return path.split("/")[0] 9 | 10 | def prevent(path): 11 | "Prevents API call from being made by returning a True or False" 12 | return False 13 | -------------------------------------------------------------------------------- /watchman/pigeon.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | import re 3 | import time 4 | import requests 5 | import logging 6 | from rratelimit import Limiter 7 | 8 | # Modules in Watchman 9 | from conf import load_endpoints, LOG_FILENAME, REDIS, RL_LIMIT, RL_PERIOD 10 | import extension 11 | 12 | ENDPOINTS = load_endpoints() 13 | # Ratelimiter limiting pigeon at RL_LIMIT actions per RL_PERIOD 14 | LIMITER = Limiter(REDIS, action='pigeon', limit=RL_LIMIT, period=RL_PERIOD) 15 | 16 | # Logger Creds 17 | logging.basicConfig(filename=LOG_FILENAME, level=logging.DEBUG) 18 | log = logging.getLogger("watchman.pigeon") 19 | 20 | # Ratelimiting server pings at RL_LIMIT actions per RL_PERIOD 21 | def ratelimit(): 22 | """ 23 | This decorator ratelimits an API call based on string returned by 24 | extension.ratelimit() 25 | """ 26 | def decorateRatelimit(func): 27 | def ratelimitFunction(*args,**kargs): 28 | name = extension.ratelimit(args[0]) 29 | if name: 30 | while not LIMITER.checked_insert(name): 31 | print "{0} is being rate limited".format(name) 32 | time.sleep(0.5) 33 | 34 | ret = func(*args,**kargs) 35 | return ret 36 | 37 | return ratelimitFunction 38 | return decorateRatelimit 39 | 40 | def dumpargs(): 41 | """ 42 | This decorator dumps out the arguments passed to a function before 43 | calling it 44 | """ 45 | def decorateDumpargs(func): 46 | argnames = func.func_code.co_varnames[:func.func_code.co_argcount] 47 | fname = func.func_name 48 | def dumpargsFunction(*args, **kargs): 49 | string = "Calling '" + fname + "' with args: " + ", ".join( 50 | '%s=%r' % entry 51 | for entry in zip(argnames, args[:len(argnames)]) 52 | ) 53 | print string 54 | log.debug(string) 55 | 56 | ret = func(*args, **kargs) 57 | return ret 58 | 59 | return dumpargsFunction 60 | return decorateDumpargs 61 | 62 | def prevent(): 63 | """ 64 | This decorator prevents an API call by comparing boolean returned by 65 | extension.prevent() 66 | """ 67 | def decoratePrevent(func): 68 | def preventFunction(*args, **kargs): 69 | val = False 70 | for arg in args: 71 | if extension.prevent(arg): 72 | val = True 73 | break 74 | 75 | if val: 76 | print "Not calling" 77 | else: 78 | ret = func(*args, **kargs) 79 | return ret 80 | 81 | return preventFunction 82 | return decoratePrevent 83 | 84 | @prevent() 85 | @ratelimit() 86 | @dumpargs() 87 | def post_folder_creation(src): 88 | options = { 'path': src } 89 | requests.post(ENDPOINTS['folder_create'], params=options) 90 | 91 | @prevent() 92 | @ratelimit() 93 | @dumpargs() 94 | def post_file_creation(src): 95 | options = { 'path': src } 96 | requests.post(ENDPOINTS['file_create'], params=options) 97 | 98 | @prevent() 99 | @ratelimit() 100 | @dumpargs() 101 | def post_folder_destroy(src): 102 | options = { 'path': src } 103 | requests.post(ENDPOINTS['folder_destroy'], params=options) 104 | 105 | @prevent() 106 | @ratelimit() 107 | @dumpargs() 108 | def post_file_destroy(src): 109 | options = { 'path': src } 110 | requests.post(ENDPOINTS['file_destroy'], params=options) 111 | 112 | @prevent() 113 | @ratelimit() 114 | @dumpargs() 115 | def post_folder_move(src, dest): 116 | options = { 'oldpath': src, 'newpath': dest } 117 | requests.post(ENDPOINTS['folder_move'], params=options) 118 | 119 | @prevent() 120 | @ratelimit() 121 | @dumpargs() 122 | def post_file_move(src, dest): 123 | options = { 'oldpath': src, 'newpath': dest } 124 | requests.post(ENDPOINTS['file_move'], params=options) 125 | -------------------------------------------------------------------------------- /watchman/watch.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | import os 3 | import re 4 | import time 5 | import shutil 6 | import requests 7 | 8 | from rq import Queue 9 | from redis import Redis 10 | from watchdog.observers import Observer 11 | from watchdog.events import RegexMatchingEventHandler 12 | 13 | # Modules in Watchman 14 | import watchman.pigeon 15 | from conf import load_paths, REDIS 16 | 17 | class FileWatch(RegexMatchingEventHandler): 18 | queue = Queue('filewatcher', connection=REDIS) 19 | 20 | def start(self): 21 | self.observer = Observer() 22 | for path in load_paths(): 23 | print path 24 | self.observer.schedule(self, path=path, recursive=True) 25 | self.observer.start() 26 | print "* Running Watchman (Press CTRL+C to quit)" 27 | 28 | try: 29 | while True: 30 | time.sleep(1) 31 | except KeyboardInterrupt: 32 | self.observer.stop() 33 | self.observer.join() 34 | 35 | def stop(self): 36 | self.observer.stop() 37 | 38 | def on_created(self, event): 39 | src = event.src_path 40 | if self.is_hidden(src): 41 | # Events with hidden paths (dot files) are not registered 42 | return 43 | 44 | try: 45 | if event.is_directory: 46 | print "Created folder {0}".format(src) 47 | FileWatch.queue.enqueue( 48 | 'watchman.pigeon.post_folder_creation', 49 | src 50 | ) 51 | else: 52 | print "Created file {0}".format(src) 53 | FileWatch.queue.enqueue( 54 | 'watchman.pigeon.post_file_creation', 55 | src 56 | ) 57 | except Exception, e: 58 | print(e) 59 | 60 | def on_deleted(self, event): 61 | src = event.src_path 62 | if self.is_hidden(src): 63 | # Events with hidden paths (dot files) are not registered 64 | return 65 | 66 | try: 67 | if event.is_directory: 68 | print "Deleted folder {0}".format(src) 69 | FileWatch.queue.enqueue( 70 | 'watchman.pigeon.post_folder_destroy', 71 | src 72 | ) 73 | else: 74 | print "Deleted file {0}".format(src) 75 | FileWatch.queue.enqueue( 76 | 'watchman.pigeon.post_file_destroy', 77 | src 78 | ) 79 | 80 | except Exception, e: 81 | print(e) 82 | 83 | def on_moved(self, event): 84 | src = event.src_path 85 | dest = event.dest_path 86 | if self.is_hidden(src) or self.is_hidden(dest): 87 | # Events with hidden paths (dot files) are not registered 88 | return 89 | 90 | try: 91 | if event.is_directory: 92 | print "Moved folder from {0} to {1}".format(src, dest) 93 | FileWatch.queue.enqueue( 94 | 'watchman.pigeon.post_folder_move', 95 | src, 96 | dest 97 | ) 98 | else: 99 | print "Moved file from {0} to {1}".format(src, dest) 100 | FileWatch.queue.enqueue( 101 | 'watchman.pigeon.post_file_move', 102 | src, 103 | dest 104 | ) 105 | except Exception, e: 106 | print(e) 107 | 108 | def is_hidden(self, path): 109 | m = re.search('(?<=\/\.)\w+', path) 110 | return m is not None 111 | -------------------------------------------------------------------------------- /watchman/worker.py: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | # Run using `python watchman/worker.py` in "watchman" folder 3 | 4 | # pip install rq-dashboard 5 | # Run `rq-dashboard` and point browser to http://0.0.0.0:9181/ 6 | 7 | import os 8 | import redis 9 | from conf import REDIS, QUEUES 10 | from rq import Worker, Queue, Connection 11 | 12 | def work(): 13 | print("Hello from the worker side.") 14 | with Connection(REDIS): 15 | worker = Worker(map(Queue, QUEUES)) 16 | worker.work() 17 | -------------------------------------------------------------------------------- /watchman_sync: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ### BEGIN INIT INFO 3 | # Provides: watchman sync 4 | # Required-Start: $remote_fs $syslog 5 | # Required-Stop: $remote_fs $syslog 6 | # Default-Start: 2 3 4 5 7 | # Default-Stop: 0 1 6 8 | # Short-Description: Put a short description of the service here 9 | # Description: Put a long description of the service here 10 | ### END INIT INFO 11 | 12 | # Change the next 3 lines to suit where you install your script and what you want to call it 13 | DIR=/usr/local/bin/watchman sync 14 | DAEMON_NAME=watchman_sync 15 | 16 | # Add any command line options for your daemon here 17 | DAEMON_OPTS="" 18 | 19 | # This next line determines what user the script runs as. 20 | DAEMON_USER=$(whoami) 21 | 22 | # The process ID of the script when it runs is stored here: 23 | PIDFILE=/var/run/watchman/$DAEMON_NAME.pid 24 | 25 | . /lib/lsb/init-functions 26 | 27 | do_start () { 28 | log_daemon_msg "Starting system $DAEMON_NAME daemon" 29 | start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas /usr/local/bin/watchman sync 30 | log_end_msg $? 31 | } 32 | do_stop () { 33 | log_daemon_msg "Stopping system $DAEMON_NAME daemon" 34 | start-stop-daemon --stop --pidfile $PIDFILE --retry 10 35 | log_end_msg $? 36 | } 37 | 38 | case "$1" in 39 | 40 | start|stop) 41 | do_${1} 42 | ;; 43 | 44 | restart|reload|force-reload) 45 | do_stop 46 | do_start 47 | ;; 48 | 49 | status) 50 | status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $? 51 | ;; 52 | 53 | *) 54 | echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}" 55 | exit 1 56 | ;; 57 | 58 | esac 59 | exit 0 60 | -------------------------------------------------------------------------------- /watchman_worker: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: watchman_worker 5 | # Required-Start: $remote_fs $syslog 6 | # Required-Stop: $remote_fs $syslog 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: Ping file changes to an API of your choice 10 | # Description: Ping file changes to an API of your choice 11 | ### END INIT INFO 12 | 13 | # Change the next 3 lines to suit where you install your script and what you want to call it 14 | # DIR=/usr/local/bin/watchman worker 15 | DAEMON_NAME=watchman_worker 16 | 17 | # Add any command line options for your daemon here 18 | DAEMON_OPTS="" 19 | 20 | # This next line determines what user the script runs as. 21 | DAEMON_USER=$(whoami) 22 | 23 | # The process ID of the script when it runs is stored here: 24 | PIDFILE=/var/run/watchman/$DAEMON_NAME.pid 25 | 26 | . /lib/lsb/init-functions 27 | 28 | do_start () { 29 | log_daemon_msg "Starting system $DAEMON_NAME daemon" 30 | start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas /usr/local/bin/watchman worker 31 | log_end_msg $? 32 | } 33 | do_stop () { 34 | log_daemon_msg "Stopping system $DAEMON_NAME daemon" 35 | start-stop-daemon --stop --pidfile $PIDFILE --retry 10 36 | log_end_msg $? 37 | } 38 | 39 | case "$1" in 40 | 41 | start|stop) 42 | do_${1} 43 | ;; 44 | 45 | restart|reload|force-reload) 46 | do_stop 47 | do_start 48 | ;; 49 | 50 | status) 51 | status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $? 52 | ;; 53 | 54 | esac 55 | exit 0 56 | --------------------------------------------------------------------------------