]> glassweightruler.freedombox.rocks Git - waydroid.git/blob - tools/actions/container_manager.py
debian/control: add Depends on pipewire-pulse | pulseaudio
[waydroid.git] / tools / actions / container_manager.py
1 # Copyright 2021 Erfan Abdi
2 # SPDX-License-Identifier: GPL-3.0-or-later
3 from shutil import which
4 import logging
5 import os
6 import glob
7 import signal
8 import tools.config
9 from tools import helpers
10 from tools import services
11 import dbus
12 import dbus.service
13 import dbus.exceptions
14 from gi.repository import GLib
15
16 class DbusContainerManager(dbus.service.Object):
17 def __init__(self, looper, bus, object_path, args):
18 self.args = args
19 self.looper = looper
20 dbus.service.Object.__init__(self, bus, object_path)
21
22 @dbus.service.method("id.waydro.ContainerManager", in_signature='a{ss}', out_signature='', sender_keyword="sender", connection_keyword="conn")
23 def Start(self, session, sender, conn):
24 dbus_info = dbus.Interface(conn.get_object("org.freedesktop.DBus", "/org/freedesktop/DBus/Bus", False), "org.freedesktop.DBus")
25 uid = dbus_info.GetConnectionUnixUser(sender)
26 if str(uid) not in ["0", session["user_id"]]:
27 raise RuntimeError("Cannot start a session on behalf of another user")
28 pid = dbus_info.GetConnectionUnixProcessID(sender)
29 if str(uid) != "0" and str(pid) != session["pid"]:
30 raise RuntimeError("Invalid session pid")
31 do_start(self.args, session)
32
33 @dbus.service.method("id.waydro.ContainerManager", in_signature='b', out_signature='')
34 def Stop(self, quit_session):
35 stop(self.args, quit_session)
36
37 @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
38 def Freeze(self):
39 freeze(self.args)
40
41 @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='')
42 def Unfreeze(self):
43 unfreeze(self.args)
44
45 @dbus.service.method("id.waydro.ContainerManager", in_signature='', out_signature='a{ss}')
46 def GetSession(self):
47 try:
48 session = self.args.session
49 session["state"] = helpers.lxc.status(self.args)
50 return session
51 except AttributeError:
52 return {}
53
54 def service(args, looper):
55 dbus_obj = DbusContainerManager(looper, dbus.SystemBus(), '/ContainerManager', args)
56 looper.run()
57
58 def set_permissions(args, perm_list=None, mode="777"):
59 def chmod(path, mode):
60 if os.path.exists(path):
61 command = ["chmod", mode, "-R", path]
62 tools.helpers.run.user(args, command, check=False)
63
64 # Nodes list
65 if not perm_list:
66 perm_list = [
67 "/dev/ashmem",
68
69 # sw_sync for HWC
70 "/dev/sw_sync",
71 "/sys/kernel/debug/sync/sw_sync",
72
73 # Media
74 "/dev/Vcodec",
75 "/dev/MTK_SMI",
76 "/dev/mdp_sync",
77 "/dev/mtk_cmdq",
78
79 # Graphics
80 "/dev/graphics",
81 "/dev/pvr_sync",
82 "/dev/ion",
83 ]
84
85 # DRM render nodes
86 perm_list.extend(glob.glob("/dev/dri/renderD*"))
87 # Framebuffers
88 perm_list.extend(glob.glob("/dev/fb*"))
89 # Videos
90 perm_list.extend(glob.glob("/dev/video*"))
91 # DMA-BUF Heaps
92 perm_list.extend(glob.glob("/dev/dma_heap/*"))
93
94 for path in perm_list:
95 chmod(path, mode)
96
97 def start(args):
98 try:
99 name = dbus.service.BusName("id.waydro.Container", dbus.SystemBus(), do_not_queue=True)
100 except dbus.exceptions.NameExistsException:
101 logging.error("Container service is already running")
102 return
103
104 status = helpers.lxc.status(args)
105 if status == "STOPPED":
106 # Load binder and ashmem drivers
107 cfg = tools.config.load(args)
108 if cfg["waydroid"]["vendor_type"] == "MAINLINE":
109 if helpers.drivers.probeBinderDriver(args) != 0:
110 logging.error("Failed to load Binder driver")
111 helpers.drivers.probeAshmemDriver(args)
112 helpers.drivers.loadBinderNodes(args)
113 set_permissions(args, [
114 "/dev/" + args.BINDER_DRIVER,
115 "/dev/" + args.VNDBINDER_DRIVER,
116 "/dev/" + args.HWBINDER_DRIVER
117 ], "666")
118
119 mainloop = GLib.MainLoop()
120
121 def sigint_handler(data):
122 stop(args)
123 mainloop.quit()
124
125 GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGINT, sigint_handler, None)
126 GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, sigint_handler, None)
127 service(args, mainloop)
128 else:
129 logging.error("WayDroid container is {}".format(status))
130
131 def do_start(args, session):
132 if "session" in args:
133 raise RuntimeError("Already tracking a session")
134
135 logging.info("Starting up container for a new session")
136
137 # Networking
138 command = [tools.config.tools_src +
139 "/data/scripts/waydroid-net.sh", "start"]
140 tools.helpers.run.user(args, command)
141
142 # Sensors
143 if which("waydroid-sensord"):
144 tools.helpers.run.user(
145 args, ["waydroid-sensord", "/dev/" + args.HWBINDER_DRIVER], output="background")
146
147 # Cgroup hacks
148 if which("start"):
149 command = ["start", "cgroup-lite"]
150 tools.helpers.run.user(args, command, check=False)
151
152 # Keep schedtune around in case nesting is supported
153 if os.path.ismount("/sys/fs/cgroup/schedtune"):
154 try:
155 os.mkdir("/sys/fs/cgroup/schedtune/probe0")
156 os.mkdir("/sys/fs/cgroup/schedtune/probe0/probe1")
157 except:
158 command = ["umount", "-l", "/sys/fs/cgroup/schedtune"]
159 tools.helpers.run.user(args, command, check=False)
160 finally:
161 if os.path.exists("/sys/fs/cgroup/schedtune/probe0/probe1"):
162 os.rmdir("/sys/fs/cgroup/schedtune/probe0/probe1")
163 if os.path.exists("/sys/fs/cgroup/schedtune/probe0"):
164 os.rmdir("/sys/fs/cgroup/schedtune/probe0")
165
166 #TODO: remove NFC hacks
167 if which("stop"):
168 command = ["stop", "nfcd"]
169 tools.helpers.run.user(args, command, check=False)
170 elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-active", "-q", "nfcd"], check=False) == 0):
171 command = ["systemctl", "stop", "nfcd"]
172 tools.helpers.run.user(args, command, check=False)
173
174 # Set permissions
175 set_permissions(args)
176
177 # Create session-specific LXC config file
178 helpers.lxc.generate_session_lxc_config(args, session)
179 # Backwards compatibility
180 with open(tools.config.defaults["lxc"] + "/waydroid/config") as f:
181 if "config_session" not in f.read():
182 helpers.mount.bind(args, session["waydroid_data"],
183 tools.config.defaults["data"])
184
185 # Mount rootfs
186 cfg = tools.config.load(args)
187 helpers.images.mount_rootfs(args, cfg["waydroid"]["images_path"], session)
188
189 helpers.protocol.set_aidl_version(args)
190
191 helpers.lxc.start(args)
192 services.hardware_manager.start(args)
193
194 args.session = session
195
196 def stop(args, quit_session=True):
197 logging.info("Stopping container")
198
199 try:
200 services.hardware_manager.stop(args)
201 status = helpers.lxc.status(args)
202 if status != "STOPPED":
203 helpers.lxc.stop(args)
204 while helpers.lxc.status(args) != "STOPPED":
205 pass
206
207 # Networking
208 command = [tools.config.tools_src +
209 "/data/scripts/waydroid-net.sh", "stop"]
210 tools.helpers.run.user(args, command, check=False)
211
212 #TODO: remove NFC hacks
213 if which("start"):
214 command = ["start", "nfcd"]
215 tools.helpers.run.user(args, command, check=False)
216 elif which("systemctl") and (tools.helpers.run.user(args, ["systemctl", "is-enabled", "-q", "nfcd"], check=False) == 0):
217 command = ["systemctl", "start", "nfcd"]
218 tools.helpers.run.user(args, command, check=False)
219
220 # Sensors
221 if which("waydroid-sensord"):
222 command = ["pidof", "waydroid-sensord"]
223 pid = tools.helpers.run.user(args, command, check=False, output_return=True).strip()
224 if pid:
225 command = ["kill", "-9", pid]
226 tools.helpers.run.user(args, command, check=False)
227
228 # Umount rootfs
229 helpers.images.umount_rootfs(args)
230
231 # Backwards compatibility
232 try:
233 helpers.mount.umount_all(args, tools.config.defaults["data"])
234 except:
235 pass
236
237 if "session" in args:
238 if quit_session:
239 logging.info("Terminating session because the container was stopped")
240 try:
241 os.kill(int(args.session["pid"]), signal.SIGUSR1)
242 except:
243 pass
244 del args.session
245 except:
246 pass
247
248 def restart(args):
249 status = helpers.lxc.status(args)
250 if status == "RUNNING":
251 helpers.lxc.stop(args)
252 helpers.lxc.start(args)
253 else:
254 logging.error("WayDroid container is {}".format(status))
255
256 def freeze(args):
257 status = helpers.lxc.status(args)
258 if status == "RUNNING":
259 helpers.lxc.freeze(args)
260 while helpers.lxc.status(args) == "RUNNING":
261 pass
262 else:
263 logging.error("WayDroid container is {}".format(status))
264
265 def unfreeze(args):
266 status = helpers.lxc.status(args)
267 if status == "FROZEN":
268 helpers.lxc.unfreeze(args)
269 while helpers.lxc.status(args) == "FROZEN":
270 pass