#!/usr/bin/python2.6
# -*- coding: utf-8 -*-
#
# Univention Net Installer Daemon
#  UVMM handler
#
# Copyright 2010-2014 Univention GmbH
#
# http://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided 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 with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see
# <http://www.gnu.org/licenses/>.

from optparse import OptionParser
import ldap.filter
import multiprocessing
import os
import signal
import socket
import threading
import univention.debug as ud
import univention.uldap

def createDaemon(options):
	'''daemonize via double fork, close all file handles, open /dev/null for stdin/out/err'''
	try:
		pid = os.fork()
	except OSError, e:
		print 'Daemon Mode Error: %s' % (e.strerror, )

	if pid:
		os._exit(0) # _exit should be used for forking, pylint: disable-msg=W0212

	os.setsid()
	signal.signal(signal.SIGHUP, signal.SIG_IGN)
	try:
		pid = os.fork()
	except OSError, e:
		print 'Daemon Mode Error: %s' % (e.strerror, )
	if pid:
		pf = open(options.pidfile, 'w+')
		pf.write(str(pid))
		pf.close()
		os._exit(0) # _exit should be used for forking, pylint: disable-msg=W0212

	os.chdir("/")
	os.umask(0)

	try:
		maxfd = os.sysconf("SC_OPEN_MAX")
	except (AttributeError, ValueError):
		maxfd = 256       # default maximum
	os.closerange(0, maxfd)
	os.open("/dev/null", os.O_RDONLY)
	os.open("/dev/null", os.O_RDWR)
	os.open("/dev/null", os.O_RDWR)

def getLDAPConnection():
	# TODO: check for an alternative binddn
	if os.path.exists('/etc/ldap.secret'):
		lo = univention.uldap.getAdminConnection()
	else:
		lo = univention.uldap.getMachineConnection()
	return lo

def processor(options, data_queue):
	ud.init('/var/log/univention/net-installer-daemon.log', 1, 0)
	ud.set_level(ud.LDAP, options.debug_level)
	lo = getLDAPConnection()
	def process(data):
		search_filter = ldap.filter.filter_format('(&(objectClass=univentionHost)(cn=%s)(univentionServerReinstall=1))', (data, ))
		result = lo.search(filter=search_filter)
		if not result:
			ud.debug(ud.LDAP, ud.ERROR, 'The computer object %r was not found. Please remove the reinstall flag manually.' % (data, ))
		else:
			for (dn, _, ) in result:
				lo.modify(dn, [('univentionServerReinstall', '1', '0')])
				ud.debug(ud.LDAP, ud.PROCESS, 'The computer object %r (%r) was sucessfully modified. ' % (data, dn, ))
	while True:
		data = data_queue.get()
		process(data)

def listener(options, data_queue):
	local_host_address = socket.gethostbyname(socket.gethostname())
	size = 1024 # max hostname length
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
	s.bind((local_host_address, options.port, ))
	s.listen(1)
	while True:
		(client, _, ) = s.accept()
		def reader():
			client.settimeout(10)
			try:
				data = client.recv(size)
			except:
				return
			finally:
				client.close()
			if data:
				data = data.strip()
				data_queue.put(data)
		threading.Thread(target=reader).start()

def main():
	parser = OptionParser(usage='usage: %prog [options] command')
	parser.add_option('-p', '--port',
			action='store', dest='port', default="49173", type='int',
			help='Port for the daemon [%default]')
	parser.add_option('-P', '--pidfile',
			action='store', dest='pidfile', default="/var/run/univention-net-installer-daemon.pid",
			help='Path to the pid-file [%default]')
	parser.add_option('-D', '--daemon',
			action='store_true', dest='daemonize', default=False,
			help='Fork into background')
	parser.add_option('-d', '--debug',
			action='store', dest='debug_level', default="1", type='int',
			help='Debug level (0 to 4)')
	(options, _, ) = parser.parse_args()
	if options.daemonize:
		createDaemon(options)
	data_queue = multiprocessing.Queue()
	subthread = threading.Thread(target=processor, args=(options, data_queue, ))
	subthread.daemon = True
	subthread.start()
	listener(options, data_queue)

if __name__ == '__main__':
	main()
