X-Git-Url: https://glassweightruler.freedombox.rocks/gitweb/waydroid.git/blobdiff_plain/571ab3e4c67a2c7d7ef031c00047444abb525b2e..d0ca6a32a9eded6e2fbc7dc1fe2f70b503dde8dc:/tools/actions/container_manager.py diff --git a/tools/actions/container_manager.py b/tools/actions/container_manager.py index d6ce155..bcefc08 100644 --- a/tools/actions/container_manager.py +++ b/tools/actions/container_manager.py @@ -11,108 +11,95 @@ 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", -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) + # sw_sync for HWC + "/dev/sw_sync", + "/sys/kernel/debug/sync/sw_sync", - # 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) + # 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 start(args): + 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 +108,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 +209,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 +224,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 +252,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 +261,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