示例#1
0
    def __init__(self, filename, save_topic="/system/save"):
        # Save all changes immediately to /dev/shm, for crash recovery.
        if not os.path.exists("/dev/shm") or not recoveryDir:
            self.recoveryFile = None
        else:
            self.recoveryFile = recoveryPath(filename)

        if os.path.exists(filename):
            try:
                self.data = load(filename)
            except Exception:
                self.data = {}
                postMessage("/system/notifications/errors",
                            filename + "\n" + traceback.format_exc())
        else:
            self.data = {}
        try:
            if os.path.exists(self.recoveryFile):
                self.data = load(self.recoveryFile)
                dirty[filename] = self
        except:
            print(traceback.format_exc())
        self.filename = filename
        self.legacy_registry_key_mappings = {}
        self.lock = threading.RLock()
        self.noFileForEmpty = False
        self.private = True
        allFiles[filename] = self
        if save_topic:
            subscribe(save_topic, self.save)
示例#2
0
 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.")
示例#3
0
 def on_message(client, userdata, msg):
     try:
         s = self()
         # Everything must be fine, because we are getting messages
         messagebus.postMessage(s.busPrefix + "/in/" + msg.topic,
                                msg.payload,
                                )
         s.onStillConnected()
     except Exception:
         print(traceback.format_exc())
示例#4
0
    def test_sync(self):
        p = []

        def f(t, v):
            p.append((t, v))

        messagebus.subscribe("/test", f)
        messagebus.postMessage("/test", "foo", synchronous=True)

        self.assertEqual(p, [('/test', 'foo')])
示例#5
0
 def publish(self, topic, message, qos=2, encoding="json", retain=False):
     if encoding == 'json':
         message = json.dumps(message)
     elif encoding == 'msgpack':
         message = msgpack.packb(message, use_bin_type=True)
     elif encoding == 'utf8':
         message = message.encode("utf8")
     elif encoding == 'raw':
         pass
     else:
         raise ValueError("Invalid encoding!")
     messagebus.postMessage(self.busPrefix + "/out/" +
                            topic, message, annotation=(qos, retain))
示例#6
0
 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))
示例#7
0
    def test_async(self):
        p = []

        def f(v):
            p.append(v)

        messagebus.subscribe("test", f)
        messagebus.postMessage("test", "foo")

        s = 100
        while (not p) and s:
            time.sleep(0.01)
            s -= 1
        self.assertEqual(p, ['foo'])
示例#8
0
    def test_del(self):
        "Test unsubscribing by deleting a function"
        p = []

        def f(v):
            p.append(v)

        messagebus.subscribe("test", f)
        del f
        gc.collect()

        messagebus.postMessage("test", "foo", synchronous=True)

        self.assertEqual(p, [])
示例#9
0
def doScan():
    try:
        import rtmidi
    except ImportError:
        if once[0] == 0:
            messagebus.postMessage("/system/notifications/errors/","python-rtmidi is missing. Most MIDI related features will not work.")
            once[0]=1
        return


    m=rtmidi.RtMidiIn()
    torm =[]

    present =[(i,m.getPortName(i)) for i in range(m.getPortCount())]

    for i in allInputs:
        if not i in present:
            torm.append(i)
    for i in torm:
        del allInputs[i]


    for i in present:
        if not i in allInputs:
            try:
                m=rtmidi.RtMidiIn()
                m.openPort(i[0])
                def f(x,*a, d=i[1].replace(":",'_').replace("[",'').replace("]",'').replace(" ",'') ):
                    if isinstance(x,tuple):
                        try:
                            onMidiMessageTuple(x,d)
                        except:
                            print(traceback.format_exc())
                    else:
                        try:
                            onMidiMessage(x,d)
                        except:
                            print(traceback.format_exc())
                m.setCallback(f)
                allInputs[i]=(m,f)
            except:
                print("Can't use MIDI:"+str(i))
示例#10
0
def onMidiMessage(m,d):
    if m.isNoteOn():
        messagebus.postMessage("/midi/"+d,('noteon', m.getChannel(),m.getNoteNumber(),m.getVelocity()) )
        setTag("/midi/"+d+"/"+str(m.getChannel())+".note", m.getNoteNumber(), a= m.getVelocity())

    elif m.isNoteOff():
        messagebus.postMessage("/midi/"+d,('noteoff', m.getChannel(),m.getNoteNumber()))
        setTag("/midi/"+d+"/"+str(m.getChannel())+".note", 0, a= 0)

    elif m.isController():
        messagebus.postMessage("/midi/"+d, ('cc', m.getChannel(),m.getControllerNumber(),m.getControllerValue()))
        setTag("/midi/"+d+"/"+str(m.getChannel())+".cc["+str(m.getControllerNumber())+']', m.getControllerValue(), a= 0)

    elif m.isPitchWheel():
        messagebus.postMessage("/midi/"+d, ('pitch', m.getChannel(),m.getPitchWheelValue()))
        setTag14("/midi/"+d+"/"+str(m.getChannel())+".pitch", m.getPitchWheelValue(), a= 0)
示例#11
0
def onMidiMessageTuple(m,d):
    sb=m[0][0]
    code = sb &240
    ch = sb&15
    a = m[0][1]
    b = m[0][2]

    if code==144:
        messagebus.postMessage("/midi/"+d,('noteon', ch,a,b) )
        setTag("/midi/"+d+"/"+str(ch)+".note",a,  a= b)

    elif code==128:
        messagebus.postMessage("/midi/"+d,('noteoff', ch,a,b) )
        setTag("/midi/"+d+"/"+str(ch)+".note", 0, a= 0)

    elif code==224:
        messagebus.postMessage("/midi/"+d,('pitch', ch,a,b) )
        setTag14("/midi/"+d+"/"+str(ch)+".pitch", a+b*128, a= 0)

    elif code==176:
        messagebus.postMessage("/midi/"+d, ('cc',a,b))
        setTag("/midi/"+d+"/"+str(ch)+".cc["+str(a)+"]", b, a= 0)
def onStart():
    messagebus.postMessage("/system/notifications/important",
                           "JACK server connected")
    messagebus.postMessage("/system/sound/jackstart", "JACK server connected")
示例#13
0
selected_user = pwd.getpwuid(os.geteuid()).pw_name

recoveryDir: str = os.path.join("/dev/shm/SculleryRFRecovery", selected_user)
if os.path.exists("/dev/shm"):
    if not os.path.exists("/dev/shm/SculleryRFRecovery"):
        os.mkdir("/dev/shm/SculleryRFRecovery")
    if not os.path.exists(recoveryDir):
        os.mkdir(recoveryDir)
        # Nobody else van put stuff in there!!!
        os.chmod(recoveryDir, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC)
        p = getpwnam(selected_user)
        os.chown(recoveryDir, p.pw_uid, p.pw_gid)
    else:
        if not getpwuid(os.stat(recoveryDir).st_uid).pw_name == selected_user:
            postMessage(
                "/system/notifications/errors",
                "Hacking Detected? " + recoveryDir + " not owned by this user")
            recoveryDir = ''
else:
    recoveryDir = ''


def recoveryPath(f):
    if f.startswith("/"):
        f = f[1:]
    return os.path.join(recoveryDir, urllib.parse.quote(f, safe="/"))


class SharedStateFile():
    """
        This is a dict that is savable when the system state gets saved.
示例#14
0
    logger.info(
        "Kaithem users set up. Now exiting(May take a few seconds. You may start the service manually or via systemd/init"
    )
    cherrypy.engine.exit()
    sys.exit()

plugins = {}
try:
    for i in os.listdir(pathsetup.startupPluginsPath):
        try:
            plugins[i] = importlib.import_module(i)
            logger.info("Loaded plugin " + i)
        except Exception:
            logger.exception("Error loading plugin " + i)
            messagebus.postMessage(
                '/system/notifications/errors',
                "Error loading plugin " + i + "\n" + traceback.format_exc())
except Exception:
    messagebus.postMessage('/system/notifications/errors',
                           "Error loading plugins")
    logger.exception("Error loading plugins")

#Making drayer a standard, always-available part of Kaithem.
#This doesn't mean we actually do anything with it, we don't expose
#the service to the world till requested.
try:
    from . import drayer
    drayer.loadDrayerSetup()
except:
    logger.exception("Error loading DrayerDB Local Node")
示例#15
0
 def save():
     if config['save-before-shutdown']:
         messagebus.postMessage('/system/notifications/important/',
                                "System saving before shutting down")
         util.SaveAllState()
示例#16
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)
示例#17
0
 def onDisconnected(self):
     logging.warning("A connection has disconnected from MQTT server: " + self.server)
     if self.isConnected:
         messagebus.postMessage(self.localStatusTopic, "disconnected")
     self.isConnected = False
示例#18
0
 def onStillConnected(self):
     if not self.isConnected:
         messagebus.postMessage(self.localStatusTopic, "connected")
     self.isConnected = True
     pass
            # Check if process name contains the given name string.
            if processName.lower() in proc.name().lower():
                if proc.uids()[0] == os.geteuid():
                    return True
        except (psutil.NoSuchProcess, psutil.AccessDenied,
                psutil.ZombieProcess):
            pass
    return False


pipewireprocess1 = None
pipewireprocess2 = None

#Assume pipewire is good enough to be jack
if checkIfProcessRunning("pipewire"):
    messagebus.postMessage("/system/jack/started", "Actually, it's pipewire")


def reloadSettings():
    global pipewireprocess1, pipewireprocess2

    scullery.jacktools.usbPeriodSize = settings.get("usbPeriodSize", -1)
    scullery.jacktools.usbLatency = settings.get("usbLatency", -1)
    scullery.jacktools.usbPeriods = settings.get("usbPeriods", -1)
    scullery.jacktools.usbQuality = settings.get("usbQuality", 0)

    scullery.jacktools.periodSize = settings.get("jackPeriodSize", 512)
    scullery.jacktools.jackPeriods = max(settings.get("jackPeriods", 3), 3)
    scullery.jacktools.sharePulse = settings.get("sharePulse", None)
    scullery.jacktools.jackDevice = settings.get("jackDevice", "hw:0,0")
示例#20
0
 def out_handler(topic, message, timestamp, annotation):
     t = topic[len(self.busPrefix + "/out/"):]
     messagebus.postMessage(self.busPrefix + "/in/" + t,
                            message)
def onFail():
    messagebus.postMessage("/system/notifications/errors",
                           "JACK server has failed")
def b():
    selftest[1] = time.monotonic()
    if selftest[0]< time.monotonic()-75:
        if lastpost[0]< time.monotonic()-600:
            lastpost[0]=time.monotonic()
            messagebus.postMessage("/system/notifications/errors", "Something caused a scheduler continual selftest function not to run.")
def reloadSettings():
    global pipewireprocess1, pipewireprocess2

    scullery.jacktools.usbPeriodSize = settings.get("usbPeriodSize", -1)
    scullery.jacktools.usbLatency = settings.get("usbLatency", -1)
    scullery.jacktools.usbPeriods = settings.get("usbPeriods", -1)
    scullery.jacktools.usbQuality = settings.get("usbQuality", 0)

    scullery.jacktools.periodSize = settings.get("jackPeriodSize", 512)
    scullery.jacktools.jackPeriods = max(settings.get("jackPeriods", 3), 3)
    scullery.jacktools.sharePulse = settings.get("sharePulse", None)
    scullery.jacktools.jackDevice = settings.get("jackDevice", "hw:0,0")

    if not (checkIfProcessRunning("pipewire")
            or settings.get("jackMode", None) == "pipewire"):
        scullery.jacktools.useAdditionalSoundcards = settings.get(
            "useAdditionalSoundcards", "yes")
    else:
        #Let pipewire do it all for us!!
        scullery.jacktools.useAdditionalSoundcards = "no"

    scullery.jacktools.usePulse = settings.get("sharePulse", None) != "disable"

    scullery.jacktools.dummy = False

    if not settings.get("jackMode", None) == "pipewire":
        if pipewireprocess1:
            pipewireprocess1.kill()
            pipewireprocess1 = None
        if pipewireprocess2:
            pipewireprocess2.kill()
            pipewireprocess2 = None

    if checkIfProcessRunning("pipewire") or settings.get("jackMode",
                                                         None) == "use":
        scullery.jacktools.manageJackProcess = False

    elif settings.get("jackMode", None) == "pipewire":
        scullery.jacktools.manageJackProcess = False
        import subprocess

        if os.geteuid() != 0:
            subprocess.call("systemctl --user start pipewire", shell=True)
        else:
            if (not pipewireprocess1) or (not pipewireprocess1.poll() == None):
                pipewireprocess1 = subprocess.Popen(
                    "dbus-launch --sh-syntax --exit-with-session; pipewire;",
                    shell=True)

            if (not pipewireprocess2) or (not pipewireprocess2.poll() == None):
                pipewireprocess2 = subprocess.Popen("pipewire-media-session",
                                                    shell=True)
                import time
                time.sleep(2)
                messagebus.postMessage("/system/jack/started",
                                       "Actually, it's pipewire")

    elif settings.get("jackMode", None) == "manage":
        scullery.jacktools.manageJackProcess = True
    elif settings.get("jackMode", None) == "dummy":
        scullery.jacktools.manageJackProcess = True
        scullery.jacktools.dummy = True