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 if (hasattr(os
, "devnull")):
31 REDIRECT_TO
= os
.devnull
33 REDIRECT_TO
= "/dev/null"
39 #the instances will have their own init
45 print "Unlinking", self
.pidfile
46 os
.unlink(self
.pidfile
)
50 f
= open(self
.pidfile
)
56 #Check if previous run are still launched by reading the pidfile
57 #if the pid exist by not the pid, we remove the pidfile
58 #if still exit, we can replace (kill) the other run
60 def check_parallel_run(self
, do_replace
):
61 if os
.path
.exists(self
.pidfile
):
65 except os
.error
, detail
:
66 if detail
.errno
== errno
.ESRCH
:
67 print "stale pidfile exists. removing it."
68 os
.unlink(self
.pidfile
)
70 #if replace, kill the old process
75 raise SystemExit, "valid pidfile exists. Exiting."
78 #Make the program a daemon. It can redirect all outputs (stdout, stderr) to debug file if need
79 #or to devnull if no debug
80 def create_daemon(self
, do_debug
=False, debug_file
=''):
81 #First we manage the file descriptor, because debug file can be
84 maxfd
= resource
.getrlimit(resource
.RLIMIT_NOFILE
)[1]
85 if (maxfd
== resource
.RLIM_INFINITY
):
88 # Iterate through and close all file descriptors.
89 for fd
in range(0, maxfd
):
92 except OSError:# ERROR, fd wasn't open to begin with (ignored)
94 #If debug, all will be writen to if
96 os
.open(debug_file
, os
.O_CREAT | os
.O_WRONLY | os
.O_TRUNC
)
97 else:#else, nowhere...
98 os
.open(REDIRECT_TO
, os
.O_RDWR
)
99 os
.dup2(0, 1)# standard output (1)
100 os
.dup2(0, 2)# standard error (2)
106 raise Exception, "%s [%d]" % (e
.strerror
, e
.errno
)
112 raise Exception, "%s [%d]" % (e
.strerror
, e
.errno
)
114 os
.chdir(self
.workdir
)
122 #We writ the pid file now
123 f
= open(self
.pidfile
, "w")
124 f
.write("%d" % os
.getpid())
128 #Just give the uid of a user by looking at it's name
129 def find_uid_from_name(self
):
131 return getpwnam(self
.user
)[2]
132 except KeyError , exp
:
133 print "Error: the user", self
.user
, "is unknown"
136 #Just give the gid of a group by looking at it's name
137 def find_gid_from_name(self
):
139 return getgrnam(self
.group
)[2]
140 except KeyError , exp
:
141 print "Error: the group", self
.group
, "is unknown"
145 #Change user of the running program. Just insult the admin
146 #if he wants root run (it can override). If change failed,
148 def change_user(self
, insane
=False):
149 if (self
.user
== 'root' or self
.group
== 'root') and not insane
:
150 print "What's ??? You want the application run under the root account?"
151 print "I am not agree with it. If you really whant it, put :"
152 print "idontcareaboutsecurity=yes"
153 print "in the config file"
157 uid
= self
.find_uid_from_name()
158 gid
= self
.find_gid_from_name()
159 if uid
== None or gid
== None:
163 #First group, then user :)
164 os
.setregid(gid
, gid
)
165 os
.setreuid(uid
, uid
)
167 print "Error : cannot change user/group to %s/%s (%s [%d])" % (self
.user
, self
.group
, e
.strerror
, e
.errno
)
172 #Parse self.config_file and get all properties in it
173 #If properties need a pythonization, we do it. It
174 #also put default value id the propertie is missing in
176 def parse_config_file(self
):
177 properties
= self
.__class
__.properties
178 if self
.config_file
!= None:
179 config
= ConfigParser
.ConfigParser()
180 config
.read(self
.config_file
)
181 if config
._sections
== {}:
182 print "Bad or missing config file : %s " % self
.config_file
184 for (key
, value
) in config
.items('daemon'):
185 if key
in properties
and properties
[key
]['pythonize'] != None:
186 value
= properties
[key
]['pythonize'](value
)
187 setattr(self
, key
, value
)
189 print "No config file specified, use defaults parameters"
190 #Now fill all defaults where missing parameters
191 for prop
in properties
:
192 if not hasattr(self
, prop
):
193 value
= properties
[prop
]['default']
194 if prop
in properties
and properties
[prop
]['pythonize'] != None:
195 value
= properties
[prop
]['pythonize'](value
)
196 setattr(self
, prop
, value
)
197 print "Using default value :", prop
, value
200 #Some paths can be relatives. We must have a full path by taking
201 #the config file by reference
202 def relative_paths_to_full(self
, reference_path
):
203 #print "Create relative paths with", reference_path
204 properties
= self
.__class
__.properties
205 for prop
in properties
:
206 if 'path' in properties
[prop
] and properties
[prop
]['path']:
207 path
= getattr(self
, prop
)
209 #Windows full paths are like c:/blabla
210 #Unixes are like /blabla
211 #So we look for : on windows, / for Unixes
215 if path
!= '' and path
[0] != os
.sep
:
216 new_path
= reference_path
+ os
.sep
+ path
218 #os.sep = \ on windows, and we must look at c: like format
219 if len(path
) > 2 and path
[1] != ':':
220 new_path
= reference_path
+ os
.sep
+ path
221 setattr(self
, prop
, new_path
)
222 #print "Setting %s for %s" % (new_path, prop)
225 def manage_signal(self
, sig
, frame
):
226 print "Dummy signal function Do not use this function dumbass dev ! "
230 #Set an exit function that is call when we quit
231 def set_exit_handler(self
):
232 func
= self
.manage_signal
236 win32api
.SetConsoleCtrlHandler(func
, True)
238 version
= ".".join(map(str, sys
.version_info
[:2]))
239 raise Exception("pywin32 not installed for Python " + version
)
242 signal
.signal(signal
.SIGTERM
, func
)
245 def get_header(self
):
246 return ["Shinken %s" % VERSION
,
247 "Copyright (c) 2009-2010 :",
248 "Gabes Jean (naparuba@gmail.com)",
249 "Gerhard Lausser, Gerhard.Lausser@consol.de",
252 def print_header(self
):
253 for line
in self
.get_header():