Module: check_mk
Branch: master
Commit: 9674eb0ad458e9f4ce65a5afbd44d34457061b2d
URL:
http://git.mathias-kettner.de/git/?p=check_mk.git;a=commit;h=9674eb0ad458e9…
Author: Sven Panne <sp(a)mathias-kettner.de>
Date: Mon May 30 13:58:14 2016 +0200
Fixed the event daemon protocol, its implementation and its usage.
socket.recv can (and will, see the POSIX spec!) sometimes result in a
partial read, so we have to handle this, and basically the same holds for
socket.send. As a consequence, our protocol needs a way to determine when a
request is complete. One way of doing this is shutting down the writing
side, but this would make it impossible to handle multiple requests over the
same connection.
The solution on the receiving side is simple: Do it like Livestatus and
terminate a request with an empty line. A shutdown will of course continue
to terminate a request, too. On the sending side, use socket.sendall
instead of socket.send.
In a nutshell: 99.9% of all uses of recv/send/read/write which are not
within a loop are plainly wrong. The evil thing is that bugs caused by this
will only surface with a low probability, and even less when the I/O and/or
network load is low, i.e. basically never when doing naive testing by hand.
:-P
As a bonus, we even fix the encoding issue by defining the whole protocol to
be UTF-8-based.
---
bin/mkeventd | 75 +++++++++++++++++++++++++++++++-----------------
web/htdocs/mkeventd.py | 3 +-
2 files changed, 51 insertions(+), 27 deletions(-)
diff --git a/bin/mkeventd b/bin/mkeventd
index f613009..7ff0b8d 100755
--- a/bin/mkeventd
+++ b/bin/mkeventd
@@ -374,6 +374,30 @@ class MKClientError(Exception):
Exception.__init__(self, t)
+class Queries:
+ def __init__(self, sock):
+ self._socket = sock
+ self._buffer = ""
+
+ def __iter__(self):
+ return self
+
+ def next(self):
+ while True:
+ parts = self._buffer.split("\n\n", 1)
+ if len(parts) > 1:
+ break
+ data = self._socket.recv(4096)
+ if len(data) == 0:
+ if len(self._buffer) == 0:
+ raise StopIteration()
+ parts = [self._buffer, ""]
+ break
+ self._buffer += data
+ request, self._buffer = parts
+ return request.decode("utf-8").splitlines()
+
+
#.
# .--SNMP-Traps----------------------------------------------------------.
# | ____ _ _ __ __ ____ _____ |
@@ -2649,38 +2673,37 @@ class StatusServer:
def handle_client(self, socket, allow_commands, client_ip):
- query = decode_from_bytes(socket.recv(8192)).splitlines()
- parts = query[0].split(None, 1)
- if len(parts) != 2:
- raise MKClientError("Invalid query. Need GET/COMMAND plus
argument(s)")
+ for query in Queries(socket):
+ parts = query[0].split(None, 1)
+ if len(parts) != 2:
+ raise MKClientError("Invalid query. Need GET/COMMAND plus
argument(s)")
- method, table = parts
+ method, table = parts
+ output_format = "python"
- output_format = "python"
-
- with lock_eventstatus:
- if method == "GET":
- response, output_format = self.handle_get_request(table, query[1:])
+ with lock_eventstatus:
+ if method == "GET":
+ response, output_format = self.handle_get_request(table, query[1:])
- elif method == "REPLICATE":
- response = self.handle_replicate(table, client_ip)
+ elif method == "REPLICATE":
+ response = self.handle_replicate(table, client_ip)
- elif method == "COMMAND":
- if not allow_commands:
- raise MKClientError("Sorry. Commands are disallowed via
TCP")
- self.handle_command_request(table)
- response = None
+ elif method == "COMMAND":
+ if not allow_commands:
+ raise MKClientError("Sorry. Commands are disallowed via
TCP")
+ self.handle_command_request(table)
+ response = None
- else:
- raise MKClientError("Invalid method %s (allowed are GET, COMMAND and
REPLICATE)" % method)
+ else:
+ raise MKClientError("Invalid method %s (allowed are GET, COMMAND
and REPLICATE)" % method)
- if output_format == "plain":
- for line in response:
- socket.send("\t".join([to_utf8(quote_tab(c)) for c in line]))
- socket.send("\n")
- else:
- socket.send(repr(response))
- socket.send("\n")
+ if output_format == "plain":
+ for line in response:
+ socket.send("\t".join([to_utf8(quote_tab(c)) for c in
line]))
+ socket.send("\n")
+ else:
+ socket.send(repr(response))
+ socket.send("\n")
socket.close()
diff --git a/web/htdocs/mkeventd.py b/web/htdocs/mkeventd.py
index 2a3c3bd..806dfe0 100644
--- a/web/htdocs/mkeventd.py
+++ b/web/htdocs/mkeventd.py
@@ -186,7 +186,8 @@ def query(query):
sock.settimeout(timeout)
sock.connect(socket_path)
- sock.send(query)
+ sock.sendall(query)
+ sock.shutdown(socket.SHUT_WR)
response_text = ""
while True: