import logging
import glob
import shutil
+import time
import platform
import gbinder
import tools.config
else:
return 0
+def add_node_entry(nodes, src, dist, mnt_type, options, check):
+ if check and not os.path.exists(src):
+ return False
+ entry = "lxc.mount.entry = "
+ entry += src + " "
+ if dist is None:
+ dist = src[1:]
+ entry += dist + " "
+ entry += mnt_type + " "
+ entry += options
+ nodes.append(entry)
+ return True
def generate_nodes_lxc_config(args):
+ nodes = []
def make_entry(src, dist=None, mnt_type="none", options="bind,create=file,optional 0 0", check=True):
- if check and not os.path.exists(src):
- return False
- entry = "lxc.mount.entry = "
- entry += src + " "
- if dist is None:
- dist = src[1:]
- entry += dist + " "
- entry += mnt_type + " "
- entry += options
- nodes.append(entry)
- return True
+ return add_node_entry(nodes, src, dist, mnt_type, options, check)
- nodes = []
# Necessary dev nodes
make_entry("tmpfs", "dev", "tmpfs", "nosuid 0 0", False)
make_entry("/dev/zero")
make_entry("/dev/null")
make_entry("/dev/full")
- make_entry("/dev/ashmem", check=False)
+ make_entry("/dev/ashmem")
make_entry("/dev/fuse")
make_entry("/dev/ion")
make_entry("/dev/char", options="bind,create=dir,optional 0 0")
make_entry("/dev/pvr_sync")
make_entry("/dev/pmsg0")
make_entry("/dev/dxg")
- make_entry(tools.helpers.gpu.getDriNode(args), "dev/dri/renderD128")
+ render, card = tools.helpers.gpu.getDriNode(args)
+ make_entry(render, "dev/dri/renderD128")
+ make_entry(card, "dev/dri/card0")
for n in glob.glob("/dev/fb*"):
make_entry(n)
# Low memory killer sys node
make_entry("/sys/module/lowmemorykiller", options="bind,create=dir,optional 0 0")
- # Mount /data
- make_entry("tmpfs", "mnt", "tmpfs", "mode=0755,uid=0,gid=1000", False)
- make_entry(tools.config.defaults["data"], "data", options="bind 0 0", check=False)
-
# Mount host permissions
make_entry(tools.config.defaults["host_perms"],
"vendor/etc/host-permissions", options="bind,optional 0 0")
- # Recursive mount /run to provide necessary host sockets
- make_entry("/run", options="rbind,create=dir 0 0")
- # And /dev/shm
- make_entry("/dev/shm", options="rbind,create=dir,optional 0 0")
-
# Necessary sw_sync node for HWC
make_entry("/dev/sw_sync")
make_entry("/sys/kernel/debug", options="rbind,create=dir,optional 0 0")
make_entry("/mnt/wslg", "mnt_extra/wslg",
options="rbind,create=dir,optional 0 0")
- # var
- make_entry("tmpfs", "var", "tmpfs", "nodev 0 0", False)
- make_entry("/var/run", options="rbind,create=dir,optional 0 0")
-
- # tmp
+ # Make a tmpfs at every possible rootfs mountpoint
make_entry("tmpfs", "tmp", "tmpfs", "nodev 0 0", False)
- for n in glob.glob("/tmp/run-*"):
- make_entry(n, options="rbind,create=dir,optional 0 0")
+ make_entry("tmpfs", "var", "tmpfs", "nodev 0 0", False)
+ make_entry("tmpfs", "run", "tmpfs", "nodev 0 0", False)
# NFC config
make_entry("/system/etc/libnfc-nci.conf", options="bind,optional 0 0")
command = ["mv", config_nodes_tmp_path, lxc_path]
tools.helpers.run.user(args, command)
+ # Create empty file
+ open(os.path.join(lxc_path, "config_session"), mode="w").close()
+
+def generate_session_lxc_config(args, session):
+ nodes = []
+ def make_entry(src, dist=None, mnt_type="none", options="rbind,create=file 0 0"):
+ if any(x in src for x in ["\n", "\r"]):
+ logging.warning("User-provided mount path contains illegal character")
+ return False
+ if dist is None and (not os.path.exists(src) or
+ str(os.stat(src).st_uid) != session["user_id"]):
+ logging.warning("User-provided mount path is not owned by user")
+ return False
+ return add_node_entry(nodes, src, dist, mnt_type, options, check=False)
+
+ # Make sure XDG_RUNTIME_DIR exists
+ if not make_entry("tmpfs", session["xdg_runtime_dir"], options="create=dir 0 0"):
+ raise OSError("Failed to create XDG_RUNTIME_DIR mount point")
+
+ wayland_socket = os.path.realpath(os.path.join(session["xdg_runtime_dir"], session["wayland_display"]))
+ if not make_entry(wayland_socket):
+ raise OSError("Failed to bind Wayland socket")
+
+ pulse_socket = os.path.join(session["pulse_runtime_path"], "native")
+ make_entry(pulse_socket)
+
+ if not make_entry(session["waydroid_data"], "data", options="rbind 0 0"):
+ raise OSError("Failed to bind userdata")
+
+ lxc_path = tools.config.defaults["lxc"] + "/waydroid"
+ config_nodes_tmp_path = args.work + "/config_session"
+ config_nodes = open(config_nodes_tmp_path, "w")
+ for node in nodes:
+ config_nodes.write(node + "\n")
+ config_nodes.close()
+ command = ["mv", config_nodes_tmp_path, lxc_path]
+ tools.helpers.run.user(args, command)
def make_base_props(args):
def find_hal(hardware):
props.append("sys.use_memfd=true")
egl = tools.helpers.props.host_get(args, "ro.hardware.egl")
- dri = tools.helpers.gpu.getDriNode(args)
+ dri, _ = tools.helpers.gpu.getDriNode(args)
gralloc = find_hal("gralloc")
if not gralloc:
def status(args):
command = ["lxc-info", "-P", tools.config.defaults["lxc"], "-n", "waydroid", "-sH"]
out = subprocess.run(command, stdout=subprocess.PIPE).stdout.decode('utf-8').strip()
- os.chmod(args.log, 0o666)
return out
+def wait_for_running(args):
+ lxc_status = status(args)
+ timeout = 10
+ while lxc_status != "RUNNING" and timeout > 0:
+ lxc_status = status(args)
+ logging.info(
+ "waiting {} seconds for container to start...".format(timeout))
+ timeout = timeout - 1
+ time.sleep(1)
+ if lxc_status != "RUNNING":
+ raise OSError("container failed to start")
+
def start(args):
command = ["lxc-start", "-P", tools.config.defaults["lxc"],
"-F", "-n", "waydroid", "--", "/init"]
tools.helpers.run.user(args, command, output="background")
+ wait_for_running(args)
+ # Workaround lxc-start changing stdout/stderr permissions to 700
+ os.chmod(args.log, 0o666)
def stop(args):
command = ["lxc-stop", "-P",
tools.config.defaults["lxc"], "-n", "waydroid"]
tools.helpers.run.user(args, command)
+ANDROID_ENV = {
+ "PATH": "/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin",
+ "ANDROID_ROOT": "/system",
+ "ANDROID_DATA": "/data",
+ "ANDROID_STORAGE": "/storage",
+ "ANDROID_ART_ROOT": "/apex/com.android.art",
+ "ANDROID_I18N_ROOT": "/apex/com.android.i18n",
+ "ANDROID_TZDATA_ROOT": "/apex/com.android.tzdata",
+ "ANDROID_RUNTIME_ROOT": "/apex/com.android.runtime",
+}
+
+def android_env_attach_options():
+ env = [k + "=" + v for k, v in ANDROID_ENV.items()]
+ return [x for var in env for x in ("--set-var", var)]
+
def shell(args):
state = status(args)
if state == "FROZEN":
logging.error("WayDroid container is {}".format(state))
return
command = ["lxc-attach", "-P", tools.config.defaults["lxc"],
- "-n", "waydroid", "--"]
+ "-n", "waydroid", "--clear-env"]
+ command.extend(android_env_attach_options())
+ command.append("--")
if args.COMMAND:
command.extend(args.COMMAND)
else:
command.append("/system/bin/sh")
- subprocess.run(command, env={"PATH": os.environ['PATH'] + ":/system/bin:/vendor/bin"})
+ subprocess.run(command)
if state == "FROZEN":
freeze(args)
def logcat(args):
- state = status(args)
- if state == "FROZEN":
- unfreeze(args)
- elif state != "RUNNING":
- logging.error("WayDroid container is {}".format(state))
- return
- command = ["lxc-attach", "-P", tools.config.defaults["lxc"],
- "-n", "waydroid", "--", "/system/bin/logcat"]
- subprocess.run(command)
- if state == "FROZEN":
- freeze(args)
+ args.COMMAND = ["/system/bin/logcat"]
+ shell(args)