2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
6 #This file is part of Shinken.
8 #Shinken is free software: you can redistribute it and/or modify
9 #it under the terms of the GNU Affero General Public License as published by
10 #the Free Software Foundation, either version 3 of the License, or
11 #(at your option) any later version.
13 #Shinken is distributed in the hope that it will be useful,
14 #but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 #GNU Affero General Public License for more details.
18 #You should have received a copy of the GNU Affero General Public License
19 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
21 import os
, errno
, sys
, ConfigParser
24 from pwd
import getpwnam
25 from grp
import getgrnam
28 ########################## DAEMON PART ###############################
29 # The standard I/O file descriptors are redirected to /dev/null by default.
30 REDIRECT_TO
= getattr(os
, "devnull", "/dev/null")
36 #the instances will have their own init
42 print "Unlinking", self
.pidfile
43 os
.unlink(self
.pidfile
)
47 f
= open(self
.pidfile
)
53 #Check if previous run are still launched by reading the pidfile
54 #if the pid exist by not the pid, we remove the pidfile
55 #if still exit, we can replace (kill) the other run
57 def check_parallel_run(self
, do_replace
):
58 if os
.path
.exists(self
.pidfile
):
62 except os
.error
, detail
:
63 if detail
.errno
== errno
.ESRCH
:
64 print "stale pidfile exists. removing it."
65 os
.unlink(self
.pidfile
)
67 #if replace, kill the old process
72 raise SystemExit, "valid pidfile exists. Exiting."
75 #Make the program a daemon. It can redirect all outputs (stdout, stderr) to debug file if need
76 #or to devnull if no debug
77 def create_daemon(self
, do_debug
=False, debug_file
=''):
78 #First we manage the file descriptor, because debug file can be
81 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
82 if (maxfd
== resource
.RLIM_INFINITY
):
85 # Iterate through and close all file descriptors.
86 for fd
in range(0, maxfd
):
89 except OSError:# ERROR, fd wasn't open to begin with (ignored)
91 #If debug, all will be writen to if
93 os
.open(debug_file
, os
.O_CREAT | os
.O_WRONLY | os
.O_TRUNC
)
94 else:#else, nowhere...
95 os
.open(REDIRECT_TO
, os
.O_RDWR
)
96 os
.dup2(0, 1)# standard output (1)
97 os
.dup2(0, 2)# standard error (2)
103 raise Exception, "%s [%d]" % (e
.strerror
, e
.errno
)
109 raise Exception, "%s [%d]" % (e
.strerror
, e
.errno
)
111 os
.chdir(self
.workdir
)
119 #We writ the pid file now
120 f
= open(self
.pidfile
, "w")
121 f
.write("%d" % os
.getpid())
125 #Just give the uid of a user by looking at it's name
126 def find_uid_from_name(self
):
128 return getpwnam(self
.user
)[2]
129 except KeyError , exp
:
130 print "Error: the user", self
.user
, "is unknown"
133 #Just give the gid of a group by looking at it's name
134 def find_gid_from_name(self
):
136 return getgrnam(self
.group
)[2]
137 except KeyError , exp
:
138 print "Error: the group", self
.group
, "is unknown"
142 #Change user of the running program. Just insult the admin
143 #if he wants root run (it can override). If change failed,
145 def change_user(self
, insane
=False):
146 if (self
.user
== 'root' or self
.group
== 'root') and not insane
:
147 print "What's ??? You want the application run under the root account?"
148 print "I am not agree with it. If you really whant it, put :"
149 print "idontcareaboutsecurity=yes"
150 print "in the config file"
154 uid
= self
.find_uid_from_name()
155 gid
= self
.find_gid_from_name()
156 if uid
== None or gid
== None:
160 #First group, then user :)
161 os
.setregid(gid
, gid
)
162 os
.setreuid(uid
, uid
)
164 print "Error : cannot change user/group to %s/%s (%s [%d])" % (self
.user
, self
.group
, e
.strerror
, e
.errno
)
169 #Parse self.config_file and get all properties in it
170 #If properties need a pythonization, we do it. It
171 #also put default value id the propertie is missing in
173 def parse_config_file(self
):
174 properties
= self
.__class
__.properties
175 if self
.config_file
!= None:
176 config
= ConfigParser
.ConfigParser()
177 config
.read(self
.config_file
)
178 if config
._sections
== {}:
179 print "Bad or missing config file : %s " % self
.config_file
181 for (key
, value
) in config
.items('daemon'):
182 if key
in properties
and properties
[key
]['pythonize'] != None:
183 value
= properties
[key
]['pythonize'](value
)
184 setattr(self
, key
, value
)
186 print "No config file specified, use defaults parameters"
187 #Now fill all defaults where missing parameters
188 for prop
in properties
:
189 if not hasattr(self
, prop
):
190 value
= properties
[prop
]['default']
191 if prop
in properties
and properties
[prop
]['pythonize'] != None:
192 value
= properties
[prop
]['pythonize'](value
)
193 setattr(self
, prop
, value
)
194 print "Using default value :", prop
, value
197 #Some paths can be relatives. We must have a full path by taking
198 #the config file by reference
199 def relative_paths_to_full(self
, reference_path
):
200 #print "Create relative paths with", reference_path
201 properties
= self
.__class
__.properties
202 for prop
in properties
:
203 if 'path' in properties
[prop
] and properties
[prop
]['path']:
204 path
= getattr(self
, prop
)
206 #Windows full paths are like c:/blabla
207 #Unixes are like /blabla
208 #So we look for : on windows, / for Unixes
212 if path
!= '' and path
[0] != os
.sep
:
213 new_path
= reference_path
+ os
.sep
+ path
215 #os.sep = \ on windows, and we must look at c: like format
216 if len(path
) > 2 and path
[1] != ':':
217 new_path
= reference_path
+ os
.sep
+ path
218 setattr(self
, prop
, new_path
)
219 #print "Setting %s for %s" % (new_path, prop)
222 def manage_signal(self
, sig
, frame
):
223 print "Dummy signal function Do not use this function dumbass dev ! "
227 #Set an exit function that is call when we quit
228 def set_exit_handler(self
):
229 func
= self
.manage_signal
233 win32api
.SetConsoleCtrlHandler(func
, True)
235 version
= ".".join(map(str, sys
.version_info
[:2]))
236 raise Exception("pywin32 not installed for Python " + version
)
239 signal
.signal(signal
.SIGTERM
, func
)
242 def get_header(self
):
243 return ["Shinken %s" % VERSION
,
244 "Copyright (c) 2009-2010 :",
245 "Gabes Jean (naparuba@gmail.com)",
246 "Gerhard Lausser, Gerhard.Lausser@consol.de",
249 def print_header(self
):
250 for line
in self
.get_header():