Compare commits

..

No commits in common. "add1c4f6bd15f4ccc626edd3eb71b3e7a2959067" and "db4e7809d142e757719b5f357505ade09ccfa56b" have entirely different histories.

7 changed files with 181 additions and 289 deletions

View file

@ -59,7 +59,7 @@ class Processes:
proc = None proc = None
try: try:
proc = self.popen( proc = self.popen(
run["argv"], req["run"]["argv"],
text=text, text=text,
env=env, env=env,
cwd="/home/user", cwd="/home/user",
@ -82,14 +82,14 @@ class Processes:
return res, proc return res, proc
def accept_vsock(self, s): def accept_vsock(self, s):
con, (cid, port) = s.accept() con, (cid, port) = serv.accept()
assert cid == 2, cid assert cid == 2, cid
self.sources.append(con) self.sources.append(con)
self.client_fds.add(con.fileno()) self.client_fds.add(con.fileno())
return con, (cid, port) return con, (cid, port)
def guest_main(): if __name__ == "__main__":
ps = Processes() ps = Processes()
serv = socket.fromfd(3, socket.AF_VSOCK, socket.SOCK_STREAM) serv = socket.fromfd(3, socket.AF_VSOCK, socket.SOCK_STREAM)
ps.sources.append(serv) ps.sources.append(serv)
@ -134,7 +134,3 @@ def guest_main():
con.send(res) con.send(res)
else: else:
assert False, con.fileno() assert False, con.fileno()
if __name__ == "__main__":
guest_main()

View file

@ -0,0 +1,5 @@
{
lib,
writers,
}:
writers.writePython3Bin "uvms-guest" { } ./guest.py

View file

@ -13,8 +13,6 @@
strace, strace,
util-linux, util-linux,
virtiofsd, virtiofsd,
python3Packages,
uvmslib,
taps, taps,
baseImage, baseImage,
@ -38,25 +36,21 @@ let
}; };
toolsClosure = writeClosure toolsFarm; toolsClosure = writeClosure toolsFarm;
in in
writers.writePython3Bin "uvms" writers.writePython3Bin "uvms" { } (
{ replaceVars ./uvms.py {
libraries = [ uvmslib ]; BWRAP = "${lib.getExe bubblewrap}";
} TOOLS = "${toolsFarm}/bin";
( TOOLS_CLOSURE = toolsClosure;
replaceVars ./uvms.py { CROSVM = lib.getExe crosvm;
BWRAP = "${lib.getExe bubblewrap}"; STRACE = lib.getExe strace;
TOOLS = "${toolsFarm}/bin"; TAPS = "${lib.getExe taps}";
TOOLS_CLOSURE = toolsClosure; VIRTIOFSD = "${lib.getExe virtiofsd}";
CROSVM = lib.getExe crosvm;
STRACE = lib.getExe strace;
TAPS = "${lib.getExe taps}";
VIRTIOFSD = "${lib.getExe virtiofsd}";
BASE_CONFIG = baseImage.config.system.build.ch; BASE_CONFIG = baseImage.config.system.build.ch;
SYSTEM = baseImage.config.system.build.toplevel; SYSTEM = baseImage.config.system.build.toplevel;
SYSTEM_CLOSURE = writeClosure [ SYSTEM_CLOSURE = writeClosure [
baseImage.config.system.build.toplevel baseImage.config.system.build.toplevel
baseImage.config.system.build.ch baseImage.config.system.build.ch
]; ];
} }
) )

View file

@ -10,7 +10,6 @@ import os
import subprocess import subprocess
import socket import socket
import json import json
import re
from argparse import ArgumentParser from argparse import ArgumentParser
from contextlib import contextmanager, closing, ExitStack from contextlib import contextmanager, closing, ExitStack
@ -21,7 +20,6 @@ parser.add_argument("--prefix", default="$HOME/uvms/$VM")
parser.add_argument("--vm-config", default="@BASE_CONFIG@") # noqa: E501 parser.add_argument("--vm-config", default="@BASE_CONFIG@") # noqa: E501
parser.add_argument("--persist-home", action="store_true") parser.add_argument("--persist-home", action="store_true")
parser.add_argument("--run", action="append") parser.add_argument("--run", action="append")
parser.add_argument("--mem", default=None)
parser.add_argument("app", nargs="*", default=()) parser.add_argument("app", nargs="*", default=())
TOOLS_DIR = "@TOOLS@" # noqa: E501 TOOLS_DIR = "@TOOLS@" # noqa: E501
@ -299,7 +297,6 @@ class Processes:
# "-Z", # "-Z",
# "-ff", # "-ff",
CH, CH,
# "-v",
"--api-socket", "--api-socket",
"fd=0", "fd=0",
# f"fd={s.fileno()}" # f"fd={s.fileno()}"
@ -321,8 +318,6 @@ class Processes:
# ro_bind=["/nix/store"], # I give up # ro_bind=["/nix/store"], # I give up
unshare_net=False, unshare_net=False,
shell=False, shell=False,
stdout=None,
stderr=None,
# pass_fds=(s.fileno(),) # pass_fds=(s.fileno(),)
) )
) )
@ -343,23 +338,29 @@ class Processes:
): ):
sock_path = self.prefix + "/gpu.sock" sock_path = self.prefix + "/gpu.sock"
args = [ args = [
SOCKETBINDER,
"-b",
"1",
sock_path,
"s6-ipcserverd",
"-1c1",
# "@STRACE@", # noqa: E501
# "-Z",
# "-ff",
"@CROSVM@", # noqa: E501 "@CROSVM@", # noqa: E501
"--no-syslog", "--no-syslog",
"--log-level",
"debug",
"device", "device",
"gpu", "gpu",
"--socket-path", "--fd",
sock_path, "0",
"--wayland-sock", "--wayland-sock",
f'{PASSTHRU_ENV["XDG_RUNTIME_DIR"]}/{PASSTHRU_ENV["WAYLAND_DISPLAY"]}', # noqa: E501 f'{PASSTHRU_ENV["XDG_RUNTIME_DIR"]}/{PASSTHRU_ENV["WAYLAND_DISPLAY"]}', # noqa: E501
"--params", "--params",
'{ "context-types": "cross-domain" }', '{ "context-types": "cross-domain:virgl2:venus" }',
] ]
with self.popen( with self.popen(
*args, *args,
stderr=None, stderr=None,
stdout=None,
) as proc, removing(sock_path, sock_path + ".lock"): ) as proc, removing(sock_path, sock_path + ".lock"):
yield proc, sock_path yield proc, sock_path
@ -468,21 +469,6 @@ def connect_ch_vsock(
yield s yield s
BYTES_PATTERN = re.compile(r"^([0-9]+)([MmGgKk]?)$")
BYTES_UNITS = {
"k": 1024,
"m": 1048576,
"g": 1024 * 1048576,
}
def parse_bytes(s):
m = BYTES_PATTERN.match(s)
assert m, s
size, unit = m.groups()
return int(size) * BYTES_UNITS.get(unit.lower(), 1)
@contextmanager @contextmanager
def listen_ch_vsock( def listen_ch_vsock(
vsock_sock_path, vsock_sock_path,
@ -578,16 +564,6 @@ def main(args, args_next, cleanup, ps):
) )
virtiofs_socks.append(("home", sock_path)) virtiofs_socks.append(("home", sock_path))
config["payload"]["cmdline"] += " uvms.persist-home=1" config["payload"]["cmdline"] += " uvms.persist-home=1"
if args.mem is not None:
config["memory"]["size"] = parse_bytes(args.mem)
config["memory"]["hotplug_size"] = parse_bytes(args.mem)
if "platform" not in config:
config["platform"] = {}
config["platform"]["oem_strings"] = [
"io.systemd.credential:vmm.notify_socket=vsock-stream:2:8888",
*config["platform"].get("oem_strings", []),
]
gpud, gpud_path = cleanup.enter_context(ps.start_gpu()) gpud, gpud_path = cleanup.enter_context(ps.start_gpu())
@ -614,71 +590,48 @@ def main(args, args_next, cleanup, ps):
ps.exec(*ch_remote, "boot") ps.exec(*ch_remote, "boot")
ps.exec(*ch_remote, "info") ps.exec(*ch_remote, "info")
ready = False
with ready_sock: with ready_sock:
ready_sock.settimeout(8.0) ready_sock.settimeout(20.0)
for _ in range(1048576): try:
if ready: con, _ = ready_sock.accept()
break except: # noqa: E722
try: print(
con, _ = ready_sock.accept() "CH didn't try connecting to the readiness notification socket"
except: # noqa: E722 ) # noqa: E501
print( else:
"WARNING: CH didn't try connecting to the readiness notification socket" # noqa: E501 with con:
) msg = con.recv(128)
ready = True assert msg.startswith(b"READY=1"), msg
break
else:
with con:
msg = con.recv(1024)
for ln in msg.split(b"\n"):
ln = ln.strip()
print(ln)
# if ln.startswith(b"X_SYSTEMD_UNIT_ACTIVE=uvms-guest.service"): # noqa: E501
if ln.startswith(b"READY=1"): # noqa: E501
ready = True
break
assert ready
with connect_ch_vsock(ps.prefix + "/vsock.sock", 24601) as guest: with connect_ch_vsock(ps.prefix + "/vsock.sock", 24601) as guest:
for r in args.run: for r in args.run:
res = {} try:
for _ in range(1): guest.send(
if "status" in res: json.dumps(
break {
try: "run": {
guest.send( "argv": [r],
json.dumps( "EXTRA_PATH": [
{ f"{a}/bin" for a in app_paths
"run": { ], # noqa: E501
"argv": [r],
"EXTRA_PATH": [
f"{a}/bin" for a in app_paths
], # noqa: E501
}
} }
).encode("utf8") }
) ).encode("utf8")
res = guest.recv(8192) )
try: res = guest.recv(8192)
res = json.loads(guest.recv(8192)) try:
except json.JSONDecodeError as e: res = json.loads(guest.recv(8192))
print( except json.JSONDecodeError as e:
f"Couldn't interpret --run {r} response: {e} {res}" print(f"Couldn't interpret --run {r} response: {e} {res}")
) # noqa: E501 continue
res = {} adverb = (
# res = {"status": "failed"} "Successfully"
except Exception as e: if res["status"] == "exec succeeded"
print(f"Couldn't --run {r}: {repr(e)}") else "Failed to" # noqa: E501
if "status" not in res: )
res["status"] = "fail" print(f"{adverb} --run {r}: {res}")
adverb = ( except Exception as e:
"Successfully" print(f"Couldn't --run {r}: {repr(e)}")
if res["status"] == "exec succeeded"
else "Failed to" # noqa: E501
)
print(f"{adverb} --run {r}: {res}")
try: try:
ch.wait() ch.wait()
except KeyboardInterrupt: except KeyboardInterrupt:

View file

@ -1,21 +0,0 @@
{
lib,
python3Packages,
}:
python3Packages.buildPythonPackage {
pname = "uvmslib";
version = "0.0.0";
pyproject = true;
src =
let
fs = lib.fileset;
in
fs.toSource {
root = ./.;
fileset = fs.unions [
./pyproject.toml
./uvmslib.py
];
};
build-system = [ python3Packages.setuptools ];
}

View file

@ -1,10 +0,0 @@
[build-system]
build-backend = "setuptools.build_meta"
requires = [ "setuptools" ]
[project]
name = "uvms"
version = "0.0.0"
[project.scripts]
"uvms-guest" = "uvmslib:guest_main"

View file

@ -42,7 +42,7 @@ in
_module.args.uvmsPkgs = lib.mkDefault (pkgs.callPackage ../pkgs { }); _module.args.uvmsPkgs = lib.mkDefault (pkgs.callPackage ../pkgs { });
# some.failure-handler.enable = true; # some.failure-handler.enable = true;
hardware.graphics.enable = true; hardware.graphics.enable = true;
# boot.kernelPackages = pkgs.linuxPackagesFor uvmsPkgs.linux-uvm; boot.kernelPackages = pkgs.linuxPackagesFor uvmsPkgs.linux-uvm;
# boot.isContainer = true; # boot.isContainer = true;
boot.initrd.kernelModules = [ boot.initrd.kernelModules = [
"drm" "drm"
@ -251,20 +251,19 @@ in
}; };
systemd.sockets."uvms-guest" = { systemd.sockets."uvms-guest" = {
requiredBy = [ "multi-user.target" ]; wantedBy = [ "default.target" ];
before = [ "multi-user.target" ];
listenStreams = [ listenStreams = [
"vsock::24601" "vsock::24601"
]; ];
partOf = [ "uvms-guest.service" ]; partOf = [ "uvms-guest.service" ];
}; };
systemd.services."uvms-guest" = { systemd.services."uvms-guest" = {
before = [ "multi-user.target" ]; requiredBy = [ "multi-user.target" ];
onFailure = [ "shutdown.service" ]; onFailure = [ "shutdown.service" ];
serviceConfig = { serviceConfig = {
User = "user"; User = "user";
Group = "users"; Group = "users";
ExecStart = "${lib.getExe' uvmsPkgs.uvmslib "uvms-guest"}"; ExecStart = "${lib.getExe uvmsPkgs.uvms-guest}";
ExecStop = [ ExecStop = [
"/run/current-system/sw/bin/echo GUEST DOWN" "/run/current-system/sw/bin/echo GUEST DOWN"
"/run/current-system/sw/bin/systemctl poweroff" "/run/current-system/sw/bin/systemctl poweroff"
@ -289,6 +288,7 @@ in
"console=ttyS0" "console=ttyS0"
"reboot=t" "reboot=t"
"panic=-1" "panic=-1"
"io.systemd.credential:vmm.notify_socket=vsock-stream:2:8888"
# "rootfstype=virtiofs" # "rootfstype=virtiofs"
# "root=rootstore" # "root=rootstore"
]; ];
@ -301,146 +301,121 @@ in
}; };
uvms.ch.settings = mkOption { uvms.ch.settings = mkOption {
default = { }; default = { };
type = types.submodule ( type = types.submodule {
let freeformType = jsonType;
osConfig = config; options = {
in payload = {
{ config, ... }: cmdline = mkOption {
{
freeformType = jsonType;
options = {
payload = {
cmdline = mkOption {
type = types.str;
default = concatStringsSep " " (
osConfig.boot.kernelParams
++ [
# "init=${lib.removePrefix "/nix/store" "${osConfig.system.build.toplevel}"}/init"
"init=${osConfig.system.build.toplevel}/init"
]
);
defaultText = ''concatStringsSep " " ${osConfig.boot.kernelParams}'';
};
kernel = mkOption {
type = types.str;
default = "${kernel}/${kernelTarget}";
};
initramfs = mkOption {
type = types.nullOr types.str;
default = "${initialRamdisk}/${initrdFile}";
};
};
vsock = {
cid = mkOption {
type = types.int;
default = 4;
};
socket = mkOption {
type = types.str;
default = "vsock.sock";
};
};
"api-socket" = mkOption {
type = types.str; type = types.str;
default = "vmm.sock"; default = concatStringsSep " " (
}; config.boot.kernelParams
"serial".mode = mkOption { ++ [
type = types.str; # "init=${lib.removePrefix "/nix/store" "${config.system.build.toplevel}"}/init"
default = "File"; "init=${config.system.build.toplevel}/init"
}; ]
"serial".file = mkOption {
type = types.nullOr types.str;
default = "serial";
};
"console".mode = mkOption {
type = types.str;
default = "Pty";
};
"console".file = mkOption {
type = types.nullOr types.str;
default = null;
};
# "watchdog" = true;
# "seccomp" = true;
disks = mkOption {
default = [ ];
type = types.listOf (
types.submodule {
freeformType = jsonType;
options = {
path = mkOption {
type = types.oneOf [
types.path
types.str
];
};
readonly = mkOption {
type = types.bool;
default = true;
};
id = mkOption { type = types.str; };
};
}
); );
defaultText = ''concatStringsSep " " ${config.boot.kernelParams}'';
}; };
memory = mkOption { kernel = mkOption {
default = { }; type = types.str;
type = types.submodule { default = "${kernel}/${kernelTarget}";
};
initramfs = mkOption {
type = types.nullOr types.str;
default = "${initialRamdisk}/${initrdFile}";
};
};
vsock = {
cid = mkOption {
type = types.int;
default = 4;
};
socket = mkOption {
type = types.str;
default = "vsock.sock";
};
};
"api-socket" = mkOption {
type = types.str;
default = "vmm.sock";
};
"serial".mode = mkOption {
type = types.str;
default = "File";
};
"serial".file = mkOption {
type = types.nullOr types.str;
default = "serial";
};
"console".mode = mkOption {
type = types.str;
default = "Pty";
};
"console".file = mkOption {
type = types.nullOr types.str;
default = null;
};
# "watchdog" = true;
# "seccomp" = true;
disks = mkOption {
default = [ ];
type = types.listOf (
types.submodule {
freeformType = jsonType; freeformType = jsonType;
options = { options = {
size = mkOption { path = mkOption {
type = types.int; type = types.oneOf [
default = 4 * 1024 * 1048576; types.path
}; types.str
shared = mkOption {
type = types.bool;
default = true;
};
mergeable = mkOption {
type = types.bool;
default = true;
};
hotplug_method = mkOption {
default = "VirtioMem";
type = types.enum [
"Acpi"
"VirtioMem"
]; ];
}; };
hotplugged_size = mkOption { readonly = mkOption {
type = types.int; type = types.bool;
default = 512 * 1048576; default = true;
}; };
hotplug_size = mkOption { id = mkOption { type = types.str; };
type = types.int;
default = config.memory.size;
};
# hugepages = mkOption {
# type = types.bool;
# default = true;
# };
}; };
}; }
}; );
cpus = mkOption { };
default = { }; memory = mkOption {
type = types.submodule { default = { };
freeformType = jsonType; type = types.submodule {
options = { freeformType = jsonType;
boot_vcpus = mkOption { options = {
type = types.int; size = mkOption {
default = 4; type = types.int;
}; default = 3 * 1024 * 1048576;
max_vcpus = mkOption { };
type = types.int; shared = mkOption {
default = 4; type = types.bool;
}; default = true;
};
mergeable = mkOption {
type = types.bool;
default = true;
}; };
}; };
}; };
}; };
} cpus = mkOption {
); default = { };
type = types.submodule {
freeformType = jsonType;
options = {
boot_vcpus = mkOption {
type = types.int;
default = 4;
};
max_vcpus = mkOption {
type = types.int;
default = 4;
};
};
};
};
};
};
}; };
}; };
} }