forked from victronenergy/velib_python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
streamcommand.py
93 lines (73 loc) · 3.02 KB
/
streamcommand.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
## @package dbus_vrm
import logging
logger = logging.getLogger(__name__)
import codecs
import threading
import subprocess
from time import sleep
# Runs a command, and calls sendfeedback with the statusupdates.
class StreamCommand(object):
SIGNALS = {
1: "SIGHUP", 2: "SIGINT", 3: "SIGQUIT", 4: "SIGILL", 6: "SIGABRT", 7: "SIGBUS", 8: "SIGFPE",
9: "SIGKILL", 10: "SIGBUS", 11: "SIGSEGV", 12: "SIGSYS", 13: "SIGPIPE", 14: "SIGALRM",
15: "SIGTERM"}
def run(self, command, timeout, feedbacksender):
self.feedbacksender = feedbacksender
self.returncode = None
self.utf8_decoder = codecs.getdecoder("utf_8")
self.latin1_decoder = codecs.getdecoder("latin1")
def target():
logger.info('Thread started for running %s' % command)
self.feedbacksender.send({"status": "starting"})
try:
self.process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
except OSError as e:
logger.info("Command %s could not be started, errno: %s, msg: %s"
% (command, e.errno, e.strerror))
self.feedbacksender.send({"status": "error",
"errormessage": "Could not start (errno %s, msg %s)" % (e.errno, e.strerror),
"errorcode": 731}, finished=True)
self.process = None
return
self.readandsend()
thread = threading.Thread(target=target)
thread.start()
thread.join(timeout)
if self.process is None:
# Error message has already beent sent
return None
# Make sure to send all the output
self.readandsend()
if thread.is_alive():
logger.warning("Command %s will now be terminated because of timeout" % command)
self.process.terminate() # TODO or should it be killed?
thread.join()
logger.warning("Command %s has been terminated" % command)
r = {"status": "error", "errormessage": "Stopped by timeout", "errorcode": 732}
elif self.process.returncode < 0:
signal = -1 * self.process.returncode
error = "Stopped with signal %d - %s" % (signal, self.SIGNALS.get(signal, "unknown"))
logger.warning("Command %s abnormal stop. %s" % (command, error))
r = {"status": "error", "errorcode": 733, "errormessage": error}
else:
logger.info("Command %s execution completed. Exitcode %d" % (command, self.process.returncode))
r = {"status": "finished", "exitcode": self.process.returncode}
self.feedbacksender.send(r, finished=True)
return self.process.returncode
def readandsend(self):
# TODO: check that below code works OK with vup stdout encoding (UTF-8), including non-standard ASCII chars
while True:
self.process.stdout.flush()
line = self.process.stdout.readline()
try:
unicode_line, _ = self.utf8_decoder(line)
except UnicodeDecodeError:
unicode_line, _ = self.latin1_decoder(line)
# Max length on pubnub is 1800 chars, and output is much better readable with the bare eye
# when sent per line. So no need to send it alltogether.
self.feedbacksender.send({"status": "running", "xmloutput": unicode_line})
if line == b'' and self.process.poll() != None:
break
sleep(0.04)