def master_conf_initdb(): file = DaemonConfigParser(MASTER_CONF_FILE_TO) # mysql password password = "".join( random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128) ) password = sha1(password.encode("utf-8")) file.writeValueFromSection("mysql", "password", password.hexdigest()) os.system( "sed -i \"s/define('DB_PASSWORD', 'domoleaf')/define('DB_PASSWORD', '" + password.hexdigest() + "')/g\" /etc/domoleaf/www/config.php" ) # mysql user query1 = 'DELETE FROM user WHERE User="******"' query2 = 'DELETE FROM db WHERE User="******"' query3 = ( "INSERT INTO user (Host, User, Password) VALUES ('%', 'domoleaf', PASSWORD('" + password.hexdigest() + "'));" ) query4 = 'INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ("%","domoleaf","domoleaf","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y");' query5 = "FLUSH PRIVILEGES" Popen( ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query1], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1, ) Popen( ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query2], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1, ) Popen( ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query3], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1, ) Popen( ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query4], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1, ) Popen( ["mysql", "--defaults-file=/etc/mysql/debian.cnf", "mysql", "-e", query5], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1, )
def slave_conf_init(): file = DaemonConfigParser(SLAVE_CONF_FILE_TO); #KEY KEY = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128)) KEY = md5(KEY.encode('utf-8')).hexdigest() file.writeValueFromSection('personnal_key', 'aes', KEY); #KNX Interface knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b ' if os.path.exists('/dev/ttyAMA0'): knx_edit = knx_edit + 'tpuarts:/dev/ttyAMA0"'; elif os.path.exists('/dev/ttyS0'): knx_edit = knx_edit + 'tpuarts:/dev/ttyS0"'; else: knx_edit = knx_edit + 'ipt:127.0.0.1"'; conf_knx = open('/etc/knxd.conf', 'w'); conf_knx.write(knx_edit + '\n'); conf_knx.close();
def master_conf_initdb(): file = DaemonConfigParser(MASTER_CONF_FILE_TO); #mysql password password = ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128)) password = sha1(password.encode('utf-8')) file.writeValueFromSection('mysql', 'password', password.hexdigest()); os.system('sed -i "s/define(\'DB_PASSWORD\', \'domoleaf\')/define(\'DB_PASSWORD\', \''+password.hexdigest()+'\')/g" /etc/domoleaf/www/config.php') #mysql user query1 = 'DELETE FROM user WHERE User="******"'; query2 = 'DELETE FROM db WHERE User="******"'; query3 = 'INSERT INTO user (Host, User, Password) VALUES (\'%\', \'domoleaf\', PASSWORD(\''+password.hexdigest()+'\'));'; query4 = 'INSERT INTO db (Host, Db, User, Select_priv, Insert_priv, Update_priv, Delete_priv, Create_priv, Drop_priv, Grant_priv, References_priv, Index_priv, Alter_priv, Create_tmp_table_priv, Lock_tables_priv, Create_view_priv, Show_view_priv, Create_routine_priv, Alter_routine_priv, Execute_priv, Event_priv, Trigger_priv) VALUES ("%","domoleaf","domoleaf","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y");'; query5 = 'FLUSH PRIVILEGES'; Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query1], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1); Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query2], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1); Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query3], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1); Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query4], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1); Popen(['mysql', '--defaults-file=/etc/mysql/debian.cnf', 'mysql', '-e', query5], stdin=PIPE, stdout=PIPE, stderr=PIPE, bufsize=-1);
def slave_conf_init(): file = DaemonConfigParser(SLAVE_CONF_FILE_TO) #KEY KEY = ''.join( random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(128)) KEY = md5(KEY.encode('utf-8')).hexdigest() file.writeValueFromSection('personnal_key', 'aes', KEY) #KNX Interface knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b ' if os.path.exists('/dev/ttyAMA0'): knx_edit = knx_edit + 'tpuarts:/dev/ttyAMA0"' elif os.path.exists('/dev/ttyS0'): knx_edit = knx_edit + 'tpuarts:/dev/ttyS0"' else: knx_edit = knx_edit + 'ipt:127.0.0.1"' conf_knx = open('/etc/knxd.conf', 'w') conf_knx.write(knx_edit + '\n') conf_knx.close()
class SlaveDaemon: """ Main slave class It does communication between different monitors (KNX, EnOcean... in C) and the masters (servers) """ def __init__(self, log_flag): self.logger = Logger(log_flag, LOG_FILE); self.logger.info('Started Domoleaf Slave daemon'); print('######## SLAVE DAEMON #######') self.connected_masters = {}; self.connected_knx = []; self.connected_enocean = []; self.connected_cron = []; self.clients = []; self._scanner = Scanner(); self._hostlist = []; myhostname = socket.gethostname().upper() if SLAVE_NAME_PREFIX in myhostname: self._scanner.scan(); self._hostlist = self._scanner._HostList; else: self._hostlist.append(Host('', '127.0.0.1', myhostname)); self._parser = DaemonConfigParser(SLAVE_CONF_FILE); self.encrypt_keys = {}; self.knx_sock = None; self.master_sock = None; self.enocean_sock = None; self.cron_sock = None; self.private_aes = self._parser.getValueFromSection('personnal_key', 'aes'); self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'), self._parser.getValueFromSection('wifi', 'password'), self._parser.getValueFromSection('wifi', 'encryption'), self._parser.getValueFromSection('wifi', 'mode'), 0); self.connect_port = self._parser.getValueFromSection(SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY); self.functions = { KNX_READ_REQUEST : self.knx_read_request, KNX_WRITE_SHORT : self.knx_write_short, KNX_WRITE_LONG : self.knx_write_long, KNX_WRITE_TEMP : self.knx_write_temp, CHECK_SLAVE : self.check_slave, MONITOR_IP : self.monitor_ip, DATA_UPDATE : self.update, SEND_TECH : self.send_tech, SEND_ALIVE : self.send_alive, SEND_INTERFACES : self.send_interfaces, SHUTDOWN_D3 : self.shutdown_d3, REBOOT_D3 : self.reboot_d3, WIFI_UPDATE : self.wifi_update }; def update(self, json_obj, connection): p = call(['dpkg', '--configure', '-a']) call(['apt-get', 'update']); call(['DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', 'domoslave', '-y']); version = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0]; json_str = '{"packet_type": "update_finished", "aes_pass": "******", "new_version": ' + version + '}'; encrypt_IV = AESManager.get_IV(); spaces = 16 - len(json_str) % 16; json_str = json_str + (spaces * ' '); encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV); data = encode_obj.encrypt(json_str); # faut ouvrir une nouvelle socket pour envoyer la nouvelle version # connection.send(bytes(encrypt_IV, 'utf-8') + data); def run(self): """ Initialization of the sockets for listenning incomming connections. Calls the loop function. """ self.run = True; self.knx_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); self.enocean_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); self.cron_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); self.knx_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1); self.master_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1); self.enocean_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1); self.cron_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1); port = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_PORT_ENTRY); if not port: sys.exit(2); port_master = self._parser.getValueFromSection(SLAVE_CONF_LISTEN_SECTION, SLAVE_CONF_LISTEN_PORT_ENTRY); if not port_master: sys.exit(2); port_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_PORT_ENTRY); if not port_enocean: sys.exit(2); port_cron = self._parser.getValueFromSection(SLAVE_CONF_CRON_SECTION, SLAVE_CONF_CRON_PORT_ENTRY); if not port_cron: sys.exit(2); self.knx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); self.master_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); self.enocean_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); self.cron_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1); self.knx_sock.bind(('', int(port))); self.master_sock.bind(('', int(port_master))); self.enocean_sock.bind(('', int(port_enocean))); self.cron_sock.bind(('127.0.0.1', int(port_cron))); self.knx_sock.listen(MAX_KNX); self.master_sock.listen(MAX_MASTERS); self.enocean_sock.listen(MAX_ENOCEAN); self.cron_sock.listen(MAX_CRON); self.send_monitor_ip() self.loop(); def accept_knx(self): """ Get available sockets for reading on the KNX socket. """ rlist, wlist, elist = select.select([self.knx_sock], [], [], SELECT_TIMEOUT); append = self.connected_knx.append for connection in rlist: new_knx, addr = connection.accept(); append(new_knx); self.receive_from_knx(self.connected_knx); def accept_masters(self): """ Get available sockets for reading on the master socket. """ rlist, wlist, elist = select.select([self.master_sock], [], [], SELECT_TIMEOUT); masters_socks = []; append = masters_socks.append; for item in rlist: new_conn, addr = item.accept(); append(new_conn); self.receive_from_masters(masters_socks); def accept_enocean(self): """ Get available sockets for reading on the EnOcean socket. """ rlist, wlist, elist = select.select([self.enocean_sock], [], [], SELECT_TIMEOUT); enocean_socks = []; append = enocean_socks.append; append_connected = self.connected_enocean.append; for item in rlist: new_conn, addr = item.accept(); append(new_conn); append_connected(new_conn); self.receive_from_enocean(enocean_socks); def accept_cron(self): """ Get available sockets for reading on the Cron socket. """ rlist, wlist, elist = select.select([self.cron_sock], [], [], SELECT_TIMEOUT); cron_socks = []; append = cron_socks.append; append_connected = self.connected_cron.append; for item in rlist: new_conn, addr = item.accept(); append(new_conn); append_connected(new_conn); self.receive_from_cron(cron_socks); def parse_data(self, data, connection): """ Calls the wanted function with the packet_type described in 'data' (JSON syntax) """ json_obj = json.JSONDecoder().decode(data); if json_obj['packet_type'] in self.functions.keys(): self.functions[json_obj['packet_type']](json_obj, connection); else: raise Exception(str(json_obj['packet_type'])+": is not a valid packet type"); def knx_read_request(self, json_obj, connection): """ System call of "groupread" with parameters. """ call(['knxtool', CALL_GROUPREAD, EIB_URL, json_obj['addr_to_read']]); def knx_write_temp(self, json_obj, connection): """ System call of "groupwrite" with parameters. Almost the same as "knx_write_long" function, except that parameters are not the same """ val = json_obj['value'].split(' ') call(['knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], val[0], val[1]]); def knx_write_short(self, json_obj, connection): """ System call of "groupswrite" with parameters. """ call(['knxtool', CALL_GROUPSWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value'])]); def knx_write_long(self, json_obj, connection): """ System call of "groupwrite" with parameters. """ call(['knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value'])]); def receive_from_masters(self, masters_to_read): """ Read data comming from masters and call "parse_data" function. """ for master in masters_to_read: data = master.recv(4096); decrypt_IV = data[:16].decode(); decode_obj = AES.new(self.private_aes, AES.MODE_CBC, decrypt_IV); data2 = decode_obj.decrypt(data[16:]); self.parse_data(data2.decode(), master); master.close(); def receive_from_knx(self, knx_to_read): """ Read data from monitor KNX and transmits to master. """ for knx in knx_to_read: data = knx.recv(TELEGRAM_LENGTH); if data: self.send_knx_data_to_masters(data); if knx in self.connected_knx: knx.close(); self.connected_knx.remove(knx); def receive_from_enocean(self, enocean_to_read): """ Read data from monitor EnOcean and transmits to master. """ for enocean in enocean_to_read: data = enocean.recv(4096); if data: self.send_enocean_data_to_masters(data); if enocean in self.connected_enocean: enocean.close(); self.connected_enocean.remove(enocean); def receive_from_cron(self, cron_to_read): """ Receive data from Cron and execute it. """ for cron in cron_to_read: data = cron.recv(4096); if data: json_str = json.JSONEncoder().encode( { "packet_type": data.decode() } ); self.parse_data(json_str, cron); if cron in self.connected_cron: cron.close(); self.connected_cron.remove(cron); def check_slave(self, json_obj, connection): """ Callback called each time a check_slave packet is received. Used to confirm the existence of this daemon. """ interface_knx = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_INTERFACE); interface_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_INTERFACE); version = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0]; json_str = '{"packet_type": "check_slave", "aes_pass": "******", "version": "' + version + '", "interface_knx": "' + interface_knx + '", "interface_enocean": "' + interface_enocean + '"}'; master_hostname = str(json_obj['sender_name']); encrypt_IV = AESManager.get_IV(); spaces = 16 - len(json_str) % 16; json_str = json_str + (spaces * ' '); encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV); data = encode_obj.encrypt(json_str); connection.send(bytes(encrypt_IV, 'utf-8') + data); def monitor_ip(self, json_obj, connection): """ Re scan the local network to refresh hostlist. """ self._scanner.scan(); self._hostlist = self._scanner._HostList; def send_monitor_ip(self): json_str = json.JSONEncoder().encode( { "packet_type": "monitor_ip" } ); self.send_data_to_all_masters(json_str); def loop(self): """ Main daemon loop. """ while self.run: try: self.accept_knx(); except Exception as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop accept_knx: '+str(e)); print('in loop accept_knx: ',str(e)); except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop: Keyboard interrupt'); try: self.accept_masters(); except Exception as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop accept_masters: '+str(e)); print('in loop accept_masters: ',str(e)); except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop: Keyboard interrupt'); try: self.accept_enocean(); except Exception as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop accept_enocean: '+str(e)); print('in loop accept_enocean: ',str(e)); except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop: Keyboard interrupt'); try: self.accept_cron(); except Exception as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop accept_cron: '+str(e)); print('in loop accept_cron: ',str(e)); except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in loop: Keyboard interrupt'); def stop(self): """ Stop the daemon and closes all sockets. """ for name, sock in self.connected_masters.items(): sock.close(); for knx in self.connected_knx: knx.close(); self.knx_sock.close(); def connect_to_masters(self): """ Stored every device on network which have his hostname beginning by "MD3" and stores it in the self.connected_masters dict(), with hostnames as keys and sockets freshly open as values. """ hostname = socket.gethostname() self.connected_masters = {}; for host in self._hostlist: if MASTER_NAME_PREFIX in host._Hostname or str(host._IpAddr) == '127.0.0.1': if not self.connect_port: self.logger.error('in connect_to_masters: No '+SLAVE_CONF_CONNECT_PORT_ENTRY+' in '+SLAVE_CONF_CONNECT_SECTION+' section or maybe no such '+SLAVE_CONF_CONNECT_SECTION+' defined'); sys.exit(1); try: self.logger.debug('Connecting to '+str(host._IpAddr)+':'+str(self.connect_port)); sock = socket.create_connection((host._IpAddr, self.connect_port)); hostname = host._Hostname.split('.')[0]; self.connected_masters[host._Hostname] = sock; except Exception as e: frameinfo = getframeinfo(currentframe()); self.logger.error('in connect_to_masters: '+str(e)); pass; def send_knx_data_to_masters(self, data): """ Converts 'data' from bytes to a clear KNX datagran, and sends it to available slaves. """ ctrl = int(data[0]); src_addr = int.from_bytes(data[1:3], byteorder='big'); dst_addr = int.from_bytes(data[3:5], byteorder='big'); data_len = int.from_bytes(data[5:6], byteorder='big'); telegram_data = data[6:7 + data_len]; typ = -1; value = 0; if telegram_data[1] & 0xC0 == 0x00: # read typ = 0; elif telegram_data[1] & 0xC0 == 0x40: # resp typ = 1; if data_len == 2: value = int(telegram_data[1] & 0x0f); elif data_len > 2: value = int.from_bytes(telegram_data[2:data_len], byteorder='big'); elif telegram_data[1] & 0xC0 == 0x80: # write typ = 2; if data_len == 2: value = int(telegram_data[1] & 0x0f); elif data_len > 2: typ = 3; value = int.from_bytes(telegram_data[2:data_len], byteorder='big'); json_str = json.JSONEncoder().encode( { "packet_type": "monitor_knx", "type": typ, "src_addr": individual2string(src_addr), "dst_addr": group2string(dst_addr), "date": str(time.time()).split('.')[0], "value": value, "sender_name": socket.gethostname() } ); self.send_data_to_all_masters(json_str); def send_enocean_data_to_masters(self, data): """ Converts 'data' from bytes to a clear EnOcean datagran, and sends it to available slaves. """ if (data[4] == PACKET_TYPE_RADIO_ERP1): # si le packet_type == radio_erp1 data_len = int.from_bytes(data[1:2], byteorder='big'); opt_data_len = int(data[3]); src_str = "%X" % int.from_bytes(data[1+data_len:5+data_len], byteorder='big'); if len(src_str) < 8: src_str = "0"+src_str; json_dict = { "packet_type": "monitor_enocean", "src_addr": src_str, "dst_addr": "%X" % int.from_bytes(data[261:265 + opt_data_len], byteorder='big'), "date": str(time.time()).split('.')[0], "sender_name": socket.gethostname(), "type": int(data[6]) }; if data[6] == RORG_NORMAL: json_dict['value'] = int(data[7]); elif data[6] == RORG_TEMPERATURE: json_dict['value'] = float(40 - ((40 / 255) * int(data[9]))); json_str = json.JSONEncoder().encode(json_dict); self.send_data_to_all_masters(json_str); def send_data_to_all_masters(self, json_str): """ Sends a string 'json_str' to available slaves on network. """ self.connect_to_masters(); # ici envoyer a tous les masters for name in self.connected_masters.keys(): try: master = self.connected_masters[name]; AES.key_size = 32; aes_IV = AESManager.get_IV(); encode_obj = AES.new(self.private_aes, AES.MODE_CBC, aes_IV); spaces = 16 - len(json_str) % 16; data2 = encode_obj.encrypt(json_str + (spaces * ' ')); master.send(bytes(aes_IV, 'utf-8') + data2); master.close(); except KeyError as e: self.logger.error('in send_data_to_all_masters: '+str(e)); print(e); pass; def send_tech(self, json_obj, connection): json_str = json.JSONEncoder().encode( { "packet_type": "send_tech", "info": GLManager.TechInfo() } ); self.send_data_to_all_masters(json_str); def send_alive(self, json_obj, connection): json_str = json.JSONEncoder().encode( { "packet_type": "send_alive", "info": GLManager.TechAlive() } ); self.send_data_to_all_masters(json_str); def send_interfaces(self, json_obj, connection): try: if os.path.exists('/tmp/knxd'): call(['service', 'knxd', 'stop']); previous_val_knx = self._parser.getValueFromSection('knx', 'interface'); previous_val_EnOcean = self._parser.getValueFromSection('enocean', 'interface'); new_val = str(json_obj['interface_arg_knx']) self._parser.writeValueFromSection('knx', 'interface', new_val); self._parser.writeValueFromSection('knx', 'activated', str(json_obj['daemon_knx'])); self._parser.writeValueFromSection('enocean', 'interface', str(json_obj['interface_arg_EnOcean'])); if not previous_val_knx or previous_val_knx is None: call(['update-rc.d', 'knxd', 'defaults']); call(['update-rc.d', 'knxd', 'enable']); if not new_val or new_val is None: Popen(['systemctl', '-q', 'disable', 'knxd']); else: knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b '; if json_obj['interface_knx'] == 'tpuarts': knx_edit += json_obj['interface_knx']+':/dev/'+new_val+'"'; else: knx_edit += json_obj['interface_knx']+':'+new_val+'"'; conf_knx = open('/etc/knxd.conf', 'w'); conf_knx.write(knx_edit+'\n'); conf_knx.close(); call(['service', 'knxd', 'start']); if json_obj['daemon_knx'] == 1: if os.path.exists('/var/run/monitor_knx.pid'): os.remove('/var/run/monitor_knx.pid'); Popen(['monitor_knx', 'ip:localhost', '--daemon']); except Exception as e: self.logger.error(e); json_str = '{"packet_type": "send_interfaces", "aes_pass": "******"}'; master_hostname = str(json_obj['sender_name']); encrypt_IV = AESManager.get_IV(); spaces = 16 - len(json_str) % 16; json_str = json_str + (spaces * ' '); encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV); data = encode_obj.encrypt(json_str); connection.send(bytes(encrypt_IV, 'utf-8') + data); if previous_val_EnOcean != str(json_obj['interface_arg_EnOcean']): call(['service', 'domoslave', 'restart']); def shutdown_d3(self, json_obj, connection): call(['poweroff']); def reboot_d3(self, json_obj, connection): call(['reboot']); def wifi_init(self, ssid, password, security, mode, opt): try: ps_process = Popen(["ps", "-x"], stdout=PIPE); res = Popen(["grep", "hostapd"], stdin=ps_process.stdout, stdout=PIPE); res = res.stdout.read().decode().split("\n")[0].split(' '); ps_process.stdout.close(); if res: while ('' in res): res.remove(''); call(['kill', '-9', res[0]]); ps_process = Popen(["ps", "-x"], stdout=PIPE); res = Popen(["grep", "wpa_supplicant"], stdin=ps_process.stdout, stdout=PIPE); res = res.stdout.read().decode().split("\n")[0].split(' '); ps_process.stdout.close(); if res: while ('' in res): res.remove(''); call(['kill', '-9', res[0]]); call(['ifconfig', 'wlan0', 'down']); if mode == WIFI_MODE_DISABLED: if opt == 1: call(['service', 'dnsmasq', 'stop']); elif mode == WIFI_MODE_CLIENT: call(['ifconfig', 'wlan0', 'up']); if opt == 1: call(['service', 'dnsmasq', 'stop']); conf_file = open('/etc/network/interfaces', 'w'); conf_str = ''.join(['auto lo\niface lo inet loopback\n\nallow-hotplug eth0\n', 'iface eth0 inet dhcp\n\nallow-hotplug usb0\niface usb0 inet dhcp\n\n', 'auto wlan0\niface wlan0 inet dhcp\n\twpa-conf ', WPA_SUPPLICANT_CONF_FILE, '\n']); conf_file.write(conf_str); conf_file.close(); conf_file = open(WPA_SUPPLICANT_CONF_FILE, 'w'); conf_str = ''.join(['ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n', 'update_config=1\nctrl_interface_group=0\neapol_version=1\n', 'ap_scan=1\n fast_reauth=1\n\n\nnetwork={\n\tdisabled=0\n', '\tssid="', ssid, '"\n\tscan_ssid=0\n\tpriority=1\n']); if security == WIFI_SECURITY_WPA: conf_str += ('\tproto=WPA\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n'+ '\tpairwise=TKIP CCMP\n\tgroup=TKIP CCMP\n\tpsk="'+password+'"\n'); elif security == WIFI_SECURITY_WPA2: conf_str += ('\tproto=RSN\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n\tpairwise=CCMP TKIP\n'+ '\tgroup=CCMP TKIP\n\tpsk="'+password+'"\n'); elif security == WIFI_SECURITY_WEP: conf_str += '\tkey_mgmt=NONE\n\tauth_alg=SHARED\n'; if len(password) == 5 or len(password) == 10: conf_str += '\tgroup=WEP40\n'; elif len(password) == 13 or len(password) == 26: conf_str += '\tgroup=WEP104\n'; else: conf_str += '\tgroup=WEP40 WEP104\n'; conf_str += '\twep_key0="'+password+'"\n\twep_tx_keyidx=0\n'; conf_str += '\tpriority=1\n}\n'; conf_file.write(conf_str); conf_file.close(); call(['wpa_supplicant', '-Dnl80211', '-iwlan0', '-c' + WPA_SUPPLICANT_CONF_FILE , '-B']); call(['dhclient', 'wlan0']); elif mode == WIFI_MODE_ACCESS_POINT: call(['ifconfig', 'wlan0', '172.16.0.1', 'netmask', '255.255.255.0', 'up']); conf_file = open(HOSTAPD_CONF_FILE, 'w'); conf_str = ''.join(['interface=wlan0\n\ndriver=nl80211\n\nssid=', ssid, '\n\n', 'hw_mode=g\n\nieee80211n=1\n\nchannel=6\n\nbeacon_int=100\n\n', 'dtim_period=2\n\nmax_num_sta=255\n\nrts_threshold=2347\n\n', 'fragm_threshold=2346\n\nmacaddr_acl=0\n\n']); if security == WIFI_SECURITY_WPA: conf_str += ('auth_algs=1\n\nwpa=1\n\nwpa_passphrase='+password+'\n\n'+ 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=TKIP\n'); elif security == WIFI_SECURITY_WPA2: conf_str += ('auth_algs=1\n\nwpa=2\n\nwpa_passphrase='+password+'\n\n'+ 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=CCMP\n\nrsn_pairwise=CCMP\n'); else: self.logger.error('Wifi security = Unknown'); conf_file.write(conf_str); conf_file.close(); if opt == 1: conf_file = open(DNSMASQ_CONF_FILE, 'w'); conf_str = 'domain-needed\ninterface=wlan0\ndhcp-range=172.16.0.2,172.16.0.254,12h\n'; conf_file.write(conf_str); conf_file.close(); call(['service', 'dnsmasq', 'restart']); call(['iptables', '-t', 'nat', '-A', 'POSTROUTING', '-j', 'MASQUERADE']); call(['hostapd', HOSTAPD_CONF_FILE, '-B']); else: call(['ifconfig', 'wlan0', 'up']); self.logger.error('Wifi mode = Unknown'); except Exception as e: self.logger.error(e); def wifi_update(self, json_obj, connection): try: self._parser.writeValueFromSection('wifi', 'ssid', json_obj['ssid']); self._parser.writeValueFromSection('wifi', 'password', json_obj['password']); self._parser.writeValueFromSection('wifi', 'encryption', json_obj['security']); self._parser.writeValueFromSection('wifi', 'mode', json_obj['mode']); self.wifi_init(json_obj['ssid'], json_obj['password'], json_obj['security'], json_obj['mode'], 1); except Exception as e: self.logger.error(e); json_str = '{"packet_type": "wifi_update", "aes_pass": "******"}'; master_hostname = str(json_obj['sender_name']); encrypt_IV = AESManager.get_IV(); spaces = 16 - len(json_str) % 16; json_str = json_str + (spaces * ' '); encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV); data = encode_obj.encrypt(json_str); connection.send(bytes(encrypt_IV, 'utf-8') + data);
class SlaveDaemon: ## The constructor # # @param log_flag The flag saying whether the logs should be printing or not def __init__(self, log_flag): ## Logger object for formatting and printing logs self.logger = Logger(log_flag, LOG_FILE) self.logger.info('Started Domoleaf Slave daemon') ## Array of master daemon on local network self.connected_masters = {} ## Array of monitor KNX on local network self.connected_knx = [] ## Array of monitor EnOcean on local network self.connected_enocean = [] ## Array of cron running on the system self.connected_cron = [] self._scanner = Scanner(log_flag) self._hostlist = [] myhostname = socket.gethostname().upper() if SLAVE_NAME_PREFIX in myhostname: self._scanner.scan() self._hostlist = self._scanner._HostList else: self._hostlist.append(Host('', '127.0.0.1', myhostname)) self._parser = DaemonConfigParser(SLAVE_CONF_FILE) ## Keys for encrypting communications self.encrypt_keys = {} ## Main socket for communication with KNX daemon self.knx_sock = None ## Main socket for communication with master daemon self.master_sock = None ## Main socket for communication with enocean daemon self.enocean_sock = None ## Main socket for communication with cron self.cron_sock = None ## Private AES key got from configuration file self.private_aes = self._parser.getValueFromSection( 'personnal_key', 'aes') self.wifi_init(self._parser.getValueFromSection('wifi', 'ssid'), self._parser.getValueFromSection('wifi', 'password'), self._parser.getValueFromSection('wifi', 'encryption'), self._parser.getValueFromSection('wifi', 'mode'), 0) ## Port on which connect got from configuration file self.connect_port = self._parser.getValueFromSection( SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY) ## Callback array indexed on packet type self.functions = { KNX_READ_REQUEST: self.knx_read_request, KNX_WRITE_SHORT: self.knx_write_short, KNX_WRITE_LONG: self.knx_write_long, KNX_WRITE_TEMP: self.knx_write_temp, CHECK_SLAVE: self.check_slave, MONITOR_IP: self.monitor_ip, DATA_UPDATE: self.update, SEND_TECH: self.send_tech, SEND_ALIVE: self.send_alive, SEND_INTERFACES: self.send_interfaces, SHUTDOWN_D3: self.shutdown_d3, REBOOT_D3: self.reboot_d3, WIFI_UPDATE: self.wifi_update } ## Updates the base system of the slave daemon. # # @param json_obj Not used here. # @param connection Not used here. # @return None def update(self, json_obj, connection): p = call(['dpkg', '--configure', '-a']) call(['apt-get', 'update']) call([ 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', 'domoslave', '-y' ]) version = os.popen( "dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0] json_str = '{"packet_type": "update_finished", "aes_pass": "******", "new_version": ' + version + '}' encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * ' ') encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) # faut ouvrir une nouvelle socket pour envoyer la nouvelle version # connection.send(bytes(encrypt_IV, 'utf-8') + data); ## Initializes the sockets for listenning incomming connections. # # @return None def run(self): ## Run flag at True for running, at False to stop the main loop self.run = True self.knx_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.enocean_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.cron_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.knx_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.master_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.enocean_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.cron_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) port = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_PORT_ENTRY) if not port: sys.exit(2) port_master = self._parser.getValueFromSection( SLAVE_CONF_LISTEN_SECTION, SLAVE_CONF_LISTEN_PORT_ENTRY) if not port_master: sys.exit(2) port_enocean = self._parser.getValueFromSection( SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_PORT_ENTRY) if not port_enocean: sys.exit(2) port_cron = self._parser.getValueFromSection( SLAVE_CONF_CRON_SECTION, SLAVE_CONF_CRON_PORT_ENTRY) if not port_cron: sys.exit(2) self.knx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.master_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.enocean_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.cron_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.knx_sock.bind(('', int(port))) self.master_sock.bind(('', int(port_master))) self.enocean_sock.bind(('', int(port_enocean))) self.cron_sock.bind(('127.0.0.1', int(port_cron))) self.knx_sock.listen(MAX_KNX) self.master_sock.listen(MAX_MASTERS) self.enocean_sock.listen(MAX_ENOCEAN) self.cron_sock.listen(MAX_CRON) self.send_monitor_ip() self.loop() ## Gets available sockets for reading on the KNX socket. # # @return None def accept_knx(self): rlist, wlist, elist = select.select([self.knx_sock], [], [], SELECT_TIMEOUT) append = self.connected_knx.append for connection in rlist: new_knx, addr = connection.accept() append(new_knx) self.receive_from_knx(self.connected_knx) ## Gets available sockets for reading on the master socket. # # @return None def accept_masters(self): rlist, wlist, elist = select.select([self.master_sock], [], [], SELECT_TIMEOUT) masters_socks = [] append = masters_socks.append for item in rlist: new_conn, addr = item.accept() append(new_conn) self.receive_from_masters(masters_socks) ## Gets available sockets for reading on the EnOcean socket. # # @return None def accept_enocean(self): rlist, wlist, elist = select.select([self.enocean_sock], [], [], SELECT_TIMEOUT) enocean_socks = [] append = enocean_socks.append append_connected = self.connected_enocean.append for item in rlist: new_conn, addr = item.accept() append(new_conn) append_connected(new_conn) self.receive_from_enocean(enocean_socks) ## Gets available sockets for reading on the Cron socket. # # @return None def accept_cron(self): rlist, wlist, elist = select.select([self.cron_sock], [], [], SELECT_TIMEOUT) cron_socks = [] append = cron_socks.append append_connected = self.connected_cron.append for item in rlist: new_conn, addr = item.accept() append(new_conn) append_connected(new_conn) self.receive_from_cron(cron_socks) ## Checks the packet_type of the data, and calls the appropriate function. # # @param data Packet data including the packet type. # @param connection Connection object sent to the function. # @return None def parse_data(self, data, connection): json_obj = json.JSONDecoder().decode(data) if json_obj['packet_type'] in self.functions.keys(): self.functions[json_obj['packet_type']](json_obj, connection) else: raise Exception( str(json_obj['packet_type']) + ": is not a valid packet type") ## System call of 'groupread' with parameters. # # @param json_obj JSON Object containing the address to read. # @param connection Not used here. # @return None def knx_read_request(self, json_obj, connection): call(['knxtool', CALL_GROUPREAD, EIB_URL, json_obj['addr_to_read']]) ## System call of "groupwrite" with parameters. # # @param json_obj JSON Object containing the address to which send, and the values to send. # @param connection Not used here. # @return None def knx_write_temp(self, json_obj, connection): val = json_obj['value'].split(' ') call([ 'knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], val[0], val[1] ]) ## System call of "groupswrite" with parameters. # # @param json_obj JSON Object containing the address to which send, and the value to send. # @param connection Not used here. # @return None def knx_write_short(self, json_obj, connection): call([ 'knxtool', CALL_GROUPSWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value']) ]) ## System call of "groupwrite" with parameters. # # @param json_obj JSON Object containing the address to which send, and the value to send. # @param connection Not used here. # @return None def knx_write_long(self, json_obj, connection): call([ 'knxtool', CALL_GROUPWRITE, EIB_URL, json_obj['addr_to_send'], str(json_obj['value']) ]) ## Reads data comming from amsters and calls parse_data(). # # @param masters_to_read Array containing the sockets of all the masters found on local network. # @return None def receive_from_masters(self, masters_to_read): for master in masters_to_read: data = master.recv(4096) decrypt_IV = data[:16].decode() decode_obj = AES.new(self.private_aes, AES.MODE_CBC, decrypt_IV) data2 = decode_obj.decrypt(data[16:]) self.parse_data(data2.decode(), master) master.close() ## Reads data from monitor KNX and transmits to master. # # @param knx_to_read Array containing the sockets of all the KNX daemons on local network. # @return None def receive_from_knx(self, knx_to_read): for knx in knx_to_read: data = knx.recv(TELEGRAM_LENGTH) if data: self.send_knx_data_to_masters(data) if knx in self.connected_knx: knx.close() self.connected_knx.remove(knx) ## Reads data from monitor EnOcean and transmits to master. # # @param enocean_to_read Array containing the sockets of all the EnOcean daemons on local network. # @return None def receive_from_enocean(self, enocean_to_read): for enocean in enocean_to_read: data = enocean.recv(4096) if data: self.send_enocean_data_to_masters(data) if enocean in self.connected_enocean: enocean.close() self.connected_enocean.remove(enocean) ## Receives data from a cron and executes it. # # @param cron_to_read Array containing the sockets of all crons on local network. # @return None def receive_from_cron(self, cron_to_read): for cron in cron_to_read: data = cron.recv(4096) if data: json_str = json.JSONEncoder().encode( {"packet_type": data.decode()}) self.parse_data(json_str, cron) if cron in self.connected_cron: cron.close() self.connected_cron.remove(cron) ## Checks the existence of this daemon. # This function is called when a check_slave packet is received. # # @param json_obj JSON Object containing the hostname of the sender of the packet. # @param connection Connection object used to send the response. # @return None def check_slave(self, json_obj, connection): interface_knx = self._parser.getValueFromSection( SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_INTERFACE) interface_enocean = self._parser.getValueFromSection( SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_INTERFACE) version = os.popen( "dpkg-query -W -f='${Version}\n' domoslave").read().split('\n')[0] json_str = '{"packet_type": "check_slave", "aes_pass": "******", "version": "' + version + '", "interface_knx": "' + interface_knx + '", "interface_enocean": "' + interface_enocean + '"}' master_hostname = str(json_obj['sender_name']) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * ' ') encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) connection.send(bytes(encrypt_IV, 'utf-8') + data) ## Re scans the local network and refreshes hostlist. # # @param json_obj Not used here. # @param connection Not used here. # @return None def monitor_ip(self, json_obj, connection): self._scanner.scan() self._hostlist = self._scanner._HostList ## Sends a monitor_ip packet to all the masters available. # # @return None def send_monitor_ip(self): json_str = json.JSONEncoder().encode({"packet_type": "monitor_ip"}) self.send_data_to_all_masters(json_str) ## Main daemon loop. # # @return None def loop(self): while self.run: try: self.accept_knx() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop accept_knx: ' + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop: Keyboard interrupt') try: self.accept_masters() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop accept_masters: ' + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop: Keyboard interrupt') try: self.accept_enocean() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop accept_enocean: ' + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop: Keyboard interrupt') try: self.accept_cron() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop accept_cron: ' + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in loop: Keyboard interrupt') ## Stops the daemon and closes all sockets. # # @return None def stop(self): for name, sock in self.connected_masters.items(): sock.close() for knx in self.connected_knx: knx.close() self.knx_sock.close() ## Stores every host on local network if its hostname begins by 'MD3' in connected_masters dict(). # # @return None def connect_to_masters(self): hostname = socket.gethostname() self.connected_masters = {} for host in self._hostlist: if MASTER_NAME_PREFIX in host._Hostname or str( host._IpAddr) == '127.0.0.1': if not self.connect_port: self.logger.error('in connect_to_masters: No ' + SLAVE_CONF_CONNECT_PORT_ENTRY + ' in ' + SLAVE_CONF_CONNECT_SECTION + ' section or maybe no such ' + SLAVE_CONF_CONNECT_SECTION + ' defined') sys.exit(1) try: self.logger.debug('Connecting to ' + str(host._IpAddr) + ':' + str(self.connect_port)) sock = socket.create_connection( (host._IpAddr, self.connect_port)) hostname = host._Hostname.split('.')[0] self.connected_masters[host._Hostname] = sock except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error('in connect_to_masters: ' + str(e)) pass ## Converts data from bytes to a clear KNX datagram, and sends it to all available masters. # # @param data The data having to be converted from bytes to clear KNX datagram. # @return None def send_knx_data_to_masters(self, data): ctrl = int(data[0]) src_addr = int.from_bytes(data[1:3], byteorder='big') dst_addr = int.from_bytes(data[3:5], byteorder='big') data_len = int.from_bytes(data[5:6], byteorder='big') telegram_data = data[6:7 + data_len] typ = -1 value = 0 if telegram_data[1] & 0xC0 == 0x00: # read typ = 0 elif telegram_data[1] & 0xC0 == 0x40: # resp typ = 1 if data_len == 2: value = int(telegram_data[1] & 0x0f) elif data_len > 2: value = int.from_bytes(telegram_data[2:data_len], byteorder='big') elif telegram_data[1] & 0xC0 == 0x80: # write typ = 2 if data_len == 2: value = int(telegram_data[1] & 0x0f) elif data_len > 2: typ = 3 value = int.from_bytes(telegram_data[2:data_len], byteorder='big') json_str = json.JSONEncoder().encode({ "packet_type": "monitor_knx", "type": typ, "src_addr": individual2string(src_addr), "dst_addr": group2string(dst_addr), "date": str(time.time()).split('.')[0], "value": value, "sender_name": socket.gethostname() }) self.send_data_to_all_masters(json_str) ## Convertes data from bytes to a clear EnOcean datagram, and sends it to available masters. # # @param data The data having to be converted from bytes to EnOcean datagram. # @return None def send_enocean_data_to_masters(self, data): if (data[4] == PACKET_TYPE_RADIO_ERP1 ): # si le packet_type == radio_erp1 data_len = int.from_bytes(data[1:2], byteorder='big') opt_data_len = int(data[3]) src_str = "%X" % int.from_bytes(data[1 + data_len:5 + data_len], byteorder='big') if len(src_str) < 8: src_str = "0" + src_str json_dict = { "packet_type": "monitor_enocean", "src_addr": src_str, "dst_addr": "%X" % int.from_bytes(data[261:265 + opt_data_len], byteorder='big'), "date": str(time.time()).split('.')[0], "sender_name": socket.gethostname(), "type": int(data[6]) } if data[6] == RORG_NORMAL: json_dict['value'] = int(data[7]) elif data[6] == RORG_TEMPERATURE: json_dict['value'] = float(40 - ((40 / 255) * int(data[9]))) json_str = json.JSONEncoder().encode(json_dict) self.send_data_to_all_masters(json_str) ## Sends data to all masters available on local network. # # @param json_str The data to send under form of a JSON Object stringified. # @return None def send_data_to_all_masters(self, json_str): self.connect_to_masters() for name in self.connected_masters.keys(): try: master = self.connected_masters[name] AES.key_size = 32 aes_IV = AESManager.get_IV() encode_obj = AES.new(self.private_aes, AES.MODE_CBC, aes_IV) spaces = 16 - len(json_str) % 16 data2 = encode_obj.encrypt(json_str + (spaces * ' ')) master.send(bytes(aes_IV, 'utf-8') + data2) master.close() except KeyError as e: self.logger.error('in send_data_to_all_masters: ' + str(e)) pass ## Sends the informations about the slave to all masters available. # # @param json_obj Not used here. # @param connection Not used here. # @return None def send_tech(self, json_obj, connection): json_str = json.JSONEncoder().encode({ "packet_type": "send_tech", "info": GLManager.TechInfo() }) self.send_data_to_all_masters(json_str) ## Sends that the salve daemon is alive to all masters available. # # @param json_obj Not used here. # @param connection Not used here. # @return None def send_alive(self, json_obj, connection): json_str = json.JSONEncoder().encode({ "packet_type": "send_alive", "info": GLManager.TechAlive() }) self.send_data_to_all_masters(json_str) ## Sends the protocol interface informations to all masters available. # # @param json_obj Protocol interface informations. # @param connection Connection object used to send the response. # @return None def send_interfaces(self, json_obj, connection): try: if os.path.exists('/tmp/knxd'): call(['service', 'knxd', 'stop']) previous_val_knx = self._parser.getValueFromSection( 'knx', 'interface') previous_val_EnOcean = self._parser.getValueFromSection( 'enocean', 'interface') new_val = str(json_obj['interface_arg_knx']) self._parser.writeValueFromSection('knx', 'interface', new_val) self._parser.writeValueFromSection('knx', 'activated', str(json_obj['daemon_knx'])) self._parser.writeValueFromSection( 'enocean', 'interface', str(json_obj['interface_arg_EnOcean'])) if not previous_val_knx or previous_val_knx is None: call(['update-rc.d', 'knxd', 'defaults']) call(['update-rc.d', 'knxd', 'enable']) if not new_val or new_val is None: Popen(['systemctl', '-q', 'disable', 'knxd']) else: knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b ' if json_obj['interface_knx'] == 'tpuarts': knx_edit += json_obj[ 'interface_knx'] + ':/dev/' + new_val + '"' else: knx_edit += json_obj['interface_knx'] + ':' + new_val + '"' conf_knx = open('/etc/knxd.conf', 'w') conf_knx.write(knx_edit + '\n') conf_knx.close() call(['service', 'knxd', 'start']) if json_obj['daemon_knx'] == 1: if os.path.exists('/var/run/monitor_knx.pid'): os.remove('/var/run/monitor_knx.pid') Popen(['monitor_knx', 'ip:localhost', '--daemon']) except Exception as e: self.logger.error(e) json_str = '{"packet_type": "send_interfaces", "aes_pass": "******"}' master_hostname = str(json_obj['sender_name']) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * ' ') encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) connection.send(bytes(encrypt_IV, 'utf-8') + data) if previous_val_EnOcean != str(json_obj['interface_arg_EnOcean']): call(['service', 'domoslave', 'restart']) ## Shuts down the slave D3. # # @param json_obj Not used here. # @param connection Not used here. # @return None def shutdown_d3(self, json_obj, connection): call(['poweroff']) ## Reboots the slave D3. # # @param json_obj Not used here. # @param connection Not used here. # @return None def reboot_d3(self, json_obj, connection): call(['reboot']) ## Initializes the wifi protocol. # # @param ssid The SSID of the network. # @param password The password to connect to the network. # @param security The security type of the connection. # @param mode The mode of the initialization of the network interface. # @param opt Flag to do some more stuff if it is 1. # @return None def wifi_init(self, ssid, password, security, mode, opt): try: ps_process = Popen(["ps", "-x"], stdout=PIPE) res = Popen(["grep", "hostapd"], stdin=ps_process.stdout, stdout=PIPE) res = res.stdout.read().decode().split("\n")[0].split(' ') ps_process.stdout.close() if res: while ('' in res): res.remove('') call(['kill', '-9', res[0]]) ps_process = Popen(["ps", "-x"], stdout=PIPE) res = Popen(["grep", "wpa_supplicant"], stdin=ps_process.stdout, stdout=PIPE) res = res.stdout.read().decode().split("\n")[0].split(' ') ps_process.stdout.close() if res: while ('' in res): res.remove('') call(['kill', '-9', res[0]]) call(['ifconfig', 'wlan0', 'down']) if mode == WIFI_MODE_DISABLED: if opt == 1: call(['service', 'dnsmasq', 'stop']) elif mode == WIFI_MODE_CLIENT: call(['ifconfig', 'wlan0', 'up']) if opt == 1: call(['service', 'dnsmasq', 'stop']) conf_file = open('/etc/network/interfaces', 'w') conf_str = ''.join([ 'auto lo\niface lo inet loopback\n\nallow-hotplug eth0\n', 'iface eth0 inet dhcp\n\nallow-hotplug usb0\niface usb0 inet dhcp\n\n', 'auto wlan0\niface wlan0 inet dhcp\n\twpa-conf ', WPA_SUPPLICANT_CONF_FILE, '\n' ]) conf_file.write(conf_str) conf_file.close() conf_file = open(WPA_SUPPLICANT_CONF_FILE, 'w') conf_str = ''.join([ 'ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n', 'update_config=1\nctrl_interface_group=0\neapol_version=1\n', 'ap_scan=1\n fast_reauth=1\n\n\nnetwork={\n\tdisabled=0\n', '\tssid="', ssid, '"\n\tscan_ssid=0\n\tpriority=1\n' ]) if security == WIFI_SECURITY_WPA: conf_str += ( '\tproto=WPA\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n' + '\tpairwise=TKIP CCMP\n\tgroup=TKIP CCMP\n\tpsk="' + password + '"\n') elif security == WIFI_SECURITY_WPA2: conf_str += ( '\tproto=RSN\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n\tpairwise=CCMP TKIP\n' + '\tgroup=CCMP TKIP\n\tpsk="' + password + '"\n') elif security == WIFI_SECURITY_WEP: conf_str += '\tkey_mgmt=NONE\n\tauth_alg=SHARED\n' if len(password) == 5 or len(password) == 10: conf_str += '\tgroup=WEP40\n' elif len(password) == 13 or len(password) == 26: conf_str += '\tgroup=WEP104\n' else: conf_str += '\tgroup=WEP40 WEP104\n' conf_str += '\twep_key0="' + password + '"\n\twep_tx_keyidx=0\n' conf_str += '\tpriority=1\n}\n' conf_file.write(conf_str) conf_file.close() call([ 'wpa_supplicant', '-Dnl80211', '-iwlan0', '-c' + WPA_SUPPLICANT_CONF_FILE, '-B' ]) call(['dhclient', 'wlan0']) elif mode == WIFI_MODE_ACCESS_POINT: call([ 'ifconfig', 'wlan0', '172.16.0.1', 'netmask', '255.255.255.0', 'up' ]) conf_file = open(HOSTAPD_CONF_FILE, 'w') conf_str = ''.join([ 'interface=wlan0\n\ndriver=nl80211\n\nssid=', ssid, '\n\n', 'hw_mode=g\n\nieee80211n=1\n\nchannel=6\n\nbeacon_int=100\n\n', 'dtim_period=2\n\nmax_num_sta=255\n\nrts_threshold=2347\n\n', 'fragm_threshold=2346\n\nmacaddr_acl=0\n\n' ]) if security == WIFI_SECURITY_WPA: conf_str += ('auth_algs=1\n\nwpa=1\n\nwpa_passphrase=' + password + '\n\n' + 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=TKIP\n') elif security == WIFI_SECURITY_WPA2: conf_str += ( 'auth_algs=1\n\nwpa=2\n\nwpa_passphrase=' + password + '\n\n' + 'wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=CCMP\n\nrsn_pairwise=CCMP\n' ) else: self.logger.error('Wifi security = Unknown') conf_file.write(conf_str) conf_file.close() if opt == 1: conf_file = open(DNSMASQ_CONF_FILE, 'w') conf_str = 'domain-needed\ninterface=wlan0\ndhcp-range=172.16.0.2,172.16.0.254,12h\n' conf_file.write(conf_str) conf_file.close() call(['service', 'dnsmasq', 'restart']) call([ 'iptables', '-t', 'nat', '-A', 'POSTROUTING', '-j', 'MASQUERADE' ]) call(['hostapd', HOSTAPD_CONF_FILE, '-B']) else: call(['ifconfig', 'wlan0', 'up']) self.logger.error('Wifi mode = Unknown') except Exception as e: self.logger.error(e) ## Updates the wifi informations. # # @param json_obj JSON Object containing all the informations for the wifi. # @param connection Connection object used to send the response. # @return None def wifi_update(self, json_obj, connection): try: self._parser.writeValueFromSection('wifi', 'ssid', json_obj['ssid']) self._parser.writeValueFromSection('wifi', 'password', json_obj['password']) self._parser.writeValueFromSection('wifi', 'encryption', json_obj['security']) self._parser.writeValueFromSection('wifi', 'mode', json_obj['mode']) self.wifi_init(json_obj['ssid'], json_obj['password'], json_obj['security'], json_obj['mode'], 1) except Exception as e: self.logger.error(e) json_str = '{"packet_type": "wifi_update", "aes_pass": "******"}' master_hostname = str(json_obj['sender_name']) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * ' ') encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) connection.send(bytes(encrypt_IV, 'utf-8') + data)
def slave_conf_copy(): file_from = DaemonConfigParser(SLAVE_CONF_FILE_FROM); file_to = DaemonConfigParser(SLAVE_CONF_FILE_TO); #listen var = file_from.getValueFromSection('listen', 'port'); file_to.writeValueFromSection('listen', 'port', var); #connect var = file_from.getValueFromSection('connect', 'port'); file_to.writeValueFromSection('connect', 'port', var); #knx var = file_from.getValueFromSection('knx', 'port'); file_to.writeValueFromSection('knx', 'port', var); var = file_from.getValueFromSection('knx', 'interface'); file_to.writeValueFromSection('knx', 'interface', var); var = file_from.getValueFromSection('knx', 'activated'); file_to.writeValueFromSection('knx', 'activated', var); #enocean var = file_from.getValueFromSection('enocean', 'port'); file_to.writeValueFromSection('enocean', 'port', var); var = file_from.getValueFromSection('enocean', 'interface'); file_to.writeValueFromSection('enocean', 'interface', var); #cron var = file_from.getValueFromSection('cron', 'port'); file_to.writeValueFromSection('cron', 'port', var); var = file_from.getValueFromSection('cron', 'address'); file_to.writeValueFromSection('cron', 'address', var); #personnal_key var = file_from.getValueFromSection('personnal_key', 'aes'); file_to.writeValueFromSection('personnal_key', 'aes', var); #openvpn var = file_from.getValueFromSection('openvpn', 'openvpnserver'); file_to.writeValueFromSection('openvpn', 'openvpnserver', var);
def master_conf_copy(): file_from = DaemonConfigParser(MASTER_CONF_FILE_BKP) file_to = DaemonConfigParser(MASTER_CONF_FILE_TO) # listen var = file_from.getValueFromSection("listen", "port_slave") file_to.writeValueFromSection("listen", "port_slave", var) var = file_from.getValueFromSection("listen", "port_cmd") file_to.writeValueFromSection("listen", "port_cmd", var) # connect var = file_from.getValueFromSection("connect", "port") file_to.writeValueFromSection("connect", "port", var) # mysql var = file_from.getValueFromSection("mysql", "user") file_to.writeValueFromSection("mysql", "user", var) var = file_from.getValueFromSection("mysql", "database_name") file_to.writeValueFromSection("mysql", "database_name", var) # greenleaf var = file_from.getValueFromSection("greenleaf", "commercial") file_to.writeValueFromSection("greenleaf", "commercial", var) var = file_from.getValueFromSection("greenleaf", "admin_addr") file_to.writeValueFromSection("greenleaf", "admin_addr", var)
def master_conf_copy(): file_from = DaemonConfigParser(MASTER_CONF_FILE_BKP); file_to = DaemonConfigParser(MASTER_CONF_FILE_TO); #listen var = file_from.getValueFromSection('listen', 'port_slave'); file_to.writeValueFromSection('listen', 'port_slave', var); var = file_from.getValueFromSection('listen', 'port_cmd'); file_to.writeValueFromSection('listen', 'port_cmd', var); #connect var = file_from.getValueFromSection('connect', 'port'); file_to.writeValueFromSection('connect', 'port', var); #mysql var = file_from.getValueFromSection('mysql', 'user'); file_to.writeValueFromSection('mysql', 'user', var); var = file_from.getValueFromSection('mysql', 'database_name'); file_to.writeValueFromSection('mysql', 'database_name', var); #greenleaf var = file_from.getValueFromSection('greenleaf', 'commercial'); file_to.writeValueFromSection('greenleaf', 'commercial', var); var = file_from.getValueFromSection('greenleaf', 'admin_addr'); file_to.writeValueFromSection('greenleaf', 'admin_addr', var);
class SlaveDaemon: ## The constructor # # @param log_flag The flag saying whether the logs should be printing or not def __init__(self, log_flag): ## Logger object for formatting and printing logs self.logger = Logger(log_flag, LOG_FILE) self.logger.info("Started Domoleaf Slave daemon") ## Array of master daemon on local network self.connected_masters = {} ## Array of monitor KNX on local network self.connected_knx = [] ## Array of monitor EnOcean on local network self.connected_enocean = [] ## Array of cron running on the system self.connected_cron = [] self._scanner = Scanner(log_flag) self._hostlist = [] myhostname = socket.gethostname().upper() if SLAVE_NAME_PREFIX in myhostname: self._scanner.scan() self._hostlist = self._scanner._HostList else: self._hostlist.append(Host("", "127.0.0.1", myhostname)) self._parser = DaemonConfigParser(SLAVE_CONF_FILE) ## Keys for encrypting communications self.encrypt_keys = {} ## Main socket for communication with KNX daemon self.knx_sock = None ## Main socket for communication with master daemon self.master_sock = None ## Main socket for communication with enocean daemon self.enocean_sock = None ## Main socket for communication with cron self.cron_sock = None ## Private AES key got from configuration file self.private_aes = self._parser.getValueFromSection("personnal_key", "aes") self.wifi_init( self._parser.getValueFromSection("wifi", "ssid"), self._parser.getValueFromSection("wifi", "password"), self._parser.getValueFromSection("wifi", "encryption"), self._parser.getValueFromSection("wifi", "mode"), 0, ) ## Port on which connect got from configuration file self.connect_port = self._parser.getValueFromSection(SLAVE_CONF_CONNECT_SECTION, SLAVE_CONF_CONNECT_PORT_ENTRY) ## Callback array indexed on packet type self.functions = { KNX_READ_REQUEST: self.knx_read_request, KNX_WRITE_SHORT: self.knx_write_short, KNX_WRITE_LONG: self.knx_write_long, KNX_WRITE_TEMP: self.knx_write_temp, CHECK_SLAVE: self.check_slave, MONITOR_IP: self.monitor_ip, DATA_UPDATE: self.update, SEND_TECH: self.send_tech, SEND_ALIVE: self.send_alive, SEND_INTERFACES: self.send_interfaces, SHUTDOWN_D3: self.shutdown_d3, REBOOT_D3: self.reboot_d3, WIFI_UPDATE: self.wifi_update, } ## Updates the base system of the slave daemon. # # @param json_obj Not used here. # @param connection Not used here. # @return None def update(self, json_obj, connection): p = call(["dpkg", "--configure", "-a"]) call(["apt-get", "update"]) call(["DEBIAN_FRONTEND=noninteractive", "apt-get", "install", "domoslave", "-y"]) version = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split("\n")[0] json_str = ( '{"packet_type": "update_finished", "aes_pass": "******", "new_version": ' + version + "}" ) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * " ") encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) # faut ouvrir une nouvelle socket pour envoyer la nouvelle version # connection.send(bytes(encrypt_IV, 'utf-8') + data); ## Initializes the sockets for listenning incomming connections. # # @return None def run(self): ## Run flag at True for running, at False to stop the main loop self.run = True self.knx_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.master_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.enocean_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.cron_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.knx_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.master_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.enocean_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) self.cron_sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) port = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_PORT_ENTRY) if not port: sys.exit(2) port_master = self._parser.getValueFromSection(SLAVE_CONF_LISTEN_SECTION, SLAVE_CONF_LISTEN_PORT_ENTRY) if not port_master: sys.exit(2) port_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_PORT_ENTRY) if not port_enocean: sys.exit(2) port_cron = self._parser.getValueFromSection(SLAVE_CONF_CRON_SECTION, SLAVE_CONF_CRON_PORT_ENTRY) if not port_cron: sys.exit(2) self.knx_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.master_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.enocean_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.cron_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.knx_sock.bind(("", int(port))) self.master_sock.bind(("", int(port_master))) self.enocean_sock.bind(("", int(port_enocean))) self.cron_sock.bind(("127.0.0.1", int(port_cron))) self.knx_sock.listen(MAX_KNX) self.master_sock.listen(MAX_MASTERS) self.enocean_sock.listen(MAX_ENOCEAN) self.cron_sock.listen(MAX_CRON) self.send_monitor_ip() self.loop() ## Gets available sockets for reading on the KNX socket. # # @return None def accept_knx(self): rlist, wlist, elist = select.select([self.knx_sock], [], [], SELECT_TIMEOUT) append = self.connected_knx.append for connection in rlist: new_knx, addr = connection.accept() append(new_knx) self.receive_from_knx(self.connected_knx) ## Gets available sockets for reading on the master socket. # # @return None def accept_masters(self): rlist, wlist, elist = select.select([self.master_sock], [], [], SELECT_TIMEOUT) masters_socks = [] append = masters_socks.append for item in rlist: new_conn, addr = item.accept() append(new_conn) self.receive_from_masters(masters_socks) ## Gets available sockets for reading on the EnOcean socket. # # @return None def accept_enocean(self): rlist, wlist, elist = select.select([self.enocean_sock], [], [], SELECT_TIMEOUT) enocean_socks = [] append = enocean_socks.append append_connected = self.connected_enocean.append for item in rlist: new_conn, addr = item.accept() append(new_conn) append_connected(new_conn) self.receive_from_enocean(enocean_socks) ## Gets available sockets for reading on the Cron socket. # # @return None def accept_cron(self): rlist, wlist, elist = select.select([self.cron_sock], [], [], SELECT_TIMEOUT) cron_socks = [] append = cron_socks.append append_connected = self.connected_cron.append for item in rlist: new_conn, addr = item.accept() append(new_conn) append_connected(new_conn) self.receive_from_cron(cron_socks) ## Checks the packet_type of the data, and calls the appropriate function. # # @param data Packet data including the packet type. # @param connection Connection object sent to the function. # @return None def parse_data(self, data, connection): json_obj = json.JSONDecoder().decode(data) if json_obj["packet_type"] in self.functions.keys(): self.functions[json_obj["packet_type"]](json_obj, connection) else: raise Exception(str(json_obj["packet_type"]) + ": is not a valid packet type") ## System call of 'groupread' with parameters. # # @param json_obj JSON Object containing the address to read. # @param connection Not used here. # @return None def knx_read_request(self, json_obj, connection): call(["knxtool", CALL_GROUPREAD, EIB_URL, json_obj["addr_to_read"]]) ## System call of "groupwrite" with parameters. # # @param json_obj JSON Object containing the address to which send, and the values to send. # @param connection Not used here. # @return None def knx_write_temp(self, json_obj, connection): val = json_obj["value"].split(" ") call(["knxtool", CALL_GROUPWRITE, EIB_URL, json_obj["addr_to_send"], val[0], val[1]]) ## System call of "groupswrite" with parameters. # # @param json_obj JSON Object containing the address to which send, and the value to send. # @param connection Not used here. # @return None def knx_write_short(self, json_obj, connection): call(["knxtool", CALL_GROUPSWRITE, EIB_URL, json_obj["addr_to_send"], str(json_obj["value"])]) ## System call of "groupwrite" with parameters. # # @param json_obj JSON Object containing the address to which send, and the value to send. # @param connection Not used here. # @return None def knx_write_long(self, json_obj, connection): call(["knxtool", CALL_GROUPWRITE, EIB_URL, json_obj["addr_to_send"], str(json_obj["value"])]) ## Reads data comming from amsters and calls parse_data(). # # @param masters_to_read Array containing the sockets of all the masters found on local network. # @return None def receive_from_masters(self, masters_to_read): for master in masters_to_read: data = master.recv(4096) decrypt_IV = data[:16].decode() decode_obj = AES.new(self.private_aes, AES.MODE_CBC, decrypt_IV) data2 = decode_obj.decrypt(data[16:]) self.parse_data(data2.decode(), master) master.close() ## Reads data from monitor KNX and transmits to master. # # @param knx_to_read Array containing the sockets of all the KNX daemons on local network. # @return None def receive_from_knx(self, knx_to_read): for knx in knx_to_read: data = knx.recv(TELEGRAM_LENGTH) if data: self.send_knx_data_to_masters(data) if knx in self.connected_knx: knx.close() self.connected_knx.remove(knx) ## Reads data from monitor EnOcean and transmits to master. # # @param enocean_to_read Array containing the sockets of all the EnOcean daemons on local network. # @return None def receive_from_enocean(self, enocean_to_read): for enocean in enocean_to_read: data = enocean.recv(4096) if data: self.send_enocean_data_to_masters(data) if enocean in self.connected_enocean: enocean.close() self.connected_enocean.remove(enocean) ## Receives data from a cron and executes it. # # @param cron_to_read Array containing the sockets of all crons on local network. # @return None def receive_from_cron(self, cron_to_read): for cron in cron_to_read: data = cron.recv(4096) if data: json_str = json.JSONEncoder().encode({"packet_type": data.decode()}) self.parse_data(json_str, cron) if cron in self.connected_cron: cron.close() self.connected_cron.remove(cron) ## Checks the existence of this daemon. # This function is called when a check_slave packet is received. # # @param json_obj JSON Object containing the hostname of the sender of the packet. # @param connection Connection object used to send the response. # @return None def check_slave(self, json_obj, connection): interface_knx = self._parser.getValueFromSection(SLAVE_CONF_KNX_SECTION, SLAVE_CONF_KNX_INTERFACE) interface_enocean = self._parser.getValueFromSection(SLAVE_CONF_ENOCEAN_SECTION, SLAVE_CONF_ENOCEAN_INTERFACE) version = os.popen("dpkg-query -W -f='${Version}\n' domoslave").read().split("\n")[0] json_str = ( '{"packet_type": "check_slave", "aes_pass": "******", "version": "' + version + '", "interface_knx": "' + interface_knx + '", "interface_enocean": "' + interface_enocean + '"}' ) master_hostname = str(json_obj["sender_name"]) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * " ") encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) connection.send(bytes(encrypt_IV, "utf-8") + data) ## Re scans the local network and refreshes hostlist. # # @param json_obj Not used here. # @param connection Not used here. # @return None def monitor_ip(self, json_obj, connection): self._scanner.scan() self._hostlist = self._scanner._HostList ## Sends a monitor_ip packet to all the masters available. # # @return None def send_monitor_ip(self): json_str = json.JSONEncoder().encode({"packet_type": "monitor_ip"}) self.send_data_to_all_masters(json_str) ## Main daemon loop. # # @return None def loop(self): while self.run: try: self.accept_knx() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop accept_knx: " + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop: Keyboard interrupt") try: self.accept_masters() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop accept_masters: " + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop: Keyboard interrupt") try: self.accept_enocean() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop accept_enocean: " + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop: Keyboard interrupt") try: self.accept_cron() except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop accept_cron: " + str(e)) except KeyboardInterrupt as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in loop: Keyboard interrupt") ## Stops the daemon and closes all sockets. # # @return None def stop(self): for name, sock in self.connected_masters.items(): sock.close() for knx in self.connected_knx: knx.close() self.knx_sock.close() ## Stores every host on local network if its hostname begins by 'MD3' in connected_masters dict(). # # @return None def connect_to_masters(self): hostname = socket.gethostname() self.connected_masters = {} for host in self._hostlist: if MASTER_NAME_PREFIX in host._Hostname or str(host._IpAddr) == "127.0.0.1": if not self.connect_port: self.logger.error( "in connect_to_masters: No " + SLAVE_CONF_CONNECT_PORT_ENTRY + " in " + SLAVE_CONF_CONNECT_SECTION + " section or maybe no such " + SLAVE_CONF_CONNECT_SECTION + " defined" ) sys.exit(1) try: self.logger.debug("Connecting to " + str(host._IpAddr) + ":" + str(self.connect_port)) sock = socket.create_connection((host._IpAddr, self.connect_port)) hostname = host._Hostname.split(".")[0] self.connected_masters[host._Hostname] = sock except Exception as e: frameinfo = getframeinfo(currentframe()) self.logger.error("in connect_to_masters: " + str(e)) pass ## Converts data from bytes to a clear KNX datagram, and sends it to all available masters. # # @param data The data having to be converted from bytes to clear KNX datagram. # @return None def send_knx_data_to_masters(self, data): ctrl = int(data[0]) src_addr = int.from_bytes(data[1:3], byteorder="big") dst_addr = int.from_bytes(data[3:5], byteorder="big") data_len = int.from_bytes(data[5:6], byteorder="big") telegram_data = data[6 : 7 + data_len] typ = -1 value = 0 if telegram_data[1] & 0xC0 == 0x00: # read typ = 0 elif telegram_data[1] & 0xC0 == 0x40: # resp typ = 1 if data_len == 2: value = int(telegram_data[1] & 0x0F) elif data_len > 2: value = int.from_bytes(telegram_data[2:data_len], byteorder="big") elif telegram_data[1] & 0xC0 == 0x80: # write typ = 2 if data_len == 2: value = int(telegram_data[1] & 0x0F) elif data_len > 2: typ = 3 value = int.from_bytes(telegram_data[2:data_len], byteorder="big") json_str = json.JSONEncoder().encode( { "packet_type": "monitor_knx", "type": typ, "src_addr": individual2string(src_addr), "dst_addr": group2string(dst_addr), "date": str(time.time()).split(".")[0], "value": value, "sender_name": socket.gethostname(), } ) self.send_data_to_all_masters(json_str) ## Convertes data from bytes to a clear EnOcean datagram, and sends it to available masters. # # @param data The data having to be converted from bytes to EnOcean datagram. # @return None def send_enocean_data_to_masters(self, data): if data[4] == PACKET_TYPE_RADIO_ERP1: # si le packet_type == radio_erp1 data_len = int.from_bytes(data[1:2], byteorder="big") opt_data_len = int(data[3]) src_str = "%X" % int.from_bytes(data[1 + data_len : 5 + data_len], byteorder="big") if len(src_str) < 8: src_str = "0" + src_str json_dict = { "packet_type": "monitor_enocean", "src_addr": src_str, "dst_addr": "%X" % int.from_bytes(data[261 : 265 + opt_data_len], byteorder="big"), "date": str(time.time()).split(".")[0], "sender_name": socket.gethostname(), "type": int(data[6]), } if data[6] == RORG_NORMAL: json_dict["value"] = int(data[7]) elif data[6] == RORG_TEMPERATURE: json_dict["value"] = float(40 - ((40 / 255) * int(data[9]))) json_str = json.JSONEncoder().encode(json_dict) self.send_data_to_all_masters(json_str) ## Sends data to all masters available on local network. # # @param json_str The data to send under form of a JSON Object stringified. # @return None def send_data_to_all_masters(self, json_str): self.connect_to_masters() for name in self.connected_masters.keys(): try: master = self.connected_masters[name] AES.key_size = 32 aes_IV = AESManager.get_IV() encode_obj = AES.new(self.private_aes, AES.MODE_CBC, aes_IV) spaces = 16 - len(json_str) % 16 data2 = encode_obj.encrypt(json_str + (spaces * " ")) master.send(bytes(aes_IV, "utf-8") + data2) master.close() except KeyError as e: self.logger.error("in send_data_to_all_masters: " + str(e)) pass ## Sends the informations about the slave to all masters available. # # @param json_obj Not used here. # @param connection Not used here. # @return None def send_tech(self, json_obj, connection): json_str = json.JSONEncoder().encode({"packet_type": "send_tech", "info": GLManager.TechInfo()}) self.send_data_to_all_masters(json_str) ## Sends that the salve daemon is alive to all masters available. # # @param json_obj Not used here. # @param connection Not used here. # @return None def send_alive(self, json_obj, connection): json_str = json.JSONEncoder().encode({"packet_type": "send_alive", "info": GLManager.TechAlive()}) self.send_data_to_all_masters(json_str) ## Sends the protocol interface informations to all masters available. # # @param json_obj Protocol interface informations. # @param connection Connection object used to send the response. # @return None def send_interfaces(self, json_obj, connection): try: if os.path.exists("/tmp/knxd"): call(["service", "knxd", "stop"]) previous_val_knx = self._parser.getValueFromSection("knx", "interface") previous_val_EnOcean = self._parser.getValueFromSection("enocean", "interface") new_val = str(json_obj["interface_arg_knx"]) self._parser.writeValueFromSection("knx", "interface", new_val) self._parser.writeValueFromSection("knx", "activated", str(json_obj["daemon_knx"])) self._parser.writeValueFromSection("enocean", "interface", str(json_obj["interface_arg_EnOcean"])) if not previous_val_knx or previous_val_knx is None: call(["update-rc.d", "knxd", "defaults"]) call(["update-rc.d", "knxd", "enable"]) if not new_val or new_val is None: Popen(["systemctl", "-q", "disable", "knxd"]) else: knx_edit = 'KNXD_OPTS="-e 1.0.254 -D -T -S -b ' if json_obj["interface_knx"] == "tpuarts": knx_edit += json_obj["interface_knx"] + ":/dev/" + new_val + '"' else: knx_edit += json_obj["interface_knx"] + ":" + new_val + '"' conf_knx = open("/etc/knxd.conf", "w") conf_knx.write(knx_edit + "\n") conf_knx.close() call(["service", "knxd", "start"]) if json_obj["daemon_knx"] == 1: if os.path.exists("/var/run/monitor_knx.pid"): os.remove("/var/run/monitor_knx.pid") Popen(["monitor_knx", "ip:localhost", "--daemon"]) except Exception as e: self.logger.error(e) json_str = '{"packet_type": "send_interfaces", "aes_pass": "******"}' master_hostname = str(json_obj["sender_name"]) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * " ") encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) connection.send(bytes(encrypt_IV, "utf-8") + data) if previous_val_EnOcean != str(json_obj["interface_arg_EnOcean"]): call(["service", "domoslave", "restart"]) ## Shuts down the slave D3. # # @param json_obj Not used here. # @param connection Not used here. # @return None def shutdown_d3(self, json_obj, connection): call(["poweroff"]) ## Reboots the slave D3. # # @param json_obj Not used here. # @param connection Not used here. # @return None def reboot_d3(self, json_obj, connection): call(["reboot"]) ## Initializes the wifi protocol. # # @param ssid The SSID of the network. # @param password The password to connect to the network. # @param security The security type of the connection. # @param mode The mode of the initialization of the network interface. # @param opt Flag to do some more stuff if it is 1. # @return None def wifi_init(self, ssid, password, security, mode, opt): try: ps_process = Popen(["ps", "-x"], stdout=PIPE) res = Popen(["grep", "hostapd"], stdin=ps_process.stdout, stdout=PIPE) res = res.stdout.read().decode().split("\n")[0].split(" ") ps_process.stdout.close() if res: while "" in res: res.remove("") call(["kill", "-9", res[0]]) ps_process = Popen(["ps", "-x"], stdout=PIPE) res = Popen(["grep", "wpa_supplicant"], stdin=ps_process.stdout, stdout=PIPE) res = res.stdout.read().decode().split("\n")[0].split(" ") ps_process.stdout.close() if res: while "" in res: res.remove("") call(["kill", "-9", res[0]]) call(["ifconfig", "wlan0", "down"]) if mode == WIFI_MODE_DISABLED: if opt == 1: call(["service", "dnsmasq", "stop"]) elif mode == WIFI_MODE_CLIENT: call(["ifconfig", "wlan0", "up"]) if opt == 1: call(["service", "dnsmasq", "stop"]) conf_file = open("/etc/network/interfaces", "w") conf_str = "".join( [ "auto lo\niface lo inet loopback\n\nallow-hotplug eth0\n", "iface eth0 inet dhcp\n\nallow-hotplug usb0\niface usb0 inet dhcp\n\n", "auto wlan0\niface wlan0 inet dhcp\n\twpa-conf ", WPA_SUPPLICANT_CONF_FILE, "\n", ] ) conf_file.write(conf_str) conf_file.close() conf_file = open(WPA_SUPPLICANT_CONF_FILE, "w") conf_str = "".join( [ "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n", "update_config=1\nctrl_interface_group=0\neapol_version=1\n", "ap_scan=1\n fast_reauth=1\n\n\nnetwork={\n\tdisabled=0\n", '\tssid="', ssid, '"\n\tscan_ssid=0\n\tpriority=1\n', ] ) if security == WIFI_SECURITY_WPA: conf_str += ( "\tproto=WPA\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n" + '\tpairwise=TKIP CCMP\n\tgroup=TKIP CCMP\n\tpsk="' + password + '"\n' ) elif security == WIFI_SECURITY_WPA2: conf_str += ( "\tproto=RSN\n\tkey_mgmt=WPA-PSK\n\tauth_alg=OPEN\n\tpairwise=CCMP TKIP\n" + '\tgroup=CCMP TKIP\n\tpsk="' + password + '"\n' ) elif security == WIFI_SECURITY_WEP: conf_str += "\tkey_mgmt=NONE\n\tauth_alg=SHARED\n" if len(password) == 5 or len(password) == 10: conf_str += "\tgroup=WEP40\n" elif len(password) == 13 or len(password) == 26: conf_str += "\tgroup=WEP104\n" else: conf_str += "\tgroup=WEP40 WEP104\n" conf_str += '\twep_key0="' + password + '"\n\twep_tx_keyidx=0\n' conf_str += "\tpriority=1\n}\n" conf_file.write(conf_str) conf_file.close() call(["wpa_supplicant", "-Dnl80211", "-iwlan0", "-c" + WPA_SUPPLICANT_CONF_FILE, "-B"]) call(["dhclient", "wlan0"]) elif mode == WIFI_MODE_ACCESS_POINT: call(["ifconfig", "wlan0", "172.16.0.1", "netmask", "255.255.255.0", "up"]) conf_file = open(HOSTAPD_CONF_FILE, "w") conf_str = "".join( [ "interface=wlan0\n\ndriver=nl80211\n\nssid=", ssid, "\n\n", "hw_mode=g\n\nieee80211n=1\n\nchannel=6\n\nbeacon_int=100\n\n", "dtim_period=2\n\nmax_num_sta=255\n\nrts_threshold=2347\n\n", "fragm_threshold=2346\n\nmacaddr_acl=0\n\n", ] ) if security == WIFI_SECURITY_WPA: conf_str += ( "auth_algs=1\n\nwpa=1\n\nwpa_passphrase=" + password + "\n\n" + "wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=TKIP\n" ) elif security == WIFI_SECURITY_WPA2: conf_str += ( "auth_algs=1\n\nwpa=2\n\nwpa_passphrase=" + password + "\n\n" + "wpa_key_mgmt=WPA-PSK\n\nwpa_pairwise=CCMP\n\nrsn_pairwise=CCMP\n" ) else: self.logger.error("Wifi security = Unknown") conf_file.write(conf_str) conf_file.close() if opt == 1: conf_file = open(DNSMASQ_CONF_FILE, "w") conf_str = "domain-needed\ninterface=wlan0\ndhcp-range=172.16.0.2,172.16.0.254,12h\n" conf_file.write(conf_str) conf_file.close() call(["service", "dnsmasq", "restart"]) call(["iptables", "-t", "nat", "-A", "POSTROUTING", "-j", "MASQUERADE"]) call(["hostapd", HOSTAPD_CONF_FILE, "-B"]) else: call(["ifconfig", "wlan0", "up"]) self.logger.error("Wifi mode = Unknown") except Exception as e: self.logger.error(e) ## Updates the wifi informations. # # @param json_obj JSON Object containing all the informations for the wifi. # @param connection Connection object used to send the response. # @return None def wifi_update(self, json_obj, connection): try: self._parser.writeValueFromSection("wifi", "ssid", json_obj["ssid"]) self._parser.writeValueFromSection("wifi", "password", json_obj["password"]) self._parser.writeValueFromSection("wifi", "encryption", json_obj["security"]) self._parser.writeValueFromSection("wifi", "mode", json_obj["mode"]) self.wifi_init(json_obj["ssid"], json_obj["password"], json_obj["security"], json_obj["mode"], 1) except Exception as e: self.logger.error(e) json_str = '{"packet_type": "wifi_update", "aes_pass": "******"}' master_hostname = str(json_obj["sender_name"]) encrypt_IV = AESManager.get_IV() spaces = 16 - len(json_str) % 16 json_str = json_str + (spaces * " ") encode_obj = AES.new(self.private_aes, AES.MODE_CBC, encrypt_IV) data = encode_obj.encrypt(json_str) connection.send(bytes(encrypt_IV, "utf-8") + data)
def slave_conf_copy(): file_from = DaemonConfigParser(SLAVE_CONF_FILE_FROM) file_to = DaemonConfigParser(SLAVE_CONF_FILE_TO) #listen var = file_from.getValueFromSection('listen', 'port') file_to.writeValueFromSection('listen', 'port', var) #connect var = file_from.getValueFromSection('connect', 'port') file_to.writeValueFromSection('connect', 'port', var) #knx var = file_from.getValueFromSection('knx', 'port') file_to.writeValueFromSection('knx', 'port', var) var = file_from.getValueFromSection('knx', 'interface') file_to.writeValueFromSection('knx', 'interface', var) var = file_from.getValueFromSection('knx', 'activated') file_to.writeValueFromSection('knx', 'activated', var) #enocean var = file_from.getValueFromSection('enocean', 'port') file_to.writeValueFromSection('enocean', 'port', var) var = file_from.getValueFromSection('enocean', 'interface') file_to.writeValueFromSection('enocean', 'interface', var) #cron var = file_from.getValueFromSection('cron', 'port') file_to.writeValueFromSection('cron', 'port', var) var = file_from.getValueFromSection('cron', 'address') file_to.writeValueFromSection('cron', 'address', var) #personnal_key var = file_from.getValueFromSection('personnal_key', 'aes') file_to.writeValueFromSection('personnal_key', 'aes', var) #openvpn var = file_from.getValueFromSection('openvpn', 'openvpnserver') file_to.writeValueFromSection('openvpn', 'openvpnserver', var)