2 #Copyright (C) 2009-2010 :
3 # Gabes Jean, naparuba@gmail.com
4 # Gerhard Lausser, Gerhard.Lausser@consol.de
5 # Gregory Starck, g.starck@gmail.com
6 # Hartmut Goebel, h.goebel@goebel-consult.de
8 #This file is part of Shinken.
10 #Shinken is free software: you can redistribute it and/or modify
11 #it under the terms of the GNU Affero General Public License as published by
12 #the Free Software Foundation, either version 3 of the License, or
13 #(at your option) any later version.
15 #Shinken is distributed in the hope that it will be useful,
16 #but WITHOUT ANY WARRANTY; without even the implied warranty of
17 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 #GNU Affero General Public License for more details.
20 #You should have received a copy of the GNU Affero General Public License
21 #along with Shinken. If not, see <http://www.gnu.org/licenses/>.
30 __all__
= ( 'Action' )
32 valid_exit_status
= (0, 1, 2, 3)
34 only_copy_prop
= ('id', 'status', 'command', 't_to_go', 'timeout', 'env', 'module_type')
36 shellchars
= ( '!', '$', '^', '&', '*', '(', ')', '~', '[', ']',
37 '|', '{', '}', ';', '<', '>', '?', '`')
40 # This abstract class is use just for having a common id between actions and checks
44 # Mix the env into the environnment variables
45 # into a new local env dict
46 # rmq : we cannot just update os.environ because
47 # it will be modified for all other checks too
48 def get_local_environnement(self
):
49 local_env
= copy
.copy(os
.environ
)
51 local_env
[p
] = self
.env
[p
]
56 """ Start this action command ; the command will be executed in a subprocess """
57 self
.status
= 'launched'
58 self
.check_time
= time
.time()
59 self
.wait_time
= 0.0001
60 self
.last_poll
= self
.check_time
61 # Get a local env variables with our additionnal values
62 self
.local_env
= self
.get_local_environnement()
64 return self
.execute__() ## OS specific part
67 def get_outputs(self
, out
, max_plugins_output_length
):
68 #print "Get only," , max_plugins_output_length, "bytes"
69 # Squize all output after max_plugins_output_length
70 out
= out
[:max_plugins_output_length
]
72 elts
= out
.split('\n')
74 elts_line1
= elts
[0].split('|')
75 # First line before | is output, and strip it
76 self
.output
= elts_line1
[0].strip()
77 # After | is perfdata, and strip it
78 if len(elts_line1
) > 1:
79 self
.perf_data
= elts_line1
[1].strip()
80 # The others lines are long_output
81 # but others are are not stripped
83 self
.long_output
= '\n'.join(elts
[1:])
86 def check_finished(self
, max_plugins_output_length
):
87 # We must wait, but checks are variable in time
88 # so we do not wait the same for an little check
89 # than a long ping. So we do like TCP : slow start with *2
90 # but do not wait more than 0.1s.
91 self
.last_poll
= time
.time()
92 if self
.process
.poll() is None:
93 self
.wait_time
= min(self
.wait_time
*2, 0.1)
94 #time.sleep(wait_time)
96 if (now
- self
.check_time
) > self
.timeout
:
98 #print "Kill", self.process.pid, self.command, now - self.check_time
99 self
.status
= 'timeout'
100 self
.execution_time
= now
- self
.check_time
104 # Get standards outputs
105 self
.exit_status
= self
.process
.returncode
106 (stdoutdata
, stderrdata
) = self
.process
.communicate()
108 #we should not keep the process now
111 # if the exit status is anormal, we add stderr to the output
112 if self
.exit_status
not in valid_exit_status
:
113 stdoutdata
= stdoutdata
+ stderrdata
114 # Now grep what we want in the output
115 self
.get_outputs(stdoutdata
, max_plugins_output_length
)
118 self
.execution_time
= time
.time() - self
.check_time
121 def copy_shell__(self
, new_i
):
122 """ This will assign the attributes present in 'only_copy_prop' from self to new_i """
123 for prop
in only_copy_prop
:
124 setattr(new_i
, prop
, getattr(self
, prop
))
128 def got_shell_characters(self
):
129 for c
in self
.command
:
136 ## OS specific "execute__" & "kill__" are defined by "Action" class definition:
140 class Action(__Action
):
142 # We allow direct launch only for 2.7 and higher version
143 # because if a direct launch crash, under this the file hanldes
144 # are not releases, it's not good.
145 def execute__(self
, force_shell
=sys
.version_info
< (2, 7)):
146 # If the command line got shell characters, we should go in a shell
147 # mode. So look at theses parameters
148 force_shell |
= self
.got_shell_characters()
153 cmd
= shlex
.split(self
.command
)
156 self
.process
= subprocess
.Popen(cmd
,
157 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
158 close_fds
=True, shell
=force_shell
, env
=self
.local_env
)
159 except OSError , exp
:
160 print "Debug : Error in launching command:", self
.command
, exp
, force_shell
161 # Maybe it's just a shell we try to exec. So we must retry
162 if not force_shell
and exp
.errno
== 8 and exp
.strerror
== 'Exec format error':
163 return self
.execute__(True)
164 self
.output
= exp
.__str
__()
167 self
.execution_time
= time
.time() - self
.check_time
169 # Maybe we run out of file descriptor. It's not good at all!
170 if exp
.errno
== 24 and exp
.strerror
== 'Too many open files':
171 return 'toomanyopenfiles'
174 os
.kill(self
.process
.pid
, 9)
179 TerminateProcess
= ctypes
.windll
.kernel32
.TerminateProcess
181 class Action(__Action
):
184 self
.process
= subprocess
.Popen(shlex
.split(self
.command
),
185 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, env
=self
.local_env
, shell
=True)
186 except WindowsError, exp
:
187 print "We kill the process : ", exp
, self
.command
188 self
.status
= 'timeout'
189 self
.execution_time
= time
.time() - self
.check_time
192 TerminateProcess(int(self
.process
._handle
), -1)