def create_server( self, instance_name: str = None, address: str = "127.0.0.1", port: str = 10800, server_host: str = None ) -> None: """ Initializes zeroconf service (everpl hub) announcement with the specified optional params :param instance_name: the name of this service (instance, hub); set to the "everpl hub @ hostname._everpl.tcp" by default :param address: an IP address of this instance; set to the 127.0.0.1 by default :param port: the port of announced service; set to the 10800 by default :param server_host: hostname of this instance; set to the "hostname.local" by if not set specifically (if server_host is None or is equal to the "0.0.0.0" address) :return: None """ hostname = socket.gethostname() if instance_name is None: instance_name = "everpl hub @ %s.%s" % ( hostname, self._service_type ) coded_address = socket.inet_aton(address) if server_host is None or server_host == "0.0.0.0": server_host = "%s.local" % hostname txt_properties = {} service_info = zeroconf.ServiceInfo( type_=self._service_type, name=instance_name, address=coded_address, port=port, properties=txt_properties, server=server_host ) self._zeroconf.register_service(service_info)
def zeroconf_register(self, reg_type, name=None, port=None, txt_record=None): """ Registers a new service with Zeroconf/Bonjour/Avahi. :param reg_type: type of service to register, e.g. "_gntp._tcp" :param name: displayable name of the service, if not given defaults to the OctoPrint instance name :param port: port to register for the service, if not given defaults to OctoPrint's (public) port :param txt_record: optional txt record to attach to the service, dictionary of key-value-pairs """ if not name: name = self.get_instance_name() if not port: port = self.port reg_type = self._format_zeroconf_service_type(reg_type) name = self._format_zeroconf_name(name, reg_type) txt_record = self._format_zeroconf_txt(txt_record) key = (reg_type, port) addresses = list( map(lambda x: socket.inet_aton(x), self.get_interface_addresses())) try: info = zeroconf.ServiceInfo( reg_type, name, addresses=addresses, port=port, server=f"{socket.gethostname()}.local.", properties=txt_record, ) self._zeroconf.register_service(info, allow_name_change=True) self._zeroconf_registrations[key].append(info) self._logger.info(f"Registered '{name}' for {reg_type}") except Exception: self._logger.exception( f"Could not register {name} for {reg_type} on port {port}")
def announce(self, target, addr, tivos): """ Announce the availability of our service. """ host, port = addr host_ip = self.get_address(host) if target in tivos: name, prop = tivos[target] else: name = target[0] prop = { 'TSN': '648000000000000', 'path': '/', 'protocol': 'tivo-remote', 'swversion': '0.0', 'platform': 'tcd/Series3' } name = 'Proxy(%s)' % name self.info = zeroconf.ServiceInfo(SERVICE, '%s.%s' % (name, SERVICE), host_ip, port, 0, 0, prop) self.rz.registerService(self.info)
def _server_register(self): with self._zconf_lock: hostname = socket.gethostname() ips = socket.gethostbyname_ex(hostname)[-1] if ips: compname = self._get_server_name().decode('utf-8') hostaddr = ips[0] desc = { 'platform': self._SERVER_PLATFORM, 'version': '%s.%s.%s' % self._SERVER_VERSION } info = zeroconf.ServiceInfo( self._ZEROCONF_TYPE, '%s.' % compname + self._ZEROCONF_TYPE, socket.inet_aton(hostaddr), self._tcp_srv_port, 0, 0, desc, '%s.' % hostname) self._zconf = zeroconf.Zeroconf(bindaddress=hostaddr) self._zconf.register_service(info) else: self._zconf = zeroconf.Zeroconf()
def register(name, port, properties={}): type_="_http._tcp.local." info = zeroconf.ServiceInfo( type_="_http._tcp.local.", name=name + '.' + type_, address=socket.inet_aton(get_ip()), port=port, weight=0, priority=0, properties=properties, server="kalleanka.local.") zc = zeroconf.Zeroconf() zc.register_service(info) @atexit.register def unreg(): zc.unregister_service(info) zc.close() print('unregistered zeroconf')
def _RegisterService(self, name, ip, port): # name: fully qualified service name self.service_name = 'Beremiz_%s.%s' % (name, service_type) self.name = name self.port = port self.server = zeroconf.Zeroconf() print("MDNS brodcasting on :" + ip) if ip == "0.0.0.0": ip = self.gethostaddr() print("MDNS brodcasted service address :" + ip) self.ip_32b = socket.inet_aton(ip) self.server.register_service( zeroconf.ServiceInfo(service_type, self.service_name, self.ip_32b, self.port, properties=self.serviceproperties)) self.retrytimer = None
def register_service(zc, service_type_name, service_name, ipaddr, port, protocol="tcp"): """ ZeroConf: Register service type, protocol, ipaddr and port Returns ZeroConf object and ServiceInfo object """ import zeroconf sn = service_name if len(service_name) <= 15 else service_name[:15] stn = "_{0}._{1}.local.".format(service_type_name, protocol) sn = "{0}.{1}".format(sn, stn) # "addresses" deprecates "address" in 0.23+ address = socket.inet_aton(ipaddr) kwargs = {} if tuple(map(int, zeroconf.__version__.split(".")))[:2] >= (0, 23): kwargs["addresses"] = [address] else: kwargs["address"] = address info = zeroconf.ServiceInfo(stn, sn, port=port, properties={}, **kwargs) zc.register_service(info) return info
def test_register_service_with_custom_ttl(): """Test a registering a service with a custom ttl.""" # instantiate a zeroconf instance zc = Zeroconf(interfaces=['127.0.0.1']) # start a browser type_ = "_homeassistant._tcp.local." name = "MyTestHome" info_service = r.ServiceInfo( type_, '%s.%s' % (name, type_), 80, 0, 0, {'path': '/~paulsm/'}, "ash-90.local.", addresses=[socket.inet_aton("10.0.1.2")], ) zc.register_service(info_service, ttl=30) assert zc.cache.get(info_service.dns_pointer()).ttl == 30 zc.close()
def register_service(properties): service_type = properties['type'] if 'type' in properties else "_http._tcp.local." short_name = properties['name'] if 'name' in properties else "Default Service Name" name = "%s.%s" % (short_name, service_type) ip_str = properties['ip'] if 'ip' in properties else "127.0.0.1" ip = socket.inet_aton(ip_str) port = int(properties['port']) if 'port' in properties else 8888 weight = int(properties['weight']) if 'weight' in properties else 0 priority = int(properties['priority']) if 'priority' in properties else 0 domain_name = properties['domain_name'] if 'domain_name' in properties else "myserv.local." # See http://www.dns-sd.org/ServiceTypes.html global info info = zeroconf.ServiceInfo(service_type, name, address=ip, port=port, weight=weight, priority=priority, properties=properties, server=domain_name ) try: zc.register_service(info) except zeroconf.NonUniqueNameException as e: print("Registration of service failed. Name taken") sys.exit(1) log.info("Registered service: \"%s\" on name \"%s\" at address \"%s\"" % (name, domain_name, ip_str)) try: while True: time.sleep(0.1) except KeyboardInterrupt: pass finally: cleanup()
def announce(self, friendly_name=None, subtypes=None): """Announce myself on mDNS by registering a service""" svc_type = self._service + self._domain svc_name = self._instance + "." + self._service + self._domain svc_addr = socket.inet_aton(self._ip_addr) svc_port = self._port svc_server = self._instance + "." + self._domain svc_props = {"path": self._resource_path} if friendly_name is not None: svc_props["name"] = friendly_name if subtypes is not None: svc_props["type"] = subtypes srv = zeroconf.ServiceInfo(svc_type, svc_name, svc_addr, svc_port, properties=svc_props, server=svc_server) self._zconf = zeroconf.Zeroconf( interfaces=zeroconf.InterfaceChoice.Default) self._zconf.register_service(srv)
def register(self, mutex): if mutex.domain not in self.ds: info = self.ds[mutex.domain] = MutexInfo(mutex.uuid) try: info.service = zeroconf.ServiceInfo( type="%s" % (mutex.domain), name="%s.%s" % (mutex.uuid, mutex.domain), address=[], port=CL_PORT, weight=0, priority=0, properties={ "role": "candidate", "alias": self.alias }, records=[zeroconf._TYPE_TXT], ttl=HEARTBEAT_TTL, signer=self.alias) self.mdns.registerService(info.service) except Exception as e: del self.ds[mutex.domain] traceback.print_exc() raise e
def register(self, answerer, port, ssl): if not zeroConfOK: return desc = { 'host': answerer.hostname(), 'ip': answerer.ip(), 'id': answerer.id(), 'pid': str(answerer.pid()), 'port': str(port), 'ssl': str(ssl), 'path': answerer.pathToSolution(), } if desc["host"].find(".") > 0: shorthost = desc["host"][0:desc["host"].index(".")] else: shorthost = desc["host"] desc["name"]= "@"+shorthost+":"+desc["port"]+"."+ \ foamServerDescriptorString basename = path.basename(desc["path"]).replace(".", "_") extraLen = 63 - len(desc["name"]) if len(basename) > extraLen: desc["name"] = basename[0:extraLen].replace('.', '') + desc["name"] else: desc["name"] = basename + desc["name"] self.info = zc.ServiceInfo( type_=foamServerDescriptorString, name=desc["name"], # name="Nix da."+foamServerDescriptorString, address=socket.inet_aton(answerer.ip()), port=port, weight=0, priority=0, properties=desc, server=desc["host"] + ".") self.zero.register_service(self.info)
def get_service_info_for_instance(self, inst): ip = inst.public_ip_address hostname = None try: hostname = [f['Value'] for f in inst.tags if f['Key'] == 'Name'][0] except (IndexError, TypeError): LOG.warning( 'No "Name" tag is assigned for %s. Using %s as the name.', inst.id, inst.id) hostname = inst.id # TODO: sanitize name! assert isinstance(hostname, str), '%s is not string?' % hostname desc = {'model': 'Z42 (driver: %s)' % __name__} srv = zeroconf.ServiceInfo(type='_device-info._tcp.local.', name='%s._device-info._tcp.local.' % hostname, address=socket.inet_aton(ip), port=0, weight=0, priority=0, properties=desc, server='%s.local.' % hostname) return srv
def publish(self, daap_server, preferred_database=None): """ Publish a given `DAAPServer` instance. The given instances should be fully configured, including the provider. By default Zeroconf only advertises the first database, but the DAAP protocol has support for multiple databases. Therefore, the parameter `preferred_database` can be set to choose which database ID will be served. If the provider is not fully configured (in other words, if the preferred database cannot be found), this method will not publish this server. In this case, simply call this method again when the provider is ready. If the server was already published, it will be unpublished first. :param DAAPServer daap_server: DAAP Server instance to publish. :param int preferred_database: ID of the database to advertise. """ if daap_server in self.daap_servers: self.unpublish(daap_server) # Zeroconf can advertise the information for one database only. Since # the protocol supports multiple database, let the user decide which # database to advertise. If none is specified, take the first one. provider = daap_server.provider try: if preferred_database is not None: database = provider.server.databases[preferred_database] else: database = provider.server.databases.values()[0] except LookupError: # The server may not have any databases (yet). return # The IP 0.0.0.0 tells this server to bind to all interfaces. However, # Bonjour advertises itself to others, so others need an actual IP. # There is definately a better way, but it works. if daap_server.ip == "0.0.0.0": addresses = [] for address in zeroconf.get_all_addresses(socket.AF_INET): if not address == "127.0.0.1": addresses.append(socket.inet_aton(address)) else: addresses = [socket.inet_aton(daap_server.ip)] # Determine machine ID and database ID, depending on the provider. If # the provider has no support for persistent IDs, generate a random # ID. if provider.supports_persistent_id: machine_id = hex(provider.server.persistent_id) database_id = hex(database.persistent_id) else: machine_id = hex(generate_persistent_id()) database_id = hex(generate_persistent_id()) # iTunes 11+ uses more properties, but this seems to be sufficient. description = { "txtvers": "1", "Password": str(int(bool(daap_server.password))), "Machine Name": provider.server.name, "Machine ID": machine_id.upper(), "Database ID": database_id.upper() } # Test is zeroconf supports multiple addresses or not. For # compatibility with zeroconf 0.17.3 or less. if not hasattr(zeroconf.ServiceInfo("", ""), "addresses"): addresses = addresses[0] self.daap_servers[daap_server] = zeroconf.ServiceInfo( type="_daap._tcp.local.", name=provider.server.name + "._daap._tcp.local.", address=addresses, port=daap_server.port, properties=description) self.zeroconf.register_service(self.daap_servers[daap_server])
def register_service(self, service_type, endpoint, params=None): """Register a service. This call announces the new services via multicast and instructs the built-in server to respond to queries about it. :param service_type: OpenStack service type, e.g. "baremetal". :param endpoint: full endpoint to reach the service. :param params: optional properties as a dictionary. :raises: :exc:`.ServiceRegistrationFailure` if the service cannot be registered, e.g. because of conflicts. """ try: parsed = _parse_endpoint(endpoint) except socket.error as ex: msg = (_("Cannot resolve the host name of %(endpoint)s: " "%(error)s. Hint: only IPv4 is supported for now.") % { 'endpoint': endpoint, 'error': ex }) raise exception.ServiceRegistrationFailure(service=service_type, error=msg) all_params = CONF.mdns.params.copy() if params: all_params.update(params) all_params.update(parsed.params) # TODO(dtantsur): allow overriding TTL values via configuration when # https://github.com/jstasiak/python-zeroconf/commit/ecc021b7a3cec863eed5a3f71a1f28e3026c25b0 # is released. info = zeroconf.ServiceInfo(_MDNS_DOMAIN, '%s.%s' % (service_type, _MDNS_DOMAIN), parsed.ip, parsed.port, properties=all_params, server=parsed.hostname) LOG.debug('Registering %s via mDNS', info) # Work around a potential race condition in the registration code: # https://github.com/jstasiak/python-zeroconf/issues/163 delay = 0.1 try: for attempt in range(CONF.mdns.registration_attempts): try: self._zc.register_service(info) except zeroconf.NonUniqueNameException: LOG.debug('Could not register %s - conflict', info) if attempt == CONF.mdns.registration_attempts - 1: raise # reset the cache to purge learned records and retry self._zc.cache = zeroconf.DNSCache() time.sleep(delay) delay *= 2 else: break except zeroconf.Error as exc: raise exception.ServiceRegistrationFailure(service=service_type, error=exc) self._registered.append(info)
def test_tc_bit_defers_last_response_missing(): zc = Zeroconf(interfaces=['127.0.0.1']) type_ = "_knowndefer._tcp.local." name = "knownname" name2 = "knownname2" name3 = "knownname3" registration_name = "%s.%s" % (name, type_) registration2_name = "%s.%s" % (name2, type_) registration3_name = "%s.%s" % (name3, type_) desc = {'path': '/~paulsm/'} server_name = "ash-2.local." server_name2 = "ash-3.local." server_name3 = "ash-4.local." info = r.ServiceInfo(type_, registration_name, 80, 0, 0, desc, server_name, addresses=[socket.inet_aton("10.0.1.2")]) info2 = r.ServiceInfo(type_, registration2_name, 80, 0, 0, desc, server_name2, addresses=[socket.inet_aton("10.0.1.2")]) info3 = r.ServiceInfo(type_, registration3_name, 80, 0, 0, desc, server_name3, addresses=[socket.inet_aton("10.0.1.2")]) zc.registry.add(info) zc.registry.add(info2) zc.registry.add(info3) def threadsafe_query(*args): async def make_query(): zc.handle_query(*args) asyncio.run_coroutine_threadsafe(make_query(), zc.loop).result() now = r.current_time_millis() _clear_cache(zc) source_ip = '203.0.113.12' generated = r.DNSOutgoing(const._FLAGS_QR_QUERY) question = r.DNSQuestion(type_, const._TYPE_PTR, const._CLASS_IN) generated.add_question(question) for _ in range(300): # Add so many answers we end up with another packet generated.add_answer_at_time(info.dns_pointer(), now) generated.add_answer_at_time(info2.dns_pointer(), now) generated.add_answer_at_time(info3.dns_pointer(), now) packets = generated.packets() assert len(packets) == 4 expected_deferred = [] next_packet = r.DNSIncoming(packets.pop(0)) expected_deferred.append(next_packet) threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred timer1 = zc._timers[source_ip] next_packet = r.DNSIncoming(packets.pop(0)) expected_deferred.append(next_packet) threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred timer2 = zc._timers[source_ip] if sys.version_info >= (3, 7): assert timer1.cancelled() assert timer2 != timer1 # Send the same packet again to similar multi interfaces threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred assert source_ip in zc._timers timer3 = zc._timers[source_ip] if sys.version_info >= (3, 7): assert not timer3.cancelled() assert timer3 == timer2 next_packet = r.DNSIncoming(packets.pop(0)) expected_deferred.append(next_packet) threadsafe_query(next_packet, source_ip, const._MDNS_PORT) assert zc._deferred[source_ip] == expected_deferred assert source_ip in zc._timers timer4 = zc._timers[source_ip] if sys.version_info >= (3, 7): assert timer3.cancelled() assert timer4 != timer3 for _ in range(8): time.sleep(0.1) if source_ip not in zc._timers: break assert source_ip not in zc._deferred assert source_ip not in zc._timers # unregister zc.registry.remove(info) zc.close()
def white_on(intensity): #turn on all r.start(int(intensity)) b.start(int(intensity)) g.start(int(intensity)) return None if __name__ == '__main__': s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip = (s.getsockname()[0]) s.close() host = ip info = zeroconf.ServiceInfo( "_http._tcp.local.", "LED PI._http._tcp.local.", socket.inet_aton(host), 80, 0, 0, { 'Available Colors': "'red', 'blue', 'green', 'yellow', 'cyan', 'magenta', 'white'" }, "ash-2.local.") zconf = zeroconf.Zeroconf() zconf.register_service(info) app.run(host, port=80, debug=False) try: while True: sleep(0.1) except KeyboardInterrupt: pass finally: zconf.unregister_service(info) zconf.close()
family = socket.AF_INET # zeroconf module doesn't support IPv6 yet. gais = socket.getaddrinfo('', addr[1], proto=socket.IPPROTO_TCP, family=family) gai = gais[0] addr = gai[4] logging.warning(f'Advertizing on mDNS.') host_ip_bytes = socket.inet_pton(family, addr[0]) server_name = CONFIG['HOSTNAME'] + '.local.' # If we don't specify properties, it defaults to None, and asserts deep in sending. info = zeroconf.ServiceInfo(JOB_SERVER_MDNS_SERVICE, JOB_SERVER_MDNS_SERVICE, host_ip_bytes, addr[1], properties=b'', server=server_name) zc.register_service(info) if addr[0] == 'localhost': logging.error( 'Hosting job_server on localhost, which excludes remote hosts.') logging.warning('Hosting job_server at: {}'.format(addr)) server = nu.Server([addr], target=th_on_accept) server.listen_until_shutdown() try: while True:
def main(): logging.config.dictConfig(DEFAULT_CONFIG) parser = argparse.ArgumentParser() parser.add_argument('--port', nargs=1, help='port used on the current machine', type=int, default=[8000]) parser.add_argument('--pci_addr', nargs=1, help='pci address of the desired board', type=int, default=[0x01]) parser.add_argument('--ip_server', nargs=1, help='IP address of the server') parser.add_argument('--port_server', nargs=1, help='port of the server') args = parser.parse_args() port = int(args.port[0]) pci_addr = int(args.pci_addr[0]) addr = get_ip() ADC_idx = addr + '_' + str(port) unique_ADC_name = 'ADC' + '_' + ADC_idx + '._http._tcp.local.' pci_addr = pci_addr trtl = 'trtl-000' + str(pci_addr) if args.ip_server: ip_server = {'addr': args.ip_server[0]} else: ip_server = {'addr': None} port_server = None if args.port_server: port_server = args.port_server[0] else: port_server = server_expose_to_device_port serv_expose = ServerExpose(port, port_server, pci_addr, trtl, unique_ADC_name) zeroconf_service = None zeroconf_info = None if(args.ip_server is None): zeroconf_info = zeroconf.ServiceInfo("_http._tcp.local.", unique_ADC_name, zeroconf.socket.inet_aton(addr), 8000, properties={'addr': addr, 'port': str(port)}) zeroconf_service = zeroconf.Zeroconf() zeroconf_service.register_service(zeroconf_info) else: serv_expose.set_server_address(ip_server['addr']) data = {'function_name': 'register_ADC', 'args': [unique_ADC_name, addr, port]} serv_expose.server_publisher.send_message(data) try: print("Application starting") serv_expose.run() except KeyboardInterrupt: if(zeroconf_service is not None): zeroconf_service.unregister_service(zeroconf_info) else: data = {'function_name': 'unregister_ADC', 'args': [unique_ADC_name]} serv_expose.server_publisher.send_message(data) time.sleep(0.1) # otherwise the message is lost os._exit(1)
def main(): # catch SIGTERM (e.g., from start-stop-daemon) signal.signal(signal.SIGTERM, term_handler) # Create needed directories if not already there mkdir(config.PLOTDIR, config.REMOTE_USER) mkdir(config.TRACKDIR, config.REMOTE_USER) mkdir(config.DBGFRAMEDIR, config.REMOTE_USER) mkdir(config.ARCHIVEDIR, config.REMOTE_USER) # Get our external IP # Loop until we have one, because we can't run without it ip = None while ip is None: try: ip = get_routed_ip() except: # any error (in part because I'm not certain what is thrown # when this fails) ip = None # wait 5 seconds between attempts, assuming we're waiting # for the network to come up time.sleep(5) # make box_interface RPyC-able service = module2service(box_interface) # and RPyC it try: server = ThreadedServer(service, hostname='localhost', port=_PORT, protocol_config={"allow_public_attrs": True}) except socket.error as e: print("Error opening socket. atles_remote may already be running.") print(e) sys.exit(1) # ThreadedServer launches threads for incoming connections, but its main accept() loop is blocking, # so we put it in a separate thread. serverthread = threading.Thread(target=server.start) serverthread.daemon = True serverthread.start() atexit.register(server.close) print("RPC server started.") # register the service via MDNS/Bonjour boxname = get_boxname() gitstatus = git_status() gitshort = gitstatus[0] gitlong = gitstatus[1] info = zeroconf.ServiceInfo( "_atlesbox._tcp.local.", "%s._atlesbox._tcp.local." % boxname, socket.inet_aton(ip), _PORT, 0, 0, { 'name': boxname, 'appdir': str(config.BASEDIR), 'user': config.REMOTE_USER, 'hasdisplay': config.HAS_DISPLAY, 'gitshort': gitshort, 'gitlong': gitlong, }) zconf = zeroconf.Zeroconf([ip]) zconf.register_service(info) atexit.register(zconf.unregister_service, info) print("Service registered: %s port %d" % (ip, _PORT)) # wait until the server is done if sys.version_info[0] >= 3: serverthread.join() else: # In Python 2, a timeout is required for join() to not just # call a blocking C function (thus blocking the signal handler). # However, infinity works. serverthread.join(float('inf'))
def webRoot(): # We don't want Cherrypy writing temp files for no reason cherrypy._cpreqbody.Part.maxrambytes = 64 * 1024 from cherrypy import _cperror logging.getLogger("cherrypy.access").propagate = False from . import tagpoints from . import builtintags def tagErrorHandler(tag, f, val): try: from . import newevt if f.__module__ in newevt.eventsByModuleName: newevt.eventsByModuleName[f.__module__]._handle_exception() else: if not hasattr(f, "_kaithemFirstErrorMarker"): f._kaithemFirstErrorMarker = True messagebus.postMessage( "/system/notifications/errors", "First err in tag subscriber " + str(f) + " from " + str(f.__module__) + " to " + tag.name) except: print(traceback.format_exc(chain=True)) tagpoints.subscriberErrorHandlers = [tagErrorHandler] tagpoints.loadAllConfiguredTags(os.path.join(directories.vardir, "tags")) # We want a notification anytime every first error in a scheduled event. # This can stay even with real python logging, we want the front page notificaton. from . import scheduling def handleFirstError(f): "Callback to deal with the first error from any given event" m = f.__module__ messagebus.postMessage( "/system/notifications/errors", "Problem in scheduled event function: " + repr(f) + " in module: " + m + ", check logs for more info.") scheduling.handleFirstError = handleFirstError from . import logviewer try: from . import timesync except: logger.exception("Could not start time sync module") messagebus.postMessage( '/system/notifications/errors', """Failed to initialize the time sync module or zeroconf discovery This may be because you are using a python version older than 3.3, or because netifaces is not installed. Some features may not work correctly. """) from . import pages from . import weblogin from . import pages from . import ManageUsers from . import newevt from . import registry from . import persist persist.registry = registry from . import modules from . import modules_interface from . import settings from . import usrpages from . import systasks from . import widgets from . import alerts logger.info("Loaded core python code") from . import config as cfgmodule if not config['host'] == 'default': bindto = config['host'] else: if config['local-access-only']: bindto = '127.0.0.1' else: bindto = '::' mode = int( cfgmodule.argcmd.nosecurity) if cfgmodule.argcmd.nosecurity else None # limit nosecurity to localhost if mode == 1: bindto = '127.0.0.1' #cherrypy.process.servers.check_port(bindto, config['http-port'], timeout=1.0) #cherrypy.process.servers.check_port(bindto, config['https-port'], timeout=1.0) logger.info("Ports are free") MyExternalIPAdress = util.updateIP() if config['change-process-title']: try: import setproctitle setproctitle.setproctitle("kaithem") logger.info("setting process title") except: logger.warning("error setting process title") from ws4py.server.cherrypyserver import WebSocketPlugin, WebSocketTool from ws4py.websocket import EchoWebSocket, WebSocket WebSocketPlugin(cherrypy.engine).subscribe() cherrypy.tools.websocket = WebSocketTool() logger.info("activated websockets") sys.modules['kaithem'] = sys.modules['__main__'] # Load all modules from the active modules directory modules.initModules() logger.info("Loaded modules") def save(): if config['save-before-shutdown']: messagebus.postMessage('/system/notifications/important/', "System saving before shutting down") util.SaveAllState() # let the user choose to have the server save everything before a shutdown if config['save-before-shutdown']: atexit.register(save) cherrypy.engine.subscribe("exit", save) import collections # Super simple hacky cache. Maybe we should # Just mostly eliminate zips and use files directly? zipcache = collections.OrderedDict() # This class represents the "/" root of the web app class webapproot(): # This lets users mount stuff at arbitrary points, so long # As it doesn't conflict with anything. # foo.bar.com/foo maps to foo,bar,/,foo # bar.com/foo is just foo def _cp_dispatch(self, vpath): sdpath = pages.getSubdomain() vpath2 = vpath[:] # For binding the root of subdomains while vpath2: # Check for any subdomain specific handling. if tuple(sdpath + ['/'] + vpath2) in pages.nativeHandlers: # found match, remove N elements from the beginning of the path, # where n is the length of the "mountpoint", becsause the mountpoint # already consumed those. # Don't do it for the fake one we add just to make this loop work though for i in vpath2: vpath.pop(0) x = pages.nativeHandlers[tuple(sdpath + ['/'] + vpath2)] # Traverse to the actual function, if there is a match, else return the index. if vpath and hasattr(x, vpath[0]): x2 = getattr(x, vpath[0]) if hasattr(x2, 'exposed') and x2.exposed: vpath.pop(0) x = x2 if not isinstance(x, Exception): return x else: raise x if tuple(vpath2) in pages.nativeHandlers: # found match, remove N elements from the beginning of the path, # where n is the length of the "mountpoint", because the mountpoint # already consumed those for i in range(len(vpath2)): vpath.pop(0) x = pages.nativeHandlers[tuple(vpath2)] if vpath and hasattr(x, vpath[0]): x2 = getattr(x, vpath[0]) if vpath and hasattr(x2, 'exposed') and x2.exposed: vpath.pop(0) x = x2 if not isinstance(x, Exception): return x else: raise x if None in pages.nativeHandlers: return pages.nativeHandlers[None] # Successively remove things from the end till we get a # prefix match vpath2.pop(-1) return None @cherrypy.expose def default(self, *path, **data): return self._cp_dispatch(list(path))(*path, **data) # Keep the dispatcher from freaking out. The actual handling # Is done by a cherrypy tool. These just keeo cp_dispatch from being called # I have NO clue why the favicon doesn't have this issue. @cherrypy.expose def static(self, *path, **data): pass @cherrypy.expose def usr(self, *path, **data): pass @cherrypy.expose def index(self, *path, **data): pages.require("/admin/mainpage.view") cherrypy.response.cookie['LastSawMainPage'] = time.time() return pages.get_template('index.html').render( api=notifications.api, alertsapi=alerts.api) @cherrypy.expose def dropdownpanel(self, *path, **data): pages.require("/admin/mainpage.view") return pages.get_template('dropdownpanel.html').render( api=notifications.api, alertsapi=alerts.api) # @cherrypy.expose # def alerts(self, *path, **data): # pages.require("/admin/mainpage.view") # return pages.get_template('alerts.html').render(api=notifications.api, alertsapi=alerts.api) @cherrypy.expose def tagpoints(self, *path, show_advanced='', **data): # This page could be slow because of the db stuff, so we restrict it more pages.require("/admin/settings.edit") if "new_numtag" in data: pages.postOnly() return pages.get_template('settings/tagpoint.html').render( new_numtag=data['new_numtag'], tagname=data['new_numtag'], show_advanced=True) if "new_strtag" in data: pages.postOnly() return pages.get_template('settings/tagpoint.html').render( new_strtag=data['new_strtag'], tagname=data['new_strtag'], show_advanced=True) if data: pages.postOnly() if path: if not path[0] in tagpoints.allTags: raise ValueError("This tag does not exist") return pages.get_template('settings/tagpoint.html').render( tagName=path[0], data=data, show_advanced=show_advanced) else: return pages.get_template('settings/tagpoints.html').render( data=data) @cherrypy.expose def tagpointlog(self, *path, **data): # This page could be slow because of the db stuff, so we restrict it more pages.require("/admin/settings.edit") pages.postOnly() if not 'exportRows' in data: return pages.get_template('settings/tagpointlog.html').render( tagName=path[0], data=data) else: import pytz import datetime import dateutil.parser for i in tagpoints.allTags[path[0]]().configLoggers: if i.accumType == data['exportType']: tz = pytz.timezone( auth.getUserSetting(pages.getAcessingUser(), 'timezone')) logtime = tz.localize( dateutil.parser.parse( data['logtime'])).timestamp() raw = i.getDataRange(logtime, time.time() + 10000000, int(data['exportRows'])) if data['exportFormat'] == "csv.iso": cherrypy.response.headers[ 'Content-Disposition'] = 'attachment; filename="%s"' % path[ 0].replace( "/", "_").replace(".", "_").replace( ":", "_")[1:] + "_" + data[ 'exportType'] + tz.localize( dateutil.parser.parse( data['logtime']) ).isoformat() + ".csv" cherrypy.response.headers[ 'Content-Type'] = 'text/csv' d = [ "Time(ISO), " + path[0].replace(",", '') + ' <accum ' + data['exportType'] + '>' ] for i in raw: dt = datetime.datetime.fromtimestamp(i[0]) d.append(dt.isoformat() + "," + str(i[1])[:128]) return '\r\n'.join(d) + '\r\n' @cherrypy.expose def zipstatic(self, *path, **data): """ take everything but the last path element, use it as a path relative to static dir open as a zip, use the last as filename in the zip, return it. """ if ".." in path: return try: if path in zipcache: zipcache.move_to_end(path) return zipcache[path] except: print("err in cache for zip") cherrypy.response.headers['Cache-Control'] = "max-age=28800" m = mimetypes.guess_type(path[-1]) cherrypy.response.headers['Content-Type'] = m[0] p = os.path.join(ddn, 'static', *path[:-1]) with zipfile.ZipFile(p) as f: d = f.read(path[-1]) zipcache[path] = d if len(zipcache) > 64: zipcache.pop(last=False) return d @cherrypy.expose def pagelisting(self, *path, **data): # Pagelisting knows to only show pages if you have permissions return pages.get_template('pagelisting.html').render_unicode( modules=modules.ActiveModules) # docs, helpmenu, and license are just static pages. @cherrypy.expose def docs(self, *path, **data): if path: if path[0] == "thirdparty": p = os.path.normpath( os.path.join(directories.srcdir, "docs", "/".join(path))) if not p.startswith( os.path.join(directories.srcdir, "docs")): raise RuntimeError("Invalid URL") cherrypy.response.headers[ 'Content-Type'] = mimetypes.guess_type(p)[0] with open(p, "rb") as f: return (f.read()) return pages.get_template('help/' + path[0] + '.html').render( path=path, data=data) return pages.get_template('help/help.html').render() @cherrypy.expose def makohelp(self, *path, **data): return pages.get_template('help/makoreference.html').render() @cherrypy.expose def about(self, *path, **data): return pages.get_template('help/about.html').render( myip=MyExternalIPAdress) @cherrypy.expose def changelog(self, *path, **data): return pages.get_template('help/changes.html').render( myip=MyExternalIPAdress) @cherrypy.expose def helpmenu(self, *path, **data): return pages.get_template('help/index.html').render() @cherrypy.expose def license(self, *path, **data): return pages.get_template('help/license.html').render() @cherrypy.expose def aerolabs_blockrain(self, *path, **data): # There is no reason to be particularly concerned here, I have no reason not to trust # Aerolabs, this is just for the people that hate hidden games and such. cherrypy.response.headers[ 'Content-Security-Policy'] = "connect-src none" return pages.get_template('blockrain.html').render() class Errors(): @cherrypy.expose def permissionerror(self, ): cherrypy.response.status = 403 return pages.get_template('errors/permissionerror.html').render() @cherrypy.expose def alreadyexists(self, ): cherrypy.response.status = 400 return pages.get_template('errors/alreadyexists.html').render() @cherrypy.expose def gosecure(self, ): cherrypy.response.status = 426 return pages.get_template('errors/gosecure.html').render() @cherrypy.expose def loginerror(self, ): cherrypy.response.status = 400 return pages.get_template('errors/loginerror.html').render() @cherrypy.expose def nofoldermoveerror(self, ): cherrypy.response.status = 400 return pages.get_template('errors/nofoldermove.html').render() @cherrypy.expose def wrongmethod(self, ): cherrypy.response.status = 405 return pages.get_template('errors/wrongmethod.html').render() @cherrypy.expose def error(self, ): cherrypy.response.status = 500 return pages.get_template('errors/error.html').render( info="An Error Occurred") def cpexception(): cherrypy.response.status = 500 try: cherrypy.response.body = bytes( pages.get_template('errors/cperror.html').render( e=_cperror.format_exc(), mk=mako.exceptions.html_error_template().render().decode( )), 'utf8') except: cherrypy.response.body = bytes( pages.get_template('errors/cperror.html').render( e=_cperror.format_exc(), mk=""), 'utf8') import zipfile from . import devices, btadmin # There are lots of other objects ad classes represeting subfolders of the website so we attatch them root = webapproot() root.login = weblogin.LoginScreen() root.auth = ManageUsers.ManageAuthorization() root.modules = modules_interface.WebInterface() root.settings = settings.Settings() root.settings.bt = btadmin.WebUI() root.errors = Errors() root.pages = usrpages.KaithemPage() root.logs = messagelogging.WebInterface() root.notifications = notifications.WI() root.widgets = widgets.WebInterface() root.syslog = logviewer.WebInterface() root.devices = devices.WebDevices() if not os.path.abspath(__file__).startswith("/usr/bin"): sdn = os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "src") ddn = os.path.join( os.path.dirname(os.path.dirname(os.path.realpath(__file__))), "data") else: sdn = "/usr/lib/kaithem/src" ddn = "/usr/share/kaithem" def allow_upload(*args, **kwargs): # Only do the callback if needed. Assume it's really big if no header. if int(cherrypy.request.headers.get( "Content-Length", 2**32)) > cherrypy.request.body.maxbytes: cherrypy.request.body.maxbytes = cherrypy.request.config[ 'tools.allow_upload.f']() cherrypy.tools.allow_upload = cherrypy.Tool('before_request_body', allow_upload) site_config = { "request.body.maxbytes": 64 * 1024, "tools.encode.on": True, "tools.encode.encoding": 'utf-8', "tools.decode.on": True, "tools.decode.encoding": 'utf-8', 'request.error_response': cpexception, 'log.screen': config['cherrypy-log-stdout'], 'server.socket_host': bindto, 'server.socket_port': config['https-port'], 'server.ssl_module': 'builtin', 'server.ssl_certificate': os.path.join(directories.ssldir, 'certificate.cert'), 'server.ssl_private_key': os.path.join(directories.ssldir, 'certificate.key'), 'server.thread_pool': config['https-thread-pool'], 'engine.autoreload.frequency': 5, 'engine.autoreload.on': False, 'tools.allow_upload.on': True, 'tools.allow_upload.f': lambda: auth.getUserLimit(pages.getAcessingUser(), "web.maxbytes") or 64 * 1024, } wscfg = { 'tools.websocket.on': True, 'tools.websocket.handler_cls': widgets.websocket } wscfg_raw = { 'tools.websocket.on': True, 'tools.websocket.handler_cls': widgets.rawwebsocket } try: from hardline import ws4py_drayer wscfg3 = { 'tools.websocket.on': True, 'tools.websocket.handler_cls': ws4py_drayer.DrayerAPIWebSocket } root.drayer_api = ws4py_drayer.WebInterface() except Exception as e: wscfg3 = {} logging.exception("Could not load the Drayer WS API") messagebus.postMessage( "/system/notifications/errors", "Drayer Server API disabled due to loading error, see logs") cnf = { '/static': { 'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(ddn, 'static'), "tools.sessions.on": False, "tools.addheader.on": True, 'tools.expires.on': True, 'tools.expires.secs': 3600 + 48 # expire in 48 hours }, '/static/js': { 'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sdn, 'js'), "tools.sessions.on": False, "tools.addheader.on": True }, '/static/vue': { 'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sdn, 'vue'), "tools.sessions.on": False, "tools.addheader.on": True }, '/static/css': { 'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sdn, 'css'), "tools.sessions.on": False, "tools.addheader.on": True }, '/static/docs': { 'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sdn, 'docs'), "tools.sessions.on": False, "tools.addheader.on": True }, '/static/zip': { 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), "tools.addheader.on": True }, '/pages': { 'tools.allow_upload.on': True, 'tools.allow_upload.f': lambda: auth.getUserLimit(pages.getAcessingUser(), "web.maxbytes") or 64 * 1024, 'request.dispatch': cherrypy.dispatch.MethodDispatcher() }, '/widgets/ws': wscfg, '/widgets/wsraw': wscfg_raw, '/drayer_api': wscfg3 } if not config['favicon-png'] == "default": cnf['/favicon.png'] = { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(directories.datadir, "static", config['favicon-png']), 'tools.expires.on': True, 'tools.expires.secs': 3600 # expire in an hour } if not config['favicon-ico'] == "default": cnf['/favicon.ico'] = { 'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(directories.datadir, "static", config['favicon-ico']), 'tools.expires.on': True, 'tools.expires.secs': 3600 # expire in an hour } # Let the user create additional static directories for i in config['serve-static']: if i not in cnf: cnf["/usr/static/" + i] = { 'tools.staticdir.on': True, 'tools.staticdir.dir': config['serve-static'][i], "tools.sessions.on": False, "tools.addheader.on": True } def addheader(*args, **kwargs): "This function's only purpose is to tell the browser to cache requests for an hour" cherrypy.response.headers['Cache-Control'] = "max-age=28800" cherrypy.response.headers["Access-Control-Allow-Origin"] = "*" #del cherrypy.response.headers['Expires'] def pageloadnotify(*args, **kwargs): systasks.aPageJustLoaded() # As far as I can tell, this second server inherits everything from the "implicit" server # except what we override. server2 = cherrypy._cpserver.Server() server2.socket_port = config['http-port'] server2._socket_host = bindto server2.thread_pool = config['http-thread-pool'] server2.subscribe() cherrypy.config.update(site_config) cherrypy.tools.pageloadnotify = cherrypy.Tool('on_start_resource', pageloadnotify) cherrypy.config['tools.pageloadnotify.on'] = True cherrypy.tools.addheader = cherrypy.Tool('before_finalize', addheader) if hasattr(cherrypy.engine, 'signal_handler'): del cherrypy.engine.signal_handler.handlers['SIGUSR1'] cherrypy.engine.signal_handler.subscribe() cherrypy.tree.mount(root, config=cnf) if time.time() < 1420070400: messagebus.postMessage( '/system/notifications/errors', "System Clock is wrong, some features may not work properly.") if time.time() < util.min_time: messagebus.postMessage( '/system/notifications/errors', "System Clock may be wrong, or time has been set backwards at some point. If system clock is correct and this error does not go away, you can fix it manually be correcting folder name timestamps in the var dir." ) cherrypy.engine.start() # Unlike other shm stuff that only gets used after startup, this # Can be used both before and after we are fully loaded. # So we need to hand off everything to the user we will actually run as. # This is also useful for when someone directly modifies config # Over SSH and we want to adopt those changes to the kaithem usr. # It's even useful if we ever change the user for some reason. # We only do this if we start as root though. if not config['run-as-user'] == 'root' and getpass.getuser() == 'root': try: d = "/dev/shm/kaithem_pyx_" + config['run-as-user'] directories.rchown(d, config['run-as-user']) directories.rchown(directories.vardir, config['run-as-user']) directories.rchown(directories.logdir, config['run-as-user']) # Might as well own our own SSL dir, that way we can change certs via the webUI. directories.rchown(directories.ssldir, config['run-as-user']) directories.rchown(directories.usersdir, config['run-as-user']) directories.rchown(directories.regdir, config['run-as-user']) except: logger.exception("This is normal on non-unix") # If configured that way on unix, check if we are root and drop root. util.drop_perms(config['run-as-user'], config['run-as-group']) pylogginghandler.onUserChanged() messagebus.postMessage('/system/startup', 'System Initialized') messagebus.postMessage('/system/notifications/important', 'System Initialized') r = util.zeroconf import zeroconf # Register an NTP service desc = {} if cfg.config['advertise-webui']: try: import socket if not cfg.config['webui-servicename'] == "default": localserver_name = cfg.config['webui-servicename'] else: localserver_name = "kaithem_" + socket.gethostname() info = zeroconf.ServiceInfo( "_http._tcp.local.", localserver_name + "._http._tcp.local.", [None], cfg.config['http-port'], 0, 0, desc) r.register_service(info) info2 = zeroconf.ServiceInfo( "_https._tcp.local.", localserver_name + "._https._tcp.local.", [None], cfg.config['https-port'], 0, 0, desc) r.register_service(info2) except: logger.exception("Error advertising MDNS service") # Open a port to the outside world. Note that this can only be enabled through the webUI, # You are safe unless someone turns it on.. workers.do(systasks.doUPnP)