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/>.
26 __all__
= ( 'Action' )
28 valid_exit_status
= (0, 1, 2, 3)
30 only_copy_prop
= ('id', 'status', 'command', 't_to_go', 'timeout', 'env')
32 shellchars
= ( '!', '$', '^', '&', '*', '(', ')', '~', '[', ']',
33 '|', '{', '}', ';', '<', '>', '?', '`')
36 # This abstract class is use just for having a common id between actions and checks
40 # Mix the env into the environnment variables
41 # into a new local env dict
42 # rmq : we cannot just update os.environ because
43 # it will be modified for all other checks too
44 def get_local_environnement(self
):
45 local_env
= copy
.copy(os
.environ
)
47 local_env
[p
] = self
.env
[p
]
52 """ Start this action command ; the command will be executed in a subprocess """
53 self
.status
= 'launched'
54 self
.check_time
= time
.time()
55 self
.wait_time
= 0.0001
56 self
.last_poll
= self
.check_time
57 # Get a local env variables with our additionnal values
58 self
.local_env
= self
.get_local_environnement()
60 return self
.execute__() ## OS specific part
63 def get_outputs(self
, out
, max_plugins_output_length
):
64 #print "Get only," , max_plugins_output_length, "bytes"
65 # Squize all output after max_plugins_output_length
66 out
= out
[:max_plugins_output_length
]
68 elts
= out
.split('\n')
70 elts_line1
= elts
[0].split('|')
71 # First line before | is output, and strip it
72 self
.output
= elts_line1
[0].strip()
73 # After | is perfdata, and strip it
74 if len(elts_line1
) > 1:
75 self
.perf_data
= elts_line1
[1].strip()
76 # The others lines are long_output
77 # but others are are not stripped
79 self
.long_output
= '\n'.join(elts
[1:])
82 def check_finished(self
, max_plugins_output_length
):
83 # We must wait, but checks are variable in time
84 # so we do not wait the same for an little check
85 # than a long ping. So we do like TCP : slow start with *2
86 # but do not wait more than 0.1s.
87 self
.last_poll
= time
.time()
88 if self
.process
.poll() is None:
89 self
.wait_time
= min(self
.wait_time
*2, 0.1)
90 #time.sleep(wait_time)
92 if (now
- self
.check_time
) > self
.timeout
:
94 #print "Kill", self.process.pid, self.command, now - self.check_time
95 self
.status
= 'timeout'
96 self
.execution_time
= now
- self
.check_time
100 # Get standards outputs
101 self
.exit_status
= self
.process
.returncode
102 (stdoutdata
, stderrdata
) = self
.process
.communicate()
104 #we should not keep the process now
107 # if the exit status is anormal, we add stderr to the output
108 if self
.exit_status
not in valid_exit_status
:
109 stdoutdata
= stdoutdata
+ stderrdata
110 # Now grep what we want in the output
111 self
.get_outputs(stdoutdata
, max_plugins_output_length
)
114 self
.execution_time
= time
.time() - self
.check_time
117 def copy_shell__(self
, new_i
):
118 """ This will assign the attributes present in 'only_copy_prop' from self to new_i """
119 for prop
in only_copy_prop
:
120 setattr(new_i
, prop
, getattr(self
, prop
))
124 def got_shell_characters(self
):
125 for c
in self
.command
:
132 ## OS specific "execute__" & "kill__" are defined by "Action" class definition:
136 class Action(__Action
):
138 # We allow direct launch only for 2.7 and higher version
139 # because if a direct launch crash, under this the file hanldes
140 # are not releases, it's not good.
141 def execute__(self
, force_shell
=sys
.version_info
< (2, 7)):
142 # If the command line got shell characters, we should go in a shell
143 # mode. So look at theses parameters
144 force_shell |
= self
.got_shell_characters()
149 shlex
.split(self
.command
)
152 self
.process
= subprocess
.Popen(cmd
,
153 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
,
154 close_fds
=True, shell
=force_shell
, env
=self
.local_env
)
155 except OSError , exp
:
156 print "Debug : Error in launching command:", self
.command
, exp
, force_shell
157 # Maybe it's just a shell we try to exec. So we must retry
158 if not force_shell
and exp
.errno
== 8 and exp
.strerror
== 'Exec format error':
159 return self
.execute__(True)
160 self
.output
= exp
.__str
__()
163 self
.execution_time
= time
.time() - self
.check_time
165 # Maybe we run out of file descriptor. It's not good at all!
166 if exp
.errno
== 24 and exp
.strerror
== 'Too many open files':
167 return 'toomanyopenfiles'
170 os
.kill(self
.process
.pid
, 9)
175 TerminateProcess
= ctypes
.windll
.kernel32
.TerminateProcess
177 class Action(__Action
):
180 self
.process
= subprocess
.Popen(shlex
.split(self
.command
),
181 stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, env
=self
.local_env
, shell
=True)
182 except WindowsError, exp
:
183 print "We kill the process : ", exp
, self
.command
184 self
.status
= 'timeout'
185 self
.execution_time
= time
.time() - self
.check_time
188 TerminateProcess(int(self
.process
._handle
), -1)