#!/usr/bin/python

#       Copyright 2012:
#
#       Nick Davidson <nicholas.davidson@gmail.com>,
#       Simon Haswell <maxcady78@hotmail.co.uk>,
#       Neil Williams <codehelp@debian.org>,
#       James Bennet <github@james-bennet.com>
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#
#       This program 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 General Public License for more details.
#
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

# -*- coding: utf-8 -*-

import optparse
import pyinotify
import daemon
import pybit
import os
import sys
import logging
import signal
import subprocess
import time

from pybit.daemonlogger import LoggingDaemonContext, FileLikeLogger
from pyinotify import ProcessEvent


META="PYBIT_WATCHER_"
PIDFILE = "/var/run/pybit-watcher.pid"


def signal_handler(signal, frame):
	try:
		print '\nClosing %s' % os.path.basename(__file__)
		sys.exit (os.EX_OK)
	except Exception as e:
		raise Exception('Error in signal handler: ' + str(e))
		return

def getDaemonLogger (filePath, format = None) :
	FORMAT = format or '%(asctime)s %(msg)s'
	logging.basicConfig(filename=filePath, format=FORMAT, level=logging.DEBUG)
	return logging.getLogger()

class EventHandler(pyinotify.ProcessEvent):
	
	def process_IN_CREATE(self, event):
		if os.path.isfile(event.pathname) and event.pathname.endswith(".changes"):
			logging.debug("Sleeping for %ss" % self.sleeptime)
			time.sleep(self.sleeptime)
			if event.path in self.settings['rules'] :
				rule = self.settings['rules'][event.path]
				cmd = "reprepro -b %s processincoming %s" % (rule['repobase'], rule['rule'])
				if ('user' in self.settings and
					self.settings['user'] != ''):
					cmd = "su %s -c '%s'" % (self.settings['user'], cmd)
				if ('dryrun' not in self.settings or
					self.settings['dryrun'] == False):
					
					process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
					(stdout, stderr) = process.communicate()
					
					if process.returncode:
						logging.debug(stderr)
						logging.debug("reprepo command failed with code: %s" % process.returncode)
					else:
						logging.debug(cmd)
				else:
					logging.debug("dry-run: %s" % cmd)
			else:
				logging.debug("couldn't find a rule which matched the watched path.")
				
	def __init__(self, settings):
		self.settings = settings
		self.sleeptime = 3
		if 'sleeptime' in self.settings:
			self.sleeptime = self.settings['sleeptime']
		ProcessEvent.__init__(self)


def run(settings):
	try:
		signal.signal(signal.SIGINT, signal_handler)
		signal.signal(signal.SIGTERM, signal_handler)
	except Exception as e:
		raise Exception('Error configuring signal handler: ' + str(e))
	
	mask = pyinotify.IN_CREATE
	if 'dryrun' in settings and settings['dryrun'] == True:
		logging.debug("Starting in dryrun mode")
	wm = pyinotify.WatchManager()
	handler = EventHandler(settings)
	notifier = pyinotify.Notifier(wm, handler)
	for path in settings['rules'].keys():
		wdd = wm.add_watch(path, mask, rec=True)
	notifier.loop()

if __name__ == '__main__':
	parser = optparse.OptionParser()
	#options we can override in the config file.
	groupConfigFile = optparse.OptionGroup(parser,
		"Config File Defaults","All the options which have defaults read from a config file.")
	
	groupConfigFile.add_option("--dry-run", dest="dryrun", action="store_true",
		help="Controls if we simulate or do we actually run.", metavar=META + "DRYRUN")
	
	groupConfigFile.add_option("--user", dest="user",
		help="Which user do we run as?", metavar=META + "USER")
	
	groupConfigFile.add_option("--sleeptime", dest="sleeptime",
		help="The number of seconds we wait after a changes file is created before we run the reprepro command.",
		metavar=META + "SLEEPTIME")
	parser.add_option("--conf_file", dest="conf_file", default="watcher/watcher.conf",
		help="Config file to read settings from, defaults to watcher.conf which will be read from configs/watcher and /etc/pybit/watcher in turn.",
		metavar=META + "CONF_FILE")

	parser.add_option("-v", dest="verbose", action="store_true", default=False,
		help="Turn on verbose messages.", metavar=META+"VERBOSE")

	parser.add_option("-d", dest="daemonise", action="store_true", default=False,
		help="Daemonise with output going to /var/log/pybit-watcher", metavar=META+"DAEMONISE")
		
	
	
	(options, args) = parser.parse_args()
	
	context = None
	if options.daemonise :
		testLogger = getDaemonLogger('/var/log/pybitwatcher.log')
		stdoutLogger = getDaemonLogger('/dev/null')
		stderrLogger = getDaemonLogger('/dev/null')
		context = LoggingDaemonContext()
		context.loggers_preserve=[testLogger]
		context.stdout_logger = stdoutLogger
		context.stderr_logger = stderrLogger
		logging.debug ("I: Daemonised")
	else :
#		FORMAT = format or '%(asctime)s %(msg)s'
		logging.basicConfig(level=logging.DEBUG)
		logging.debug ("I: Not daemonised")
	
	(settings, opened_path) = pybit.load_settings(options.conf_file)
	
	if settings == {}:
		logging.debug("Couldn't load configuration from %s" % opened_path)
		sys.exit(-1)
		
	if 'configured'  in settings and settings['configured'] == False:
		logging.debug ("Please configure the Reprepro watcher. Edit %s" % opened_path)
		sys.exit(os.EX_OK)
	
	settings = pybit.merge_options(settings, groupConfigFile, options)
	
	if options.daemonise:
		with context :
			pid = str(os.getpid())
			file(PIDFILE, 'w').write(pid)
			run(settings)
			
	else:
		logging.debug ("I: Running watcher...")
		run(settings)
