]> glassweightruler.freedombox.rocks Git - waydroid.git/blobdiff - tools/actions/container_manager.py
lxc: Mount DMA-BUF Heaps
[waydroid.git] / tools / actions / container_manager.py
index d6ce155f86935b8fc23849e26ab20d1e62bad94b..1c67eb5e1ff99a466c9c8d6c6c1861eb3dbcb97b 100644 (file)
@@ -11,108 +11,98 @@ import uuid
 import tools.config
 from tools import helpers
 from tools import services
+import dbus
+import dbus.service
+import dbus.exceptions
+from gi.repository import GLib
+
+class DbusContainerManager(dbus.service.Object):
+    def __init__(self, looper, bus, object_path, args):
+        self.args = args
+        self.looper = looper
+        dbus.service.Object.__init__(self, bus, object_path)
+
+    @dbus.service.method("id.waydro.ContainerManager", in_signature='a{ss}', out_signature='', sender_keyword="sender", connection_keyword="conn")
+    def Start(self, session, sender, conn):
+        dbus_info = dbus.Interface(conn.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus/Bus", False), "org.freedesktop.DBus")
+        uid = dbus_info.GetConnectionUnixUser(sender)
+        if str(uid) not in ["0", session["user_id"]]:
+            raise RuntimeError("Cannot start a session on behalf of another user")
+        pid = dbus_info.GetConnectionUnixProcessID(sender)
+        if str(uid) != "0" and str(pid) != session["pid"]:
+            raise RuntimeError("Invalid session pid")
+        do_start(self.args, session)
+
+    @dbus.service.method("id.waydro.ContainerManager", in_signature='b', out_signature='')
+    def Stop(self, quit_session):
+        stop(self.args, quit_session)
+
+    @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
+    def Freeze(self):
+        freeze(self.args)
+
+    @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
+    def Unfreeze(self):
+        unfreeze(self.args)
+
+    @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='a{ss}')
+    def GetSession(self):
+        try:
+            session = self.args.session
+            session["state"] = helpers.lxc.status(self.args)
+            return session
+        except AttributeError:
+            return {}
+
+def service(args, looper):
+    dbus_obj = DbusContainerManager(looper, dbus.SystemBus(), '/ContainerManager', args)
+    looper.run()
+
+def set_permissions(args, perm_list=None, mode="777"):
+    def chmod(path, mode):
+        if os.path.exists(path):
+            command = ["chmod", mode, "-R", path]
+            tools.helpers.run.user(args, command, check=False)
 
+    # Nodes list
+    if not perm_list:
+        perm_list = [
+            "/dev/ashmem",
+
+            # sw_sync for HWC
+            "/dev/sw_sync",
+            "/sys/kernel/debug/sync/sw_sync",
+
+            # Media
+            "/dev/Vcodec",
+            "/dev/MTK_SMI",
+            "/dev/mdp_sync",
+            "/dev/mtk_cmdq",
+
+            # Graphics
+            "/dev/graphics",
+            "/dev/pvr_sync",
+            "/dev/ion",
+        ]
+
+        # DRM render nodes
+        perm_list.extend(glob.glob("/dev/dri/renderD*"))
+        # Framebuffers
+        perm_list.extend(glob.glob("/dev/fb*"))
+        # Videos
+        perm_list.extend(glob.glob("/dev/video*"))
+        # DMA-BUF Heaps
+        perm_list.extend(glob.glob("/dev/dma_heap/*"))
+
+    for path in perm_list:
+        chmod(path, mode)
 
 def start(args):
-    def make_prop(full_props_path):
-        def add_prop(key, cfg_key):
-            value = session_cfg["session"][cfg_key]
-            if value != "None":
-                value = value.replace("/mnt/", "/mnt_extra/")
-                props.append(key + "=" + value)
-
-        if not os.path.isfile(args.work + "/waydroid_base.prop"):
-            raise RuntimeError("waydroid_base.prop Not found")
-        with open(args.work + "/waydroid_base.prop") as f:
-            props = f.read().splitlines()
-        if not props:
-            raise RuntimeError("waydroid_base.prop is broken!!?")
-
-        add_prop("waydroid.host.user", "user_name")
-        add_prop("waydroid.host.uid", "user_id")
-        add_prop("waydroid.host.gid", "group_id")
-        add_prop("waydroid.xdg_runtime_dir", "xdg_runtime_dir")
-        add_prop("waydroid.pulse_runtime_path", "pulse_runtime_path")
-        add_prop("waydroid.wayland_display", "wayland_display")
-        if which("waydroid-sensord") is None:
-            props.append("waydroid.stub_sensors_hal=1")
-        dpi = session_cfg["session"]["lcd_density"]
-        if dpi != "0":
-            props.append("ro.sf.lcd_density=" + dpi)
-
-        final_props = open(full_props_path, "w")
-        for prop in props:
-            final_props.write(prop + "\n")
-        final_props.close()
-        os.chmod(full_props_path, 0o644)
-
-    def set_permissions(perm_list=None, mode="777"):
-        def chmod(path, mode):
-            if os.path.exists(path):
-                command = ["chmod", mode, "-R", path]
-                tools.helpers.run.user(args, command, check=False)
-
-        # Nodes list
-        if not perm_list:
-            perm_list = [
-                "/dev/ashmem",
-
-                # sw_sync for HWC
-                "/dev/sw_sync",
-                "/sys/kernel/debug/sync/sw_sync",
-
-                # Media
-                "/dev/Vcodec",
-                "/dev/MTK_SMI",
-                "/dev/mdp_sync",
-                "/dev/mtk_cmdq",
-
-                # Graphics
-                "/dev/dri",
-                "/dev/graphics",
-                "/dev/pvr_sync",
-                "/dev/ion",
-            ]
-
-            # Framebuffers
-            perm_list.extend(glob.glob("/dev/fb*"))
-            # Videos
-            perm_list.extend(glob.glob("/dev/video*"))
-
-        for path in perm_list:
-            chmod(path, mode)
-
-    def set_aidl_version():
-        cfg = tools.config.load(args)
-        android_api = 0
-        try:
-            android_api = int(helpers.props.file_get(args,
-                    tools.config.defaults["rootfs"] + "/system/build.prop",
-                    "ro.build.version.sdk"))
-        except:
-            logging.error("Failed to parse android version from system.img")
-
-        if android_api < 28:
-            binder_protocol = "aidl"
-            sm_protocol =     "aidl"
-        elif android_api < 30:
-            binder_protocol = "aidl2"
-            sm_protocol =     "aidl2"
-        elif android_api < 31:
-            binder_protocol = "aidl3"
-            sm_protocol =     "aidl3"
-        else:
-            binder_protocol = "aidl3"
-            sm_protocol =     "aidl4"
-
-        cfg["waydroid"]["binder_protocol"] = binder_protocol
-        cfg["waydroid"]["service_manager_protocol"] = sm_protocol
-        tools.config.save(args, cfg)
-
-    def signal_handler(sig, frame):
-        services.hardware_manager.stop(args)
-        stop(args)
-        sys.exit(0)
+    try:
+        name = dbus.service.BusName("id.waydro.Container", dbus.SystemBus(), do_not_queue=True)
+    except dbus.exceptions.NameExistsException:
+        logging.error("Container service is already running")
+        return
 
     status = helpers.lxc.status(args)
     if status == "STOPPED":
@@ -121,107 +111,97 @@ def start(args):
         if cfg["waydroid"]["vendor_type"] == "MAINLINE":
             if helpers.drivers.probeBinderDriver(args) != 0:
                 logging.error("Failed to load Binder driver")
-            if helpers.drivers.probeAshmemDriver(args) != 0:
-                logging.error("Failed to load Ashmem driver")
+            helpers.drivers.probeAshmemDriver(args)
         helpers.drivers.loadBinderNodes(args)
-        set_permissions([
+        set_permissions(args, [
             "/dev/" + args.BINDER_DRIVER,
             "/dev/" + args.VNDBINDER_DRIVER,
             "/dev/" + args.HWBINDER_DRIVER
         ], "666")
 
-        if os.path.exists(tools.config.session_defaults["config_path"]):
-            session_cfg = tools.config.load_session()
-            if session_cfg["session"]["state"] != "STOPPED":
-                logging.warning("Found session config on state: {}, restart session".format(
-                    session_cfg["session"]["state"]))
-                os.remove(tools.config.session_defaults["config_path"])
-        logging.debug("Container manager is waiting for session to load")
-        while not os.path.exists(tools.config.session_defaults["config_path"]):
-            time.sleep(1)
-        
-        # Load session configs
-        session_cfg = tools.config.load_session()
-        
-        # Generate props
-        make_prop(args.work + "/waydroid.prop")
+        mainloop = GLib.MainLoop()
 
-        # Networking
-        command = [tools.config.tools_src +
-                   "/data/scripts/waydroid-net.sh", "start"]
-        tools.helpers.run.user(args, command, check=False)
+        def sigint_handler(data):
+            stop(args)
+            mainloop.quit()
 
-        # Sensors
-        if which("waydroid-sensord"):
-            tools.helpers.run.user(
-                args, ["waydroid-sensord", "/dev/" + args.HWBINDER_DRIVER], output="background")
+        GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, sigint_handler, None)
+        GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, sigint_handler, None)
+        service(args, mainloop)
+    else:
+        logging.error("WayDroid container is {}".format(status))
 
-        # Mount rootfs
-        helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"])
+def do_start(args, session):
+    if "session" in args:
+        raise RuntimeError("Already tracking a session")
 
-        set_aidl_version()
+    # Networking
+    command = [tools.config.tools_src +
+               "/data/scripts/waydroid-net.sh", "start"]
+    tools.helpers.run.user(args, command)
 
-        # Mount data
-        helpers.mount.bind(args, session_cfg["session"]["waydroid_data"],
-                           tools.config.defaults["data"])
+    # Sensors
+    if which("waydroid-sensord"):
+        tools.helpers.run.user(
+            args, ["waydroid-sensord", "/dev/" + args.HWBINDER_DRIVER], output="background")
 
-        # Cgroup hacks
-        if which("start"):
-            command = ["start", "cgroup-lite"]
-            tools.helpers.run.user(args, command, check=False)
-        command = ["umount", "-l", "/sys/fs/cgroup/schedtune"]
+    # Cgroup hacks
+    if which("start"):
+        command = ["start", "cgroup-lite"]
         tools.helpers.run.user(args, command, check=False)
 
-        #TODO: remove NFC hacks
-        if which("stop"):
-            command = ["stop", "nfcd"]
+    # Keep schedtune around in case nesting is supported
+    if os.path.ismount("/sys/fs/cgroup/schedtune"):
+        try:
+            os.mkdir("/sys/fs/cgroup/schedtune/probe0")
+            os.mkdir("/sys/fs/cgroup/schedtune/probe0/probe1")
+        except:
+            command = ["umount", "-l", "/sys/fs/cgroup/schedtune"]
             tools.helpers.run.user(args, command, check=False)
+        finally:
+            if os.path.exists("/sys/fs/cgroup/schedtune/probe0/probe1"):
+                os.rmdir("/sys/fs/cgroup/schedtune/probe0/probe1")
+            if os.path.exists("/sys/fs/cgroup/schedtune/probe0"):
+                os.rmdir("/sys/fs/cgroup/schedtune/probe0")
+
+    #TODO: remove NFC hacks
+    if which("stop"):
+        command = ["stop", "nfcd"]
+        tools.helpers.run.user(args, command, check=False)
+    elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-active", "-q", "nfcd"], check=False) == 0):
+        command = ["systemctl", "stop", "nfcd"]
+        tools.helpers.run.user(args, command, check=False)
 
-        # Set permissions
-        set_permissions()
-        
-        helpers.lxc.start(args)
-        session_cfg["session"]["state"] = helpers.lxc.status(args)
-        timeout = 10
-        while session_cfg["session"]["state"] != "RUNNING" and timeout > 0:
-            session_cfg["session"]["state"] = helpers.lxc.status(args)
-            logging.info(
-                "waiting {} seconds for container to start...".format(timeout))
-            timeout = timeout - 1
-            time.sleep(1)
-        if session_cfg["session"]["state"] != "RUNNING":
-            raise OSError("container failed to start")
-        tools.config.save_session(session_cfg)
-
-        services.hardware_manager.start(args)
-
-        signal.signal(signal.SIGINT, signal_handler)
-        while os.path.exists(tools.config.session_defaults["config_path"]):
-            session_cfg = tools.config.load_session()
-            if session_cfg["session"]["state"] == "STOPPED":
-                services.hardware_manager.stop(args)
-                sys.exit(0)
-            elif session_cfg["session"]["state"] == "UNFREEZE":
-                session_cfg["session"]["state"] = helpers.lxc.status(args)
-                tools.config.save_session(session_cfg)
-                unfreeze(args)
-            time.sleep(1)
-
-        logging.warning("session manager stopped, stopping container and waiting...")
-        stop(args)
-        services.hardware_manager.stop(args)
-        start(args)
-    else:
-        logging.error("WayDroid container is {}".format(status))
+    # Set permissions
+    set_permissions(args)
 
-def stop(args):
-    status = helpers.lxc.status(args)
-    if status != "STOPPED":
-        helpers.lxc.stop(args)
-        if os.path.exists(tools.config.session_defaults["config_path"]):
-            session_cfg = tools.config.load_session()
-            session_cfg["session"]["state"] = helpers.lxc.status(args)
-            tools.config.save_session(session_cfg)
+    # Create session-specific LXC config file
+    helpers.lxc.generate_session_lxc_config(args, session)
+    # Backwards compatibility
+    with open(tools.config.defaults["lxc"] + "/waydroid/config") as f:
+        if "config_session" not in f.read():
+            helpers.mount.bind(args, session["waydroid_data"],
+                               tools.config.defaults["data"])
+
+    # Mount rootfs
+    cfg = tools.config.load(args)
+    helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"], session)
+
+    helpers.protocol.set_aidl_version(args)
+
+    helpers.lxc.start(args)
+    services.hardware_manager.start(args)
+
+    args.session = session
+
+def stop(args, quit_session=True):
+    try:
+        services.hardware_manager.stop(args)
+        status = helpers.lxc.status(args)
+        if status != "STOPPED":
+            helpers.lxc.stop(args)
+            while helpers.lxc.status(args) != "STOPPED":
+                pass
 
         # Networking
         command = [tools.config.tools_src +
@@ -232,6 +212,9 @@ def stop(args):
         if which("start"):
             command = ["start", "nfcd"]
             tools.helpers.run.user(args, command, check=False)
+        elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-enabled", "-q", "nfcd"], check=False) == 0):
+            command = ["systemctl", "start", "nfcd"]
+            tools.helpers.run.user(args, command, check=False)
 
         # Sensors
         if which("waydroid-sensord"):
@@ -244,11 +227,21 @@ def stop(args):
         # Umount rootfs
         helpers.images.umount_rootfs(args)
 
-        # Umount data
-        helpers.mount.umount_all(args, tools.config.defaults["data"])
-
-    else:
-        logging.error("WayDroid container is {}".format(status))
+        # Backwards compatibility
+        try:
+            helpers.mount.umount_all(args, tools.config.defaults["data"])
+        except:
+            pass
+
+        if "session" in args:
+            if quit_session:
+                try:
+                    os.kill(int(args.session["pid"]), signal.SIGUSR1)
+                except:
+                    pass
+            del args.session
+    except:
+        pass
 
 def restart(args):
     status = helpers.lxc.status(args)
@@ -262,10 +255,8 @@ def freeze(args):
     status = helpers.lxc.status(args)
     if status == "RUNNING":
         helpers.lxc.freeze(args)
-        if os.path.exists(tools.config.session_defaults["config_path"]):
-            session_cfg = tools.config.load_session()
-            session_cfg["session"]["state"] = helpers.lxc.status(args)
-            tools.config.save_session(session_cfg)
+        while helpers.lxc.status(args) == "RUNNING":
+            pass
     else:
         logging.error("WayDroid container is {}".format(status))
 
@@ -273,9 +264,5 @@ def unfreeze(args):
     status = helpers.lxc.status(args)
     if status == "FROZEN":
         helpers.lxc.unfreeze(args)
-        if os.path.exists(tools.config.session_defaults["config_path"]):
-            session_cfg = tools.config.load_session()
-            session_cfg["session"]["state"] = helpers.lxc.status(args)
-            tools.config.save_session(session_cfg)
-    else:
-        logging.error("WayDroid container is {}".format(status))
+        while helpers.lxc.status(args) == "FROZEN":
+            pass