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/>.
25 #Unix and windows do not have the same import
29 TerminateProcess
= ctypes
.windll
.kernel32
.TerminateProcess
33 # This class is use just for having a common id between actions and checks
41 # If the command line got shell caracters, we should go in a shell
42 # mode. So look at theses parameters
43 # Note : it's "hard", but in fact you can launch it 100000times
44 # a second so... I don't care :) (and at least it's easy to understand)
45 def got_shell_caracters(self
):
46 for c
in self
.command
:
47 if c
in ['!','$','^','&','*','(',')','~','[',']','|','{','}',';','<','>','?','`']:
52 def get_outputs(self
, out
, max_plugins_output_length
):
53 #print "Get only," , max_plugins_output_length, "bytes"
54 # Squize all output after max_plugins_output_length
55 out
= out
[:max_plugins_output_length
]
57 elts
= out
.split('\n')
59 elts_line1
= elts
[0].split('|')
60 # First line before | is output, and strip it
61 self
.output
= elts_line1
[0].strip()
62 # After | is perfdata, and strip it
63 if len(elts_line1
) > 1:
64 self
.perf_data
= elts_line1
[1].strip()
65 # The others lines are long_output
66 # but others are are not stripped
68 self
.long_output
= '\n'.join(elts
[1:])
71 # Mix the env into the environnment variables
72 # into a new local env dict
73 # rmq : we cannot just update os.environ because
74 # it will be modified for all other checks too
75 def get_local_environnement(self
):
76 local_env
= copy
.copy(os
.environ
)
78 local_env
[p
] = self
.env
[p
]
84 self
.execute_windows()
89 def execute_windows(self
):
91 self
.status
= 'launched'
92 self
.check_time
= time
.time()
93 self
.wait_time
= 0.0001
94 self
.last_poll
= self
.check_time
95 local_env
= self
.get_local_environnement()
97 self
.process
= subprocess
.Popen(shlex
.split(self
.command
),
98 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, env
=local_env
, shell
=True)
99 except WindowsError, exp
:
100 print "We kill the process : ", exp
, self
.command
101 self
.status
= 'timeout'
102 self
.execution_time
= time
.time() - self
.check_time
106 def execute_unix(self
):
107 self
.status
= 'launched'
108 self
.check_time
= time
.time()
109 self
.last_poll
= self
.check_time
110 self
.wait_time
= 0.0001
111 #cmd = ['/bin/sh', '-c', self.command]
112 # Nagios do not use the /bin/sh -c command, so I don't do it too
114 # Get a local env variables with our additionnal values
115 local_env
= self
.get_local_environnement()
117 # We allow direct launch only for 2.7 and higher version
118 # because if a direct launch crash, under this the file hanldes
119 # are not releases, it's not good.
120 if sys
.version_info
>= (2,7):
121 shell_launch
= self
.got_shell_caracters()
124 # Maybe we will failed in our first attempt, we can retry
130 self
.process
= subprocess
.Popen(self
.command
,
131 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
132 close_fds
=True, shell
=True, env
=local_env
)
134 except OSError , exp
:
135 print "Debug : Error in launching shell command:", self
.command
, exp
136 print "Way: launch command?", self
.got_shell_caracters()
137 self
.output
= exp
.__str
__()
140 self
.execution_time
= time
.time() - self
.check_time
142 # Maybe we run out of file descriptor. It's not good at all!
143 if exp
.errno
== 24 and exp
.strerror
== 'Too many open files':
144 return 'toomanyopenfiles'
148 # Direct exec, quicker but only for 2.7 and higher, sorry.
150 p
= subprocess
.Popen(shlex
.split(self
.command
),
151 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
152 close_fds
=True, env
=local_env
)
155 except OSError , exp
:
156 # Maybe it's just a shell we try to exec. So we must retry
157 if exp
.errno
== 8 and exp
.strerror
== 'Exec format error':
159 continue # loop and go in the shell launch this time
160 # Here, we are with a real error
161 print "Debug : Error in launching direct exec command:", self
.command
, exp
162 self
.output
= exp
.__str
__()
165 self
.execution_time
= time
.time() - self
.check_time
167 # Maybe we run out of file descriptor. It's not good at all!
168 if exp
.errno
== 24 and exp
.strerror
== 'Too many open files':
169 return 'toomanyopenfiles'
174 def check_finished(self
, max_plugins_output_length
):
176 self
.check_finished_windows(max_plugins_output_length
)
178 self
.check_finished_unix(max_plugins_output_length
)
181 def check_finished_unix(self
, max_plugins_output_length
):
182 # We must wait, but checks are variable in time
183 # so we do not wait the same for an little check
184 # than a long ping. So we do like TCP : slow start with *2
185 # but do not wait more than 0.1s.
186 self
.last_poll
= time
.time()
187 if self
.process
.poll() is None:
188 self
.wait_time
= min(self
.wait_time
*2, 0.1)
189 #time.sleep(wait_time)
191 if (now
- self
.check_time
) > self
.timeout
:
194 os
.kill(self
.process
.pid
, 9)
195 #print "Kill", self.process.pid, self.command, now - self.check_time
196 self
.status
= 'timeout'
197 self
.execution_time
= now
- self
.check_time
201 # Get standards outputs
202 self
.exit_status
= self
.process
.returncode
203 (stdoutdata
, stderrdata
) = self
.process
.communicate()
205 # if the exit status is anormal, we add stderr to the output
206 if self
.exit_status
not in [0, 1, 2, 3]:
207 stdoutdata
= stdoutdata
+ stderrdata
208 # Now grep what we want in the output
209 self
.get_outputs(stdoutdata
, max_plugins_output_length
)
212 self
.execution_time
= time
.time() - self
.check_time
215 def check_finished_windows(self
, max_plugins_output_length
):
216 # We must wait, but checks are variable in time
217 # so we do not wait the same for an little check
218 # than a long ping. So we do like TCP : slow start with *2
219 # but do not wait more than 0.1s.
220 self
.last_poll
= time
.time()
221 if self
.process
.poll() is None:
222 self
.wait_time
= min(self
.wait_time
*2, 0.1)
223 #time.sleep(wait_time)
225 if (now
- self
.check_time
) > self
.timeout
:
228 TerminateProcess(int(self
.process
._handle
), -1)
229 #print "Kill", self.process.pid, self.command
230 self
.status
= 'timeout'
231 self
.execution_time
= now
- self
.check_time
235 # Get standards outputs
236 self
.exit_status
= self
.process
.returncode
237 (stdoutdata
, stderrdata
) = self
.process
.communicate()
239 #we should not keep the process now
242 # if the exit status is anormal, we add stderr to the output
243 if self
.exit_status
not in [0, 1, 2, 3]:
244 stdoutdata
= stdoutdata
+ stderrdata
245 #self.get_outputs(self.process.stdout.read(),max_plugins_output_length)
246 # Now grep what we want in the output
247 self
.get_outputs(stdoutdata
, max_plugins_output_length
)
249 # if self.exit_status != 0:
250 # print "DBG:", self.command, self.exit_status, self.output
252 self
.execution_time
= time
.time() - self
.check_time