ffpb.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. from __future__ import print_function
  2. import willie
  3. import netaddr
  4. import urllib2
  5. import re
  6. import os
  7. import subprocess
  8. import socket
  9. import SocketServer
  10. import threading
  11. msgserver = None
  12. # TODO: move into config file :)
  13. msgserver_known_senders = {
  14. "127.0.0.1": "localhost",
  15. "10.132.254.1": "gw01",
  16. "10.132.254.2": "gw02",
  17. "10.132.254.3": "gw03",
  18. "10.132.254.80": "public"
  19. }
  20. class MsgHandler(SocketServer.BaseRequestHandler):
  21. def handle(self):
  22. self.data = self.request.recv(2048).strip()
  23. sender = self.client_address[0]
  24. if sender in msgserver_known_senders:
  25. sender = msgserver_known_senders[sender]
  26. bot = self.server.bot
  27. if bot is None:
  28. print("ERROR: No bot in handle() :-(")
  29. return
  30. target = bot.config.core.owner
  31. if bot.config.has_section('ffpb') and not (bot.config.ffpb.msg_target is None):
  32. target = bot.config.ffpb.msg_target
  33. bot.msg(target, "[{0}] {1}".format(sender,self.data))
  34. class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
  35. pass
  36. def setup(bot):
  37. global msgserver
  38. if bot.config.has_section('ffpb') and bot.config.ffpb.msg_enable:
  39. host = "localhost"
  40. port = 4342
  41. if not bot.config.ffpb.msg_host is None: host = bot.config.ffpb.msg_host
  42. if not bot.config.ffpb.msg_port is None: port = int(bot.config.ffpb.msg_port)
  43. msgserver = ThreadingTCPServer((host,port), MsgHandler)
  44. msgserver.bot = bot
  45. ip, port = msgserver.server_address
  46. print("Messaging server listening on {}:{}".format(ip,port))
  47. msgserver_thread = threading.Thread(target=msgserver.serve_forever)
  48. msgserver_thread.daemon = True
  49. msgserver_thread.start()
  50. def shutdown(bot):
  51. global msgserver
  52. if not msgserver is None:
  53. msgserver.shutdown()
  54. print("Closed messaging server.")
  55. msgserver = None
  56. @willie.module.commands('status')
  57. def ffpb_status(bot, trigger):
  58. """Status des FFPB-Netzes: Anzahl (aktiver) Knoten + Clients"""
  59. response = urllib2.urlopen('http://nodecount.paderborn.freifunk.net/')
  60. html = response.read()
  61. m = re.search('<div id="nodecount">\s*(\d+)\s*</div>', html)
  62. nodecount = int(m.group(1))
  63. bot.say('nodecount = {}'.format(nodecount))
  64. def ffpb_get_address(name):
  65. peerfilename = '/home/ffpb-statusbot/knoten/' + name
  66. peer_mac = None
  67. if os.path.exists(peerfilename):
  68. peerfile = open(peerfilename, "r")
  69. for line in peerfile:
  70. if line.startswith("# MAC:"):
  71. peer_mac = line[6:].strip()
  72. peerfile.close()
  73. print("peer '", name, "': file '", peerfilename, "', MAC ", peer_mac, sep='')
  74. if not (peer_mac is None):
  75. return str(netaddr.EUI(peer_mac).ipv6_link_local())
  76. return None
  77. @willie.module.commands('ping')
  78. def ffpb_ping(bot, trigger):
  79. """Ping FFPB-Knoten"""
  80. target_name = trigger.group(2)
  81. if target_name is None or len(target_name) == 0:
  82. bot.say('Alter, wen soll ich denn pingen? Einmal mit Profis arbeiten -.-')
  83. return
  84. target = ffpb_get_address(target_name)
  85. if target is None:
  86. bot.say('Kein Plan wer mit \'' + target_name + '\' gemeint ist :/')
  87. return
  88. if target.startswith('fe80::'):
  89. target = 'fdca:ffee:ff12:132:' + target[6:]
  90. print("ping '", target , '"', sep='')
  91. result = os.system('ping6 -c 1 -W 2 ' + target + ' 2>/dev/null')
  92. if result == 0:
  93. bot.say('Knoten "' + target_name + '" antwortet \o/')
  94. elif result == 1 or result == 256:
  95. bot.say('Keine Antwort von "' + target_name + '" :-(')
  96. else:
  97. bot.say('Uh oh, irgendwas ist kaputt. Chef, ping result = ' + str(result) + ' - darf ich das essen?')
  98. @willie.module.commands('exec-on-peer')
  99. def ffpb_remoteexec(bot, trigger):
  100. """Remote Execution fuer FFPB_Knoten"""
  101. bot_params = trigger.group(2).split(' ',1)
  102. if len(bot_params) != 2:
  103. bot.say('Wenn du nicht sagst wo mach ich remote execution bei dir!')
  104. bot.say('Tipp: !exec-on-peer <peer> <cmd>')
  105. return
  106. target_name = bot_params[0]
  107. target_cmd = bot_params[1]
  108. if not trigger.admin:
  109. bot.say('Captcha required: https://xkcd.com/565/')
  110. return
  111. target = ffpb_get_address(target_name)
  112. if target is None:
  113. bot.say('Kein Plan wer mit \'' + target_name + '\' gemeint ist :/')
  114. if target.startswith('fe80::'):
  115. target = 'fdca:ffee:ff12:132:' + target[6:]
  116. cmd = 'ssh -6 -l root ' + target + ' -- "' + target_cmd + '"'
  117. print("REMOTE EXEC = " + cmd)
  118. try:
  119. result = subprocess.check_output(['ssh', '-6n', '-l', 'root', '-o', 'BatchMode=yes', '-o','StrictHostKeyChecking=no', target, target_cmd], stderr=subprocess.STDOUT, shell=False)
  120. lines = str(result).splitlines()
  121. bot.say('exec-on-peer(' + target_name + '): ' + str(len(lines)) + ' Zeilen (zeige max. 8):')
  122. for line in lines[0:8]:
  123. bot.say(line)
  124. except subprocess.CalledProcessError, e:
  125. bot.say('Fehler '+str(e.returncode)+' bei exec-on-peer('+target_name+'): ' + e.output)