# encoding: utf-8
"""
daemon.py

Created by Thomas Mangin on 2011-11-29.
Copyright (c) 2011 Exa Networks. All rights reserved.
"""

import os
import sys
import pwd
import errno
import socket
import resource

from .log.logger import Logger

def signed (value):
	if value == sys.maxint:
		return -1
	return value

class Daemon (object):
	def __init__ (self,configuration):
		self.daemonize = configuration.daemon.daemonize
		self.user = configuration.daemon.user
		self.log = Logger('daemon', configuration.log.daemon)
		#mask = os.umask(0137)
		if configuration.daemon.filemax:
			try:
				# default on mac are (256,-1)
				resource.setrlimit(resource.RLIMIT_NOFILE, (configuration.daemon.filemax, -1))
			except (resource.error,ValueError),e:
				soft,hard = resource.getrlimit(resource.RLIMIT_NOFILE)
				self.log.error('could not increase file descriptor limit : %s' % str(e))
				self.log.error('the current limit is %d' % signed(soft))
				self.log.error('the maximum possible limit is %d' % signed(hard))

	def drop_privileges (self):
		"""returns true if we are left with insecure privileges"""
		# os.name can be ['posix', 'nt', 'os2', 'ce', 'java', 'riscos']
		if os.name not in ['posix',]:
			return False

		uid = os.getuid()
		gid = os.getgid()

		if uid and gid:
			return False

		try:
			user = pwd.getpwnam(self.user)
			nuid = int(user.pw_uid)
			ngid = int(user.pw_uid)
		except KeyError:
			return True

		# not sure you can change your gid if you do not have a pid of zero
		try:
			if not uid:
				os.setuid(nuid)
			if not gid:
				os.setgid(ngid)
			return False
		except OSError:
			return True

	def _is_socket (self,fd):
		try:
			s = socket.fromfd(fd, socket.AF_INET, socket.SOCK_RAW)
		except ValueError,e:
			# The file descriptor is closed
			return False
		try:
			s.getsockopt(socket.SOL_SOCKET, socket.SO_TYPE)
		except socket.error, e:
			# It is look like one but it is not a socket ...
			if e.args[0] == errno.ENOTSOCK:
				return False
		return True

	def daemonise (self):
		if not self.daemonize:
			return

		def fork_exit ():
			try:
				pid = os.fork()
				if pid > 0:
					os._exit(0)
			except OSError, e:
				self.log.debug('Can not fork, errno %d : %s' % (e.errno,e.strerror))

		# do not detach if we are already supervised or run by init like process
		if self._is_socket(sys.__stdin__.fileno()) or os.getppid() == 1:
			return

		fork_exit()
		os.setsid()
		fork_exit()
		self.silence()

	def silence (self):
		maxfd = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
		if (maxfd == resource.RLIM_INFINITY):
			maxfd = MAXFD

		for fd in range(0, maxfd):
			try:
				os.close(fd)
			except OSError:
				pass

		os.open("/dev/null", os.O_RDWR)
		os.dup2(0, 1)
		os.dup2(0, 2)


@@ -574,24 +574,186 @@
 ax:%0A%09%09%09try:%0A
+%09%09%09%09soft_limit,hard_limit = resource.getrlimit(resource.RLIMIT_NOFILE)%0A%09%09%09%09wanted_limit = min(configuration.daemon.filemax, hard_limit if hard_limit %3E 0 else 0)%0A%0A
 %09%09%09%09# defaul
@@ -826,43 +826,36 @@
 E, (
-configuration.daemon.filemax, -1
+wanted_limit, hard_limit
 ))%0A
+%0A
 %09%09%09e
@@ -889,24 +889,100 @@
 Error),e:%0A%09%09
+%09%09self.log.error('could not increase file descriptor limit : %25s' %25 str(e))%0A%0A
 %09%09soft,hard 
@@ -1026,18 +1026,16 @@
 NOFILE)%0A
-%09%09
 %09%09self.l
@@ -1044,34 +1044,27 @@
 .error('
-could not increase
+the current
  file de
@@ -1082,26 +1082,31 @@
 mit 
-: %25s
+is %25d
 ' %25 s
-tr(e
+igned(soft
 ))%0A
-%09%09
 %09%09se
@@ -1123,23 +1123,48 @@
 or('the 
-current
+maximum possible file descriptor
  limit i
@@ -1178,25 +1178,23 @@
  signed(
-soft
+hard
 ))%0A
-%09%09
 %09%09self.l
@@ -1211,32 +1211,42 @@
 the 
-maximum possible
+requested file descriptor
  limit 
-i
+wa
 s %25d
@@ -1256,22 +1256,72 @@
  signed(
-hard))
+configuration.daemon.filemax))%0A%0A%09%09self.file_limit = soft
 %0A%0A%09def d
@@ -1812,17 +1812,17 @@
 %09if not 
-u
+g
 id:%0A%09%09%09%09
@@ -1831,14 +1831,14 @@
 .set
-u
+g
 id(n
-u
+g
 id)%0A
@@ -1839,33 +1839,33 @@
 ngid)%0A%09%09%09if not 
-g
+u
 id:%0A%09%09%09%09os.setgi
@@ -1858,30 +1858,30 @@
 :%0A%09%09%09%09os.set
-g
+u
 id(n
-g
+u
 id)%0A%09%09%09retur

Set gid before attempting to set gid