Пример #1
0
    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)
Пример #2
0
    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}")
Пример #3
0
    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)
Пример #4
0
 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()
Пример #5
0
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')
Пример #6
0
    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
Пример #7
0
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
Пример #8
0
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()
Пример #9
0
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()
Пример #10
0
    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)
Пример #11
0
    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)
Пример #13
0
    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
Пример #14
0
    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])
Пример #15
0
    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)
Пример #16
0
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()
Пример #17
0
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()
Пример #18
0
    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:
Пример #19
0
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)
Пример #20
0
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'))
Пример #21
0
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)