pmanager.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #!/usr/bin/env python3
  2. import json
  3. import os
  4. import signal
  5. import socket
  6. import subprocess
  7. import sys
  8. import time
  9. import websocket
  10. DEBUG = True
  11. DEBUG = False
  12. def log(s):
  13. if not DEBUG:
  14. return
  15. print(s)
  16. def sendmsg(msg):
  17. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  18. ret = None
  19. try:
  20. s.connect(('localhost', 9999))
  21. tosend = msg + "\r\n"
  22. encoded_msg = tosend.encode("ascii")
  23. s.sendall(encoded_msg)
  24. ret = s.recv(4096)
  25. s.close()
  26. except Exception as e:
  27. log(e)
  28. return None
  29. log("[nc output] %s " % ret)
  30. return ret
  31. class PManager:
  32. ws = websocket.WebSocket()
  33. ip = "ws://192.168.1.208:9001"
  34. p_list = {}
  35. ptable = {}
  36. log_list = {}
  37. name = ""
  38. def __init__(self, name, ptable):
  39. self.ptable = ptable
  40. self.conn(self.ip)
  41. self.name = name
  42. signal.signal(signal.SIGINT, self.sighandler)
  43. signal.signal(signal.SIGCHLD, signal.SIG_IGN) #To kill zombies
  44. while True:
  45. try:
  46. data = self.ws.recv()
  47. except Exception as e:
  48. log(e)
  49. self.conn(self.ip)
  50. continue
  51. try:
  52. j = json.loads(data)
  53. except Exception as e:
  54. log(e)
  55. log("Invalid json?")
  56. continue
  57. if ("type" not in j or "command" not in j or
  58. "key" not in j or j["type"] != "snowmix"):
  59. log("Dropping: %s" % j)
  60. continue
  61. #log(j)
  62. if j["command"] == "oneshot":
  63. opts = None
  64. if "options" in j:
  65. opts = j["options"]
  66. self.oneshot(j["key"], opts)
  67. if j["command"] == "launch":
  68. opts = None
  69. if "options" in j:
  70. opts = j["options"]
  71. self.launch(j["key"], opts)
  72. if j["command"] == "kill":
  73. self.stop(j["key"])
  74. if j["command"] == "netcat":
  75. sendmsg(j["key"])
  76. if j["command"] == "status": #and j["key"] == self.name:
  77. self.sendstatus()
  78. def sendstatus(self):
  79. self.send({"type": "snowmix", "subtype": "statusreport", "name": self.name, "data":self.p_list})
  80. def send(self, data):
  81. try:
  82. if type(data) is dict:
  83. data = json.dumps(data)
  84. print(data)
  85. self.ws.send(data)
  86. except Exception as e:
  87. print(e)
  88. def conn(self, target, timeout=None):
  89. if self.ws is None:
  90. self.ws = websocket.WebSocket()
  91. try:
  92. self.ws.connect(target)
  93. except Exception as e:
  94. log(e)
  95. if timeout is None:
  96. timeout = 2
  97. timeout = min(timeout, 90)
  98. time.sleep(timeout)
  99. self.conn(target, timeout+2) #keep trying you basterd
  100. def close(self):
  101. if self.ws is None:
  102. return
  103. self.ws.close()
  104. def sighandler(self, sig, frame):
  105. log("\rExiting!")
  106. self.close()
  107. log("Bye!")
  108. sys.exit(0)
  109. def stop(self, key):
  110. if key not in self.p_list:
  111. log("key %s doesn't exist! not killing!" % (key))
  112. return
  113. try:
  114. os.killpg(os.getpgid(self.p_list[key]), signal.SIGTERM)
  115. except Exception as e:
  116. log("This process (%d) can't be killed! Why?" % self.p_list[key])
  117. log(e)
  118. try:
  119. self.log_list[key].close()
  120. except Exception as e:
  121. log("ex file: %s" % e)
  122. if key in self.log_list:
  123. del self.log_list[key]
  124. del self.p_list[key]
  125. self.sendstatus()
  126. def oneshot(self, key, options=None):
  127. if key not in self.ptable:
  128. log("This process (%s) is not on my table!" % key)
  129. return
  130. log("Oneshotting! key: %s" % key)
  131. tolaunch = self.ptable[key]
  132. if options is not None:
  133. options = [ o for o in options if type(o) is str ]
  134. tolaunch = tolaunch + options
  135. log(tolaunch)
  136. try:
  137. proc = subprocess.Popen(tolaunch, preexec_fn=os.setsid)
  138. except Exception as e:
  139. log(e)
  140. def launch(self, key, options=None):
  141. if key in self.p_list:
  142. log("key %s exist! not running!" % (key))
  143. return
  144. if key not in self.ptable:
  145. log("This process (%s) is not on my table!" % key)
  146. return
  147. log("Launching! key: %s, opts: %s" % (key, options))
  148. tolaunch = self.ptable[key]
  149. if options is not None:
  150. options = [ o for o in options if type(o) is str ]
  151. tolaunch = tolaunch + options
  152. log(tolaunch)
  153. self.log_list[key] = open("/tmp/%s" % key, "w")
  154. try:
  155. proc = subprocess.Popen(tolaunch, preexec_fn=os.setsid, stdout=self.log_list[key])
  156. #Le doy su propio grupo al subproceso
  157. except Exception as e:
  158. log(e)
  159. sys.exit(1)
  160. self.p_list[key] = proc.pid
  161. self.sendstatus()
  162. return proc.pid
  163. if __name__ == "__main__":
  164. if len(sys.argv) != 2:
  165. print("Usage: ./%s <file.json>" % (sys.argv[0]))
  166. sys.exit(1)
  167. try:
  168. data = json.loads(open(sys.argv[1]).read())
  169. except Exception as e:
  170. print(e)
  171. sys.exit(2)
  172. ptable = data["data"]
  173. name = data["name"]
  174. m = PManager(name, ptable)