Skip to content

procd: add support for OCI runtime spec 1.3.0 - #37

Open
dangowrt wants to merge 56 commits into
openwrt:mainfrom
dangowrt:oci-1.3.0
Open

procd: add support for OCI runtime spec 1.3.0#37
dangowrt wants to merge 56 commits into
openwrt:mainfrom
dangowrt:oci-1.3.0

Conversation

@dangowrt

@dangowrt dangowrt commented May 8, 2026

Copy link
Copy Markdown
Member

Updates procd-jail and uxc to OCI runtime spec 1.3.0. v2: consolidated series, rebased onto current master. Seccomp is now applied via ptrace syscall injection (drops the LD_PRELOAD helper); also adds container lifecycle events, rootless containers via idmapped mounts, and the uxc-net/uxc-stack helpers.

Should fix #35

@dangowrt

dangowrt commented May 8, 2026

Copy link
Copy Markdown
Member Author

@dhewg would appreciate your review!

@dhewg

dhewg commented May 9, 2026

Copy link
Copy Markdown
Contributor

Nice, will take a look!
But before I test drive that on a franken setup, mind enlighten me in #38?

@Juff-Ma

Juff-Ma commented May 22, 2026

Copy link
Copy Markdown

@dangowrt While this PR does indeed fix part of my issue in #35, the part I included in the edit does this happen.

When actually trying to run a OCI container UXC will throw
user.err : jail: parsing of OCI JSON spec has failed: Operation not permitted (1)
on the log
uxc error: No such file or directory
on the command line and
[ ] 12b5d8f454bd3a9ea29ddae6e6523c96e3d2cb5f19f0a898005b37feb365a32a stopped exitcode: 1 (Operation not permitted)
when running uxc list

The directory in the uxc json file is present and filled with files:

tree /data/podman/storage/overlay-containers/12b5d8f454bd3a9ea29ddae6e6523c96e3d2cb5f19f0a898005b37feb365a32a/userdata/
├── artifacts
├── attach
├── config.json
├── ctl
├── ctr.log
├── secrets
├── shm
└── winsz

The command is just podman run quay.io/podman/hello
I've attached the config.json it generates. I changed nothing, only formatting it for readability.

@dangowrt

dangowrt commented May 23, 2026

Copy link
Copy Markdown
Member Author

@Juff-Ma I think I've found the issue, just analyzing config.json and tracing where EPERM could come from. Please retry (fix it in 6cd2985 )

@Juff-Ma

Juff-Ma commented May 23, 2026

Copy link
Copy Markdown

@Juff-Ma I think I've found the issue, just analyzing config.json and tracing where EPERM could come from. Please retry (fix it in 6cd2985 )

@dangowrt Your fix does indeed resolve the EPERM issue however it still does not work. Instead of error 1 EPERM I now get error 22 Invalid Argument. UXC stills ays No such file or sirectory with this on the log:

Sat May 23 11:19:15 2026 user.err : jail: parsing of OCI JSON spec has failed: Invalid argument (22)

and this in the listing:

[ ] f72077ecdaa5d6724a1a7118e4f952e14619ded9f17d804a6ebe1946e5e142cc stopped exitcode: 22 (Invalid argument)

config file/command is the same. Note that this also happens when running uxc start manually, so it's not an invalid argument to the uxc command itself (which I originally thought)

The issue for this happening seems to be seccomp. Since when disabling seccomp the error disappears when using UXC directly. Now it can run the container. However when trying to attach via uxc start --console NAME I get ubus request failed. (When trying to attach via -it I get Error: OCI runtime error: uxc: /sbin/uxc: unrecognized option: console-socket)

Another point is, it really only works when using uxc directly. When it is execut by podman I get the following error still:

> uxc error: No such file or directory
> ERRO[0000] Removing container 501a88cc497d6c1bb784f679a228cc50d1676a1e3e2a542a03400492174cef97 from runtime after creation failed
> Error: container create failed (no logs from conmon): conmon bytes "": readObjectStart: expect { or n, but found , error found in #0 byte of ...||..., bigger context ...||...

Also note that despite what's being said, the container is NOT removed from uxc.

Edit: here's the full podman log:
podman run --rm --security-opt seccomp=unconfined --log-level=debug e7db/geekbench
INFO[0000] podman filtering at log level debug
DEBU[0000] Called run.PersistentPreRunE(podman run --rm --security-opt seccomp=unconfined --log-level=debug e7db/geekbench)
INFO[0000] Setting parallel job count to 7
DEBU[0000] Using conmon: "/usr/bin/conmon"
INFO[0000] Using sqlite as database backend
DEBU[0000] Using graph driver overlay
DEBU[0000] Using graph root /data/podman/storage
DEBU[0000] Using run root /tmp/run/containers/storage
DEBU[0000] Using static dir /data/podman/static
DEBU[0000] Using tmp dir /var/run/libpod
DEBU[0000] Using volume path /data/podman/volumes
DEBU[0000] Using transient store: false
DEBU[0000] [graphdriver] trying provided driver "overlay"
DEBU[0000] Cached value indicated that overlay is supported
DEBU[0000] Cached value indicated that overlay is supported
DEBU[0000] Cached value indicated that metacopy is not being used
DEBU[0000] Cached value indicated that native-diff is usable
DEBU[0000] backingFs=extfs, projectQuotaSupported=false, useNativeDiff=true, usingMetacopy=false
DEBU[0000] Initializing event backend none
DEBU[0000] Configured OCI runtime runc initialization failed: no valid executable found for OCI runtime runc: invalid argument
DEBU[0000] Configured OCI runtime runj initialization failed: no valid executable found for OCI runtime runj: invalid argument
DEBU[0000] Configured OCI runtime kata initialization failed: no valid executable found for OCI runtime kata: invalid argument
DEBU[0000] Configured OCI runtime runsc initialization failed: no valid executable found for OCI runtime runsc: invalid argument
DEBU[0000] Configured OCI runtime youki initialization failed: no valid executable found for OCI runtime youki: invalid argument
DEBU[0000] Configured OCI runtime krun initialization failed: no valid executable found for OCI runtime krun: invalid argument
DEBU[0000] Configured OCI runtime ocijail initialization failed: no valid executable found for OCI runtime ocijail: invalid argument
DEBU[0000] Configured OCI runtime crun initialization failed: no valid executable found for OCI runtime crun: invalid argument
DEBU[0000] Configured OCI runtime crun-vm initialization failed: no valid executable found for OCI runtime crun-vm: invalid argument
DEBU[0000] Configured OCI runtime crun-wasm initialization failed: no valid executable found for OCI runtime crun-wasm: invalid argument
DEBU[0000] Using OCI runtime "/sbin/uxc"
DEBU[0000] Pulling image e7db/geekbench (policy: missing)
DEBU[0000] Looking up image "e7db/geekbench" in local containers storage
DEBU[0000] Normalized platform linux/amd64 to {amd64 linux  [] }
DEBU[0000] Loading registries configuration "/etc/containers/registries.conf"
DEBU[0000] Trying "localhost/e7db/geekbench:latest" ...
DEBU[0000] reference "[overlay@/data/podman/storage+/tmp/run/containers/storage:overlay.mountopt=nodev]localhost/e7db/geekbench:latest" does not resolve to an image ID
DEBU[0000] Trying "docker.io/e7db/geekbench:latest" ...
DEBU[0000] parsed reference into "[overlay@/data/podman/storage+/tmp/run/containers/storage:overlay.mountopt=nodev]@ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e"
DEBU[0000] Found image "e7db/geekbench" as "docker.io/e7db/geekbench:latest" in local containers storage
DEBU[0000] Found image "e7db/geekbench" as "docker.io/e7db/geekbench:latest" in local containers storage ([overlay@/data/podman/storage+/tmp/run/containers/storage:overlay.mountopt=nodev]@ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e)
DEBU[0000] exporting opaque data as blob "sha256:ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e"
DEBU[0000] Looking up image "e7db/geekbench" in local containers storage
DEBU[0000] Normalized platform linux/amd64 to {amd64 linux  [] }
DEBU[0000] Trying "localhost/e7db/geekbench:latest" ...
DEBU[0000] reference "[overlay@/data/podman/storage+/tmp/run/containers/storage:overlay.mountopt=nodev]localhost/e7db/geekbench:latest" does not resolve to an image ID
DEBU[0000] Trying "docker.io/e7db/geekbench:latest" ...
DEBU[0000] parsed reference into "[overlay@/data/podman/storage+/tmp/run/containers/storage:overlay.mountopt=nodev]@ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e"
DEBU[0000] Found image "e7db/geekbench" as "docker.io/e7db/geekbench:latest" in local containers storage
DEBU[0000] Found image "e7db/geekbench" as "docker.io/e7db/geekbench:latest" in local containers storage ([overlay@/data/podman/storage+/tmp/run/containers/storage:overlay.mountopt=nodev]@ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e)
DEBU[0000] exporting opaque data as blob "sha256:ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e"
DEBU[0000] Inspecting image ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e
DEBU[0000] exporting opaque data as blob "sha256:ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e"
DEBU[0000] Inspecting image ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e
DEBU[0000] Inspecting image ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e
DEBU[0000] Inspecting image ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e
DEBU[0000] using systemd mode: false
DEBU[0000] No hostname set; container's hostname will default to runtime default
DEBU[0000] Successfully loaded network podman: &{podman 5ef894788befd4d42498314b6e66282ca730aa2e1e82f9b9597bf4d1725ca074 bridge podman0 2026-05-20 16:26:39.519216229 +0000 UTC [{{{172.17.0.0 ffff0000}} 172.17.0.1 <nil>}] [] false false true [] map[] map[] map[driver:host-local]}
DEBU[0000] Successfully loaded 1 networks
DEBU[0000] Allocated lock 3 for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
DEBU[0000] exporting opaque data as blob "sha256:ecb5e3625e43b91d070b53dce80ca47b93f06a6f8926f48efb7dee49a9e3413e"
DEBU[0000] Cached value indicated that idmapped mounts for overlay are supported
DEBU[0000] Created container "6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c"
DEBU[0000] Container "6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c" has work directory "/data/podman/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata"
DEBU[0000] Container "6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c" has run directory "/tmp/run/containers/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata"
DEBU[0000] Not attaching to stdin
INFO[0000] Received shutdown.Stop(), terminating!        PID=22063
DEBU[0000] Enabling signal proxying
DEBU[0000] Cached value indicated that volatile is being used
DEBU[0000] overlay: mount_data=lowerdir=/data/podman/storage/overlay/l/CVIWIVQJHOTYEVPQEJXPDF274I:/data/podman/storage/overlay/l/5NGBTSZRZWVJIPCS2O5DUC4KPW:/data/podman/storage/overlay/l/XNKYBFRIFIOYSMOMF4WDXIIA77,upperdir=/data/podman/storage/overlay/9cb324b82ec58a3e47df09f5c829b29ede9192380f15d6ace2b2e2c76c9e0653/diff,workdir=/data/podman/storage/overlay/9cb324b82ec58a3e47df09f5c829b29ede9192380f15d6ace2b2e2c76c9e0653/work,nodev,volatile
DEBU[0000] Mounted container "6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c" at "/data/podman/storage/overlay/9cb324b82ec58a3e47df09f5c829b29ede9192380f15d6ace2b2e2c76c9e0653/merged"
DEBU[0000] Created root filesystem for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c at /data/podman/storage/overlay/9cb324b82ec58a3e47df09f5c829b29ede9192380f15d6ace2b2e2c76c9e0653/merged
DEBU[0000] Made network namespace at /run/netns/netns-3b795775-1b32-3863-6bff-7e3457bd7d8e for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
[DEBUG netavark::network::validation] "Validating network namespace..."
[DEBUG netavark::commands::setup] "Setting up..."
[INFO  netavark::firewall] Using iptables firewall driver
[DEBUG netavark::network::bridge] Setup network podman
[DEBUG netavark::network::bridge] Container interface name: eth0 with IP addresses [172.17.0.7/16]
[DEBUG netavark::network::bridge] Bridge name: podman0 with IP addresses [172.17.0.1/16]
[DEBUG netavark::network::core_utils] Setting sysctl value for net.ipv4.ip_forward to 1
[DEBUG netavark::network::core_utils] Setting sysctl value for /proc/sys/net/ipv4/conf/podman0/rp_filter to 2
[DEBUG netavark::network::core_utils] Setting sysctl value for /proc/sys/net/ipv6/conf/eth0/autoconf to 0
[DEBUG netavark::network::core_utils] Setting sysctl value for /proc/sys/net/ipv4/conf/eth0/arp_notify to 1
[DEBUG netavark::network::core_utils] Setting sysctl value for /proc/sys/net/ipv4/conf/eth0/rp_filter to 2
[INFO  netavark::network::netlink] Adding route (dest: 0.0.0.0/0 ,gw: 172.17.0.1, metric 100)
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-1D8721804F16F created on table nat
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_ISOLATION_2 exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_ISOLATION_2 exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_ISOLATION_3 exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_ISOLATION_3 exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_INPUT exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_INPUT exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_FORWARD exists on table filter
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK_FORWARD exists on table filter
[DEBUG netavark::firewall::varktables::helpers] rule -d 172.17.0.0/16 -j ACCEPT created on table nat and chain NETAVARK-1D8721804F16F
[DEBUG netavark::firewall::varktables::helpers] rule ! -d 224.0.0.0/4 -j MASQUERADE created on table nat and chain NETAVARK-1D8721804F16F
[DEBUG netavark::firewall::varktables::helpers] rule -s 172.17.0.0/16 -j NETAVARK-1D8721804F16F created on table nat and chain POSTROUTING
[DEBUG netavark::firewall::varktables::helpers] rule -p udp -s 172.17.0.0/16 --dport 53 -j ACCEPT created on table filter and chain NETAVARK_INPUT
[DEBUG netavark::firewall::varktables::helpers] rule -p tcp -s 172.17.0.0/16 --dport 53 -j ACCEPT created on table filter and chain NETAVARK_INPUT
[DEBUG netavark::firewall::varktables::helpers] rule -m conntrack --ctstate INVALID -j DROP exists on table filter and chain NETAVARK_FORWARD
[DEBUG netavark::firewall::varktables::helpers] rule -d 172.17.0.0/16 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT created on table filter and chain NETAVARK_FORWARD
[DEBUG netavark::firewall::varktables::helpers] rule -s 172.17.0.0/16 -j ACCEPT created on table filter and chain NETAVARK_FORWARD
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-HOSTPORT-SETMARK exists on table nat
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-HOSTPORT-SETMARK exists on table nat
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-HOSTPORT-MASQ exists on table nat
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-HOSTPORT-MASQ exists on table nat
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-HOSTPORT-DNAT exists on table nat
[DEBUG netavark::firewall::varktables::helpers] chain NETAVARK-HOSTPORT-DNAT exists on table nat
[DEBUG netavark::firewall::varktables::helpers] rule -j MARK  --set-xmark 0x2000/0x2000 exists on table nat and chain NETAVARK-HOSTPORT-SETMARK
[DEBUG netavark::firewall::varktables::helpers] rule -j MASQUERADE -m comment --comment 'netavark portfw masq mark' -m mark --mark 0x2000/0x2000 exists on table nat and chain NETAVARK-HOSTPORT-MASQ
[DEBUG netavark::firewall::varktables::helpers] rule -j NETAVARK-HOSTPORT-DNAT -m addrtype --dst-type LOCAL exists on table nat and chain PREROUTING
[DEBUG netavark::firewall::varktables::helpers] rule -j NETAVARK-HOSTPORT-DNAT -m addrtype --dst-type LOCAL exists on table nat and chain OUTPUT
[DEBUG netavark::dns::aardvark] Spawning aardvark server
[DEBUG netavark::dns::aardvark] start aardvark-dns: ["/usr/lib/podman/aardvark-dns", "--config", "/run/containers/networks/aardvark-dns", "-p", "53", "run"]
[DEBUG netavark::commands::setup] {
        "podman": StatusBlock {
            dns_search_domains: Some(
                [
                    "dns.podman",
                ],
            ),
            dns_server_ips: Some(
                [
                    172.17.0.1,
                ],
            ),
            interfaces: Some(
                {
                    "eth0": NetInterface {
                        mac_address: "b2:70:f0:b5:a8:f1",
                        subnets: Some(
                            [
                                NetAddress {
                                    gateway: Some(
                                        172.17.0.1,
                                    ),
                                    ipnet: 172.17.0.7/16,
                                },
                            ],
                        ),
                    },
                },
            ),
        },
    }
[DEBUG netavark::commands::setup] "Setup complete"
DEBU[0000] Not modifying container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c /etc/passwd
DEBU[0000] Modifying container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c /etc/group
DEBU[0000] Setting Cgroup path for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c to /libpod_parent/libpod-6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
DEBU[0000] reading hooks from /usr/share/containers/oci/hooks.d
DEBU[0000] Workdir "/opt/geekbench" resolved to host path "/data/podman/storage/overlay/9cb324b82ec58a3e47df09f5c829b29ede9192380f15d6ace2b2e2c76c9e0653/merged/opt/geekbench"
DEBU[0000] Created OCI spec for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c at /data/podman/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata/config.json
DEBU[0000] /usr/bin/conmon messages will be logged to syslog
DEBU[0000] running conmon: /usr/bin/conmon               args="[--api-version 1 -c 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c -u 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c -r /sbin/uxc -b /data/podman/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata -p /tmp/run/containers/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata/pidfile -n modest_hoover --exit-dir /var/run/libpod/exits --persist-dir /var/run/libpod/persist/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c --full-attach -l k8s-file:/data/podman/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata/ctr.log --log-level debug --syslog --conmon-pidfile /tmp/run/containers/storage/overlay-containers/6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c/userdata/conmon.pid --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /data/podman/storage --exit-command-arg --runroot --exit-command-arg /tmp/run/containers/storage --exit-command-arg --log-level --exit-command-arg debug --exit-command-arg --cgroup-manager --exit-command-arg cgroupfs --exit-command-arg --tmpdir --exit-command-arg /var/run/libpod --exit-command-arg --network-config-dir --exit-command-arg /etc/containers/networks/ --exit-command-arg --network-backend --exit-command-arg netavark --exit-command-arg --volumepath --exit-command-arg /data/podman/volumes --exit-command-arg --db-backend --exit-command-arg sqlite --exit-command-arg --transient-store=false --exit-command-arg --hooks-dir --exit-command-arg /usr/share/containers/oci/hooks.d --exit-command-arg --runtime --exit-command-arg uxc --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg --storage-opt --exit-command-arg overlay.mountopt=nodev --exit-command-arg --events-backend --exit-command-arg none --exit-command-arg --syslog --exit-command-arg container --exit-command-arg cleanup --exit-command-arg --stopped-only --exit-command-arg --rm --exit-command-arg 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c]"
uxc error: No such file or directory
ERRO[0000] Removing container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c from runtime after creation failed
DEBU[0000] Cleaning up container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
DEBU[0000] Tearing down network namespace at /run/netns/netns-3b795775-1b32-3863-6bff-7e3457bd7d8e for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
[DEBUG netavark::commands::teardown] "Tearing down.."
[INFO  netavark::firewall] Using iptables firewall driver
[INFO  netavark::network::bridge] removing bridge podman0
[DEBUG netavark::commands::teardown] "Teardown complete"
DEBU[0000] Unmounted container "6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c"
DEBU[0000] Removing container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
DEBU[0000] Cleaning up container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
DEBU[0000] Network is already cleaned up, skipping...
DEBU[0000] Container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c storage is already unmounted, skipping...
DEBU[0000] Removing all exec sessions for container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c
DEBU[0000] Container 6298bf193de794b9eb70faaae79e98ab517da83ce3174f0b630e2d2eb978062c storage is already unmounted, skipping...
DEBU[0000] Temp dir already cleaned up
DEBU[0000] ExitCode msg: "container create failed (no logs from conmon): conmon bytes \"\": readobjectstart: expect { or n, but found \x00, error found in #0 byte of ...||..., bigger context ...||..."
Error: container create failed (no logs from conmon): conmon bytes "": readObjectStart: expect { or n, but found , error found in #0 byte of ...||..., bigger context ...||...
DEBU[0000] Shutting down engines

Edit 2: After recompiling OpenWRT with seccomp support and rerunning the error still persists but I get a new error message:

user.err : jail: can't open /sys/fs/cgroup/libpod_parent/libpod-85650516cfe8ea8fa4a9e153b075b7c07cb7f97b75370d59d293ff80a4ffac5c/memory.swap_max: No such file or directory

However inspecting it shows that it is indeed present just with a slightly different name

-rw-r--r-- 1 root root 0 May 23 13:40 memory.swap.max

It looks like a) podman is giving out bas paths that UXC can't find or b) UXC just uses a bad name. Could this be a cgroup v1/v2 thing? According to strings and some googling ujail searches for memory.swap_max which is cgroup v1 only yet the cgroup version that's running is v2 which uses memory.swap.max instead.

@dangowrt
dangowrt force-pushed the oci-1.3.0 branch 2 times, most recently from ed1ba35 to 14c4b4a Compare May 26, 2026 13:01
@dangowrt

Copy link
Copy Markdown
Member Author

@Juff-Ma I was wrongly assuming that podman, runc and crun would implement the CLI as defined in the spec, but turns out that part of the spec is abandonned and it become more of just a convention. uxc now tries to behave more like runc or crun, eventhough that's not what the spec says (but the spec-compliant behavior also still works). Also many features added which should get it going as a runtime in recent podman.

Now regarding the seccomp problem you were seeing: I've reworked the seccomp support for now also work with "foreign" libc containers (ie. OpenWrt host being musl, container being eg. glibc or bionic). This needs a small change to the procd package Makefile as well, see below

diff --git a/package/system/procd/Makefile b/package/system/procd/Makefile
index cf730a3c0a..c508a447c6 100644
--- a/package/system/procd/Makefile
+++ b/package/system/procd/Makefile
@@ -30,12 +30,14 @@ PKG_CONFIG_DEPENDS:= \
 
 include $(INCLUDE_DIR)/package.mk
 include $(INCLUDE_DIR)/cmake.mk
+include $(INCLUDE_DIR)/kernel.mk
 
 ifeq ($(DUMP),)
   STAMP_CONFIGURED:=$(strip $(STAMP_CONFIGURED))_$(shell echo $(CONFIG_TARGET_INIT_PATH) | $(MKHASH) md5)
 endif
 
-CMAKE_OPTIONS += -DEARLY_PATH="$(TARGET_INIT_PATH)"
+CMAKE_OPTIONS += -DEARLY_PATH="$(TARGET_INIT_PATH)" \
+		 -DNOLIBC_INCLUDE_DIR="$(LINUX_DIR)/tools/include/nolibc"
 
 define Package/procd/Default
   SECTION:=base

@Juff-Ma

Juff-Ma commented May 26, 2026

Copy link
Copy Markdown

@dangowrt I've tested your build and it has the same problem I reported in Edit 2 of my previous message. The seccomp error (while I appreciate it being fixed) was easily solvable by just compiling OpenWRT with seccomp support.

Through a bit of grepping and googling I eventually made this simple one line patch but still got an error. I could try combining my change with your patchset and see if that fixes anything.

@Juff-Ma

Juff-Ma commented May 26, 2026

Copy link
Copy Markdown

@dangowrt I've tested your build and it has the same problem I reported in Edit 2 of my previous message. The seccomp error (while I appreciate it being fixed) was easily solvable by just compiling OpenWRT with seccomp support.

Through a bit of grepping and googling I eventually made this simple one line patch but still got an error. I could try combining my change with your patchset and see if that fixes anything.

I can not report success. The error about the missing cgroup file is gone but (once again) I still receive a uxc error: no such file or directory without any more info anywhere. The containers are being created though and are shown as such in the list. When I run. The relevant part of podman log has not changed

DEBU[0000] Setting Cgroup path for container a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a to /libpod_parent/libpod-a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a
DEBU[0000] reading hooks from /usr/share/containers/oci/hooks.d
DEBU[0000] Workdir "/" resolved to host path "/data/podman/storage/overlay/eea63b9351612b00eeb274fcb81e8a8136008aed4555daec2ff2f3bffdee72c4/merged"
DEBU[0000] Created OCI spec for container a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a at /data/podman/storage/overlay-containers/a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a/userdata/config.json
DEBU[0000] /usr/bin/conmon messages will be logged to syslog
DEBU[0000] running conmon: /usr/bin/conmon               args="[--api-version 1 -c a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a -u a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a -r /sbin/uxc -b /data/podman/storage/overlay-containers/a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a/userdata -p /tmp/run/containers/storage/overlay-containers/a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a/userdata/pidfile -n clever_merkle --exit-dir /var/run/libpod/exits --persist-dir /var/run/libpod/persist/a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a --full-attach -l k8s-file:/data/podman/storage/overlay-containers/a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a/userdata/ctr.log --log-level debug --syslog --conmon-pidfile /tmp/run/containers/storage/overlay-containers/a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a/userdata/conmon.pid --exit-command /usr/bin/podman --exit-command-arg --root --exit-command-arg /data/podman/storage --exit-command-arg --runroot --exit-command-arg /tmp/run/containers/storage --exit-command-arg --log-level --exit-command-arg debug --exit-command-arg --cgroup-manager --exit-command-arg cgroupfs --exit-command-arg --tmpdir --exit-command-arg /var/run/libpod --exit-command-arg --network-config-dir --exit-command-arg /etc/containers/networks/ --exit-command-arg --network-backend --exit-command-arg netavark --exit-command-arg --volumepath --exit-command-arg /data/podman/volumes --exit-command-arg --db-backend --exit-command-arg sqlite --exit-command-arg --transient-store=false --exit-command-arg --hooks-dir --exit-command-arg /usr/share/containers/oci/hooks.d --exit-command-arg --runtime --exit-command-arg uxc --exit-command-arg --storage-driver --exit-command-arg overlay --exit-command-arg --storage-opt --exit-command-arg overlay.mountopt=nodev --exit-command-arg --events-backend --exit-command-arg file --exit-command-arg --syslog --exit-command-arg container --exit-command-arg cleanup --exit-command-arg --stopped-only --exit-command-arg a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a]"
uxc error: No such file or directory
ERRO[0000] Removing container a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a from runtime after creation failed
DEBU[0000] Cleaning up container a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a
DEBU[0000] Tearing down network namespace at /run/netns/netns-262c7ca9-dba3-3001-9f44-d565a71f4cc8 for container a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a
[DEBUG netavark::commands::teardown] "Tearing down.."
[DEBUG netavark::firewall] Forcibly using firewall driver none
[INFO  netavark::firewall] Not using firewall
[INFO  netavark::network::bridge] removing bridge podman0
[DEBUG netavark::commands::teardown] "Teardown complete"
DEBU[0000] Unmounted container "a68ff245b935214d44cbf8c535ad5b55cdc450e9aac0ee0d4aba2f378c1b0b5a"
DEBU[0000] ExitCode msg: "container create failed (no logs from conmon): conmon bytes \"\": readobjectstart: expect { or n, but found \x00, error found in #0 byte of ...||..., bigger context ...||..."
Error: container create failed (no logs from conmon): conmon bytes "": readObjectStart: expect { or n, but found , error found in #0 byte of ...||..., bigger context ...||...
DEBU[0000] Shutting down engines

@Juff-Ma

Juff-Ma commented May 26, 2026

Copy link
Copy Markdown

@dangowrt I have dug deeper and created a script that logs the arguments passed to uxc. Finding out that the commands to UXC absolute are fine and DO WORK when manually running them there's dafinitely something weird at play.

So far I know the following: There is a race condition between UXC and Podman. Particularly in creating the pidfile. When the UXC command exits the pidfile is not yet created for some reason and Podman WILL fail early. Introducing 1s delay into the UXC execution via my logging script fixes this issue. (Podman reports the correct PID)

With that in place Podman now tries to actually start the container only for that to immediately fail with a new error:

Error: failed to connect to container's attach socket: /data/podman/storage/overlay-containers/5270366f7cd3fbb639fd9053d84470321f7b18968efe084190aac431f4d50921/userdata/attach: no such file or directory

Which is... weird? If it is Podmans job to create this file why isn't it doing so. And if it is UXCs job to do so why isn't podman providing ANY command line param. Now, this could be related to this debian issue which claims this can be the result of a conmon/podman version mismatch. This certainly could be the case. Podmans dependencies in the repos are not as thoroughly updated as podman itself. Conmon is 2 years out of date for example.

But still, I continued. I ran the command with -d and... It worked? Kinda at least. The container started (and showed as running) but podman immediately regards it as crashed and kills the container (note podman regards it as having exited with -1, not UXC, when it receives the kill command the container is still running). I don't know why podman kills it. Podman seems to execute a cleanup command. but I don't know why. Trying to get logs fails with a <nwarn>: stdio_input read failed Resource temporarily unavailable for the socket file from conmon, so maybe it really is at fault here as well?

@Juff-Ma

Juff-Ma commented May 28, 2026

Copy link
Copy Markdown

I'm making one small step after the other. Upgrading all podman dependencies helped a bit. I needed to patch a build error in conmon but that was easily fixable. (I'm still using the 1s delay for UXC to work around the race condition)

Using the latest tools I now am able to start a container and it actually runs. Well the container does. The app inside not so much.

When trying to get logs I get the error

jail: exec-ing /docker-entrypoint.sh
jail: failed to execve /docker-entrypoint.sh: Function not implemented

Note that /docker-entrypoint.sh is not static. It is the entrypoint specified by the Dockerfile and therefore changes with the container used.

Note that I am not able to recover from this error. My entire shell session freezes and I can't use killall to end podman or conmon.

@dangowrt

Copy link
Copy Markdown
Member Author

There is an architectural problem with how seccomp is applied which break foreign-libc containers (ie. OpenWrt with musl and container eg. with glibc). I've resolved that and tons of other issues, currently last mile of testing, going to push to this branch in the next hours with validated podman working. Meanwhile, please open a PR to update podman in case you didn't do that already and there isn't any existing PR for that (I've opened the PR to update conmon earlier this morning)

@Juff-Ma

Juff-Ma commented May 28, 2026

Copy link
Copy Markdown

There is an architectural problem with how seccomp is applied which break foreign-libc containers (ie. OpenWrt with musl and container eg. with glibc). I've resolved that and tons of other issues, currently last mile of testing, going to push to this branch in the next hours with validated podman working. Meanwhile, please open a PR to update podman in case you didn't do that already and there isn't any existing PR for that (I've opened the PR to update conmon earlier this morning)

I see. Glad to hear that. I haven't actually updated Podman itself. It is up to date. In addition to conmon I've update the netavark/aardvark-dns stack, catatonit and crun (since it was also out of date and I was at it)

I'll look into opening PRs

P.S.: Your PR does not include a second patch conmon required in my build env at least. I got a GCC false positive on -Wstringop-overread in ctr_logging.c. If your build works though that may have been an issue on my side, but I wouldn't know which.

@dangowrt

Copy link
Copy Markdown
Member Author

@Juff-Ma Please retry with the changes I've pushed now. Don't forget to also apply the patch for procd's Makefile I've posted in #37 (comment)

@Juff-Ma

Juff-Ma commented May 28, 2026

Copy link
Copy Markdown

@Juff-Ma Please retry with the changes I've pushed now. Don't forget to also apply the patch for procd's Makefile I've posted in #37 (comment)

@dangowrt IT IS ALIVE. Oh my god this is amazing. I still included my cgroups patch just to be sure but it absolutely works now. I can run containers and their services are reachable.

The work you've done to get this working is incredible.

I found 2 little issues (that do not impact day to day operations, at least for me, but I still wanna name them).

  • When using -it the console (at least my SSH connection) still hangs up. I haven't found a way to disconnect from the container
  • UXC still seems to report a file not found error sometimes (when podman deletes containers maybe?) but I have not found any actual problems from this behaviour. Maybe it is a case of too much verbosity by default in a recoverable state.

P.S.: The error really does occur when killing or deleting containers. I don't know why though. Killing never works and rm-ing will error out the first time. Still not an issue for me.

@dangowrt

Copy link
Copy Markdown
Member Author

@Juff-Ma console issues with podman should now also be fixed, please retry and report if you find the time. Thank you!

@Juff-Ma

Juff-Ma commented May 31, 2026

Copy link
Copy Markdown

@Juff-Ma console issues with podman should now also be fixed, please retry and report if you find the time. Thank you!

If you mean the commits up to 280a8bb I wasn't really able to test them (I already tried when they were pushed) since I've been encountering a new issue. I though that I had left a comment but it seems it didn't go through (why github?)

On some containers I get a jail: failed to execve /docker-entrypoint.sh: File not found error. I don't know why it happens.

Here are the two containers I used:

  • nginxdemos/hello:latest - worked
  • coturn/coturn:4 or coturn/coturn:4-alpine - did not work

@dhewg

dhewg commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Some testing feedback:
uxc start --console starts the container, but uxc hangs, ctrl+B doesn't work either, and eventually it times out:

uxc: warning: timed out waiting for instance.running
uxc error: Operation timed out

create+start+attach doesn't have that issue, but now escape sequences get printed (prompt displays as / # ^[[46;5R), job control doesn't work, nor does ctrl+c to e.g. stop top or ctrl+d to exit a shell

Sometimes create hangs too:

$ uxc create alpine
...
uxc: warning: timed out waiting for instance.ready
$ uxc create alpine
uxc error: File exists
$ uxc kill alpine
uxc error: I/O error

attach on a created but not yet started container is just an echo chamber.

In an attempt to move a broken board's debian install to uxc (so a full distro with systemd), I whipped up the attached patch and used the attached config. That worked prior to this PR, including poweroff within the container to shut it down followed by restarting it. Now it only works once, every create+start execept the very first ends in:
user.err : jail: failed to clone/fork: Resource busy
So far only a reboot seems to help. Sounds like some resource leak?

0001-jail-add-config-to-not-lock-securebits.patch
config.json

@dhewg

dhewg commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

I see some of the above mentioned issues only on my openwrt one, and not on riscv

@dhewg

dhewg commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

On riscv64, adding a memory limit to config.json:

"linux": {
        "resources": {
            "memory": {
                "limit": 536870912
            },

yields a crash:

root@muse:/mnt/data# uxc create alpine
kern.alert kernel: [66324.341012] Unable to handle kernel paging request at virtual address ffffffff80da025c
kern.alert kernel: [66324.346404] Current alpine pgtable: 4K pagesize, 39-bit VAs, pgdp=0x0000000106828000
kern.alert kernel: [66324.354081] [ffffffff80da025c] pgd=000000011fffb801, p4d=000000011fffb801, pud=000000011fffb801, pmd=00000000003800e3
kern.emerg kernel: [66324.364693] Oops [#1]
kern.debug kernel: [66324.366887] Modules linked in: ksmbd nft_fib_inet nf_flow_table_inet pl2303 nft_reject_ipv6 nft_reject_ipv4 nft_reject_inet nft_reject nft_redir nft_quota nft_numgen nft_nat nft_masq nft_log nft_limit nft_hash nft_flow_offload nft_fib_ipv6 nft_fib_ipv4 nft_fib nft_ct nft_chain_nat nf_tables nf_nat nf_flow_table nf_conntrack ftdi_sio cp210x ch341 usbserial nfnetlink nf_reject_ipv6 nf_reject_ipv4 nf_log_syslog nf_defrag_ipv6 nf_defrag_ipv4 cdc_acm sch_tbf sch_ingress sch_htb sch_hfsc em_u32 cls_u32 cls_route cls_matchall cls_fw cls_flow cls_basic act_skbedit act_mirred act_gact configs ledtrig_usbport nls_ucs2_utils asn1_decoder oid_registry veth zfs(PO) spl(O) autofs4 nls_utf8 sha256 seqiv md5 libmd5 geniv des_generic libdes deflate zlib_deflate cmac nls_iso8859_1 nls_cp437 leds_gpio ahci libahci libata gpio_button_hotplug(O) vfat fat f2fs crc32_cryptoapi [last unloaded: ksmbd]
kern.debug kernel: [66324.444496] CPU: 4 UID: 0 PID: 8808 Comm: alpine Tainted: P           O        6.18.35 #0 NONE
kern.debug kernel: [66324.453175] Tainted: [P]=PROPRIETARY_MODULE, [O]=OOT_MODULE
kern.debug kernel: [66324.458726] Hardware name: SpacemiT MusePi Pro (DT)
kern.debug kernel: [66324.463589] epc : css_clear_dir+0x1a/0xa0
kern.debug kernel: [66324.467581]  ra : cgroup_apply_control_disable+0x13a/0x178
kern.debug kernel: [66324.473049] epc : ffffffff800c2a1a ra : ffffffff800c2ca6 sp : ffffffc614b1bc40
kern.debug kernel: [66324.480254]  gp : ffffffff810d6e60 tp : ffffffd7070a1680 t0 : ffffffd700c36700
kern.debug kernel: [66324.487460]  t1 : 0000000000200020 t2 : 0000000080400020 s0 : ffffffc614b1bc70
kern.debug kernel: [66324.494664]  s1 : ffffffff8108dec0 a0 : ffffffff80da0210 a1 : ffffffff8101a068
kern.debug kernel: [66324.501868]  a2 : 0000000000000002 a3 : 0000000000000000 a4 : 0000000000000008
kern.debug kernel: [66324.509074]  a5 : 0000000000000003 a6 : ffffffff8108de80 a7 : ffffffda7205d8c0
kern.debug kernel: [66324.516279]  s2 : ffffffff80da0210 s3 : ffffffff8108dec0 s4 : ffffffff8108dec0
kern.debug kernel: [66324.523484]  s5 : ffffffff8108f6b0 s6 : ffffffff8101a068 s7 : ffffffff8108f6d8
kern.debug kernel: [66324.530688]  s8 : 0000000000000006 s9 : ffffffff80da2118 s10: ffffffff8108f6a0
kern.debug kernel: [66324.537892]  s11: 000000000000002b t3 : ffffffff80200020 t4 : 0000000000000007
kern.debug kernel: [66324.545098]  t5 : 0000000000000002 t6 : ffffffd70a9caf52
kern.debug kernel: [66324.550393] status: 0000000200000120 badaddr: ffffffff80da025c cause: 000000000000000f
kern.debug kernel: [66324.558294] [<ffffffff800c2a1a>] css_clear_dir+0x1a/0xa0
kern.debug kernel: [66324.563588] [<ffffffff800c2ca6>] cgroup_apply_control_disable+0x13a/0x178
kern.debug kernel: [66324.570359] [<ffffffff800c6e78>] cgroup_subtree_control_write+0x35c/0x464
kern.debug kernel: [66324.577129] [<ffffffff800c0b0a>] cgroup_file_write+0x56/0xf8
kern.debug kernel: [66324.582772] [<ffffffff802533c0>] kernfs_fop_write_iter+0x104/0x184
kern.debug kernel: [66324.588934] [<ffffffff801d16fc>] vfs_write+0x1cc/0x3b8
kern.debug kernel: [66324.594057] [<ffffffff801d1a1a>] ksys_write+0x52/0xc0
kern.debug kernel: [66324.599091] [<ffffffff801d1a9c>] __riscv_sys_write+0x14/0x20
kern.debug kernel: [66324.604734] [<ffffffff806c9716>] do_trap_ecall_u+0x72/0x240
kern.debug kernel: [66324.610289] [<ffffffff806d32fa>] handle_exception+0x146/0x152
kern.emerg kernel: [66324.616030] Code: cf29 7179 f022 e84a f406 e44e 1800 3983 0005 9bdd (c57c) 892a
kern.warn kernel: [66324.623483] ---[ end trace 0000000000000000 ]---

Happens reproducible with the limit, doesn't happen without it.

@dangowrt
dangowrt force-pushed the oci-1.3.0 branch 4 times, most recently from 90d7d07 to 6212139 Compare August 21, 2026 00:43
put_namespace() stats /proc/self/ns/<name> and only advertises
namespaces the running kernel actually provides, so guarding the
"time" entry with #ifdef CLONE_NEWTIME cannot enable anything the
runtime check would not. The guard tests the toolchain headers at
build time, and the guarded code does not even use CLONE_NEWTIME, so
its only possible effect is to hide time namespace support from the
features reply when procd was built against headers predating the
flag. Drop the guard together with the <sched.h> include which was
added solely to provide it.

Fixes: 47a9f0d ("service: add method to query available container features")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
_add_mount() keyed mounts by target alone and silently skipped any second
registration for an already-known target. With OCI bundles a target can
legitimately be registered twice from independent sources (for instance an
implicit mount and an explicit one), and the two may carry conflicting
parameters that the old code discarded without notice.

Compare the full descriptor instead: an exact duplicate (same source,
filesystemtype, optstr, flags, error and inner flags) is accepted as a no-op,
whereas a genuine conflict on the same target now returns EEXIST so the caller
can fail loudly rather than honour whichever registration happened to win.

Fixes: 71e75f4 ("jail: refactor mount support to cover OCI spec")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Add a ptrace-based syscall tracer, selected via -m (enforce, trace, audit
or complain) with -M naming an NDJSON log. Trace records every syscall,
classifying each into a startup phase (linker, init, app) resolved from
entry-point and libc breakpoints, so a generated profile can cover the
application phase alone. Audit and complain run the real seccomp filter
but rewrite its returns to SECCOMP_RET_TRACE, logging every denial; audit
then enforces (errno or kill, including the two-stop errno arches), while
complain permits and only records. Events stream to a udebug ring and, as
a fallback, to the NDJSON file.

procd's instance config gains seccomp_mode and seccomp_log, passed
through to ujail as -m and -M.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Expose the seccomp modes: trace, audit and complain create the container
in the chosen mode and route the log to /tmp/uxc-<name>.<mode>.json,
making complain-derived profiling a first-class operation.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Replace the C utrace binary and its LD_PRELOAD trace helper with
trace2seccomp, a ucode tool that turns a ujail seccomp trace into an OCI
seccomp profile. The old preload approach only worked for dynamically
linked binaries and required a private libpreload; the new tracer in ujail
(-m trace) handles static binaries and reports per-phase syscall sets, so
the generator can work purely from its NDJSON output.

Dispatched by argv[0]: invoked as utrace or seccomp-trace it runs a program
under `ujail -m trace` and emits an application-phase allow-list, matching
the classic policy-generation window. Invoked as trace2seccomp it converts
a previously captured NDJSON trace, optionally merging all phases or
emitting a two-phase pre/post dynamic-linker profile.

The CMake UTRACE_SUPPORT target now installs the script instead of building
the dropped utrace and preload-trace artefacts.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Stage the jail's /dev as a private tmpfs in the host namespace before
clone3() enters the user namespace, where device mknod is forbidden. Nodes
are created and chowned to the host id that container-root maps to, the
symlinks, sub-mount points and an optional console placeholder are pre-laid,
then /dev is remounted read-only so the kernel locks MNT_LOCK_READONLY when
the namespace copies it. This lets a read-only, content-addressed image run
without a writable /dev, and pivot_root now pivots to self with a lazily
detached old root rather than a writable put-old in the rootfs.

Add -V <src:dest> to bind a host path as a noexec,nosuid,nodev volume, and
prime autofs-backed sources by opening them in the host namespace so an
automount triggered later from the private namespace cannot leave the
overlay base empty.

Bind targets are created relative to the jail root via openat2 with
RESOLVE_BENEATH and RESOLVE_NO_MAGICLINKS so a symlinked target cannot
escape, and OCI bundles keep ownership of their own mount namespace.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
A container running its own netifd needs a resolv.conf that tracks netifd's
atomic replace of resolv.conf.auto on a read-only rootfs. Bind the staged
/dev/resolv.conf symlink itself (open_tree with AT_SYMLINK_NOFOLLOW, not its
target) onto /etc/resolv.conf, so every open re-resolves through
/dev/resolv.conf.d. The target file is created only when the rootfs is
writable; otherwise the staged placeholder suffices.

On the host side, pre-create resolv.conf.auto in the per-jail
/tmp/resolv.conf-<name>.d before bind-mounting it at /dev/resolv.conf.d, so
the mount has a target before the in-jail netifd first writes it.

This wiring is now gated on the private_netifd annotation rather than merely
on a new network namespace, and the org.openwrt.procd.ubus and
.netifd annotations are parsed as distinct keys.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Add uxc-net, a ucode helper that brings a container's host-side network
up and down over ubus, leaving no residue in persistent /etc/config. A veth
is created in netifd and the container end handed to the jail netns via a
dynamic jail interface; the host end is wired per attachment mode read from
the OCI annotations (org.openwrt.network.{attach,egress,ingress,proto}).

bridged:<network> enslaves the host end into a network's bridge and the jail
inherits that network's zone wholesale, auto-creating an isolated network
once if it is absent. routed places the host end as the gateway of a
point-to-point /31 in the container's own fw4 zone, compiling egress and
ingress into explicit forwardings and DNAT redirects, defaulting to
deny-all. host and none do no wiring; rollback is symmetric.

Every netifd-facing identifier is an fnv1a slug of the qualified name to fit
IFNAMSIZ and avoid the dot being read as a VLAN tag. The in-jail netifd
config is rendered to /tmp/run/uxc-net so the container side is configured
from the same declarative source.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Drive uxc-net synchronously on start and stop: "up" compiles the
container's declarative annotations into ephemeral host netifd config,
declaring the jail interface that the subsequent host-side device move picks
up, and "down" tears it down. The host device move (jail_network_attach)
now runs for every named netns container, so an annotation-less container
still receives its host-managed device without a private netifd.

A container-private ubusd and netifd are opt-in via annotations and default
off, so OCI orchestrators managing networking on the host side are not
fought by a parallel in-jail netifd. gen_jail_uci_network() drops the UCI
section-rewriting in favour of copying the config uxc-net rendered, falling
back to bare loopback. The netifd startup wait gains a timeout backstop, and
a stale ubus socket from an unclean teardown is removed so its IN_CREATE
still fires.

jail_network_stop becomes the netns-agnostic jail_network_teardown, called
from poststop and every error path so the per-jail ubusd and netifd never
leak as procd orphans.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Drive the uvol backend from uxc create and delete so a container's writable
storage is created on demand. A registration may declare an overlay-size for
the container's own rw upper, and a data-volumes array of named volumes with
mountpoints and sizes; uxc creates each via uvol (growing an existing one,
keeping it if already larger), activates it, and binds it into the jail.

The link to the backing store is read straight from the existing config: the
image volume is the basename of the bundle path, the rw state volume the
basename of write-overlay-path, so no extra field duplicates it. Image volumes
are deactivated after write, so create activates the image before ujail reads
the bundle. Booting a specific mountpoint defers auto-create until the uvol
backend (VG) is online.

On delete the rw state and per-container data volumes are reaped only when
--volumes is given; the content-addressed image volume may be shared and is
never removed here. uvol is invoked by fork/exec with explicit argv to keep
sizes and names clear of shell quoting.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Add a -x option that appends KEY=VALUE lines from a file to the OCI
process environment, parsed after the bundle's own env so a deployment
can extend it without editing the image. Blank lines, comments and lines
without an equals sign are skipped.

procd's instance config gains an envfile attribute passed through as -x,
and a changed env file triggers an instance restart.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Materialise a registration's initenv into per-container state once, on
first create, writing an env file ujail then loads via -x. A value of
"generate" yields a fresh random hex secret; "generate@<scope>" yields a
secret shared across that scope, generated-or-read under a flock so
whichever instance starts first seeds it and the rest read the identical
value. Secrets live mode 0600 under .meta/secrets and the env file is
created mode 0600.

This lets a packaged container seed first-run credentials (database
passwords, API tokens) deterministically, with stack members converging
on one shared value without ordering constraints, and without baking
secrets into the image.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Add two data-bind options that skip the executable dependency resolution
of -r: -b binds a read-only data file (a rendered config or the stack's
/etc/hosts), -k a read-only credential file. A provisioned "#!/bin/sh"
config must not be treated as a program whose host interpreter and
libraries get bound over the container's own, so neither pulls
dependencies.

A credential bind is idmapped once the OCI process user is known, mapping
host id 0 to the container's app uid/gid, so a single root-owned secret
is readable only by the container's process without a chown and without
being world readable, and the same file can serve several containers.
Because mount_all() runs in the child as the mapped userns uid and cannot
traverse host paths under uvol's 0700 .meta, the bind source is cloned as
a detached mount with open_tree in the parent (real root) and
move_mount()ed into place, mirroring the existing resolv.conf handling.

procd's instance runner maps mount type codes 2/3/4 to -V/-k/-b.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Render the provision array into the new binds (secret => idmapped -k,
plain => -b) and bind the stack's shared hosts-file read-only. The
hosts-file bind registers as mount type "4" only now, together with the
provision handling rather than with the initenv support it was developed
alongside, because procd maps type 4 to -b only from the previous commit
on; registered earlier it would have been bound via -r with executable
dependency resolution.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Emit ubus events across a container's lifecycle: instance.ready once the
OCI state reaches created, instance.running once init has execve'd
(signalled by EOF on a CLOEXEC exec-ack pipe, which also lets the parent
drop its copy of the staging /dev), and instance.stopped last in
free_and_exit, after the network is removed and cgroups destroyed. procd
emits no instance.stop for containers, so these events are the only
lifecycle signal, and the late instance.stopped doubles as the "fully
stopped, safe to recreate" marker. The error paths of main() and
post_poststop() are folded into free_and_exit() so every exit emits the
event.

The initial OCI state becomes creating instead of created, so state
queried before the runtime is set up no longer claims a created
container. This makes container_handle_kill answer NOT_FOUND while
creation is still in progress, so until the uxc half of this change
lands, "uxc delete --force" against a container in the middle of create
fails.

kill learns a negative signal meaning graceful stop: SIGTERM first, with
escalation to SIGKILL after UXC_STOP_TIMEOUT. A child that dies before
the sync pipe handshake is diagnosed with its wait status, and a failing
chdir to the OCI cwd reports the path.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Add an event-driven waiter (uloop plus an instance.* handler) so create,
start, kill and delete block on the matching event instead of returning
once the ubus call is acked. A waiter also terminates on an early
instance.stopped (the container died before becoming ready) and on a
timeout for an ungraceful death that never reaches free_and_exit. A kill
without an explicit signal sends the negative graceful-stop signal and
waits for instance.stopped. A container's stdout and stderr are routed to
the system log so a crashing payload is diagnosable via logread.

create reports what the events tell it. A container that exits before it
is ready, or that never reaches created state at all, is a failed create,
and the instance is dropped again so that the next attempt is not met
with the EEXIST of a half-created container. A container that exits
before instance.running arrives is not a failed start, on the other hand:
a payload that simply runs to completion is what runc and crun report
success for.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Refuse to delete a container whose registration carries origin "package":
such containers are owned by an apk package and must be removed with apk del,
not by hand, so uxc returns EPERM and points the operator at the right command.

On boot, self-heal an interrupted upgrade that left a registered container
whose content-addressed image volume was never written: rather than wedging on
the missing bundle, skip the container and point at "apk fix <name>" for
recovery. Combined with the orphan-state reap that purges per-container state
whose registration has gone, this keeps a fleet's on-disk state converging on
its registrations without manual intervention.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Add uxc-stack, a ucode engine that instantiates and tears down a multi-
container stack from a template. "up <app>" loads a stack template returning
compose(api), which declares named instances drawn from shared image layers,
then writes one autostart registration per instance and triggers bring-up by a
ubus event procd reconciles through the boot path, avoiding a synchronous
create that would serialise the stack on each member's start. "down <app>" is
driven from the persistent registrations (origin "stack:<app>"), not the
template, since apk removes the template before pre-remove runs.

Addressing is deterministic and state-free, derived by FNV-1a from names: a
per-instance host-uid offset strided by 64k so re-created instances keep their
data-volume ownership, and a backhaul /24 within RFC 2544 198.18.0.0/15. The
engine writes a shared /etc/hosts (retaining localhost) for resolver-free
discovery, plus per-instance annotation sidecars uxc-net merges over the
image's baked annotations. Secrets are referenced via generate@ directives uxc
materialises, and bound idmapped rather than copied. On teardown, data volumes
and their paired generated secrets are kept and reported for manual removal.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Group a container's cgroup leaf under its stack (the qualified name's first
segment) and give the leaf a per-process suffix derived from the init pid.
Previously the leaf was named purely from the jail name, so a respawn could
land in the cgroup an exiting instance was still tearing down.

That teardown writes cgroup.kill, which would then SIGKILL the newcomer
mid-setup, observed as "can't read from child". The pid suffix guarantees a
fresh leaf for every process, decoupling a new instance from the teardown of
its predecessor.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Advertise OCI runtime-spec 1.3.0 now that the process, linux, hook, seccomp,
cgroup and namespace fields added across this series cover it. uxc stamps
1.3.0 into the config.json it generates.

ujail's version check is changed from the exact "1.0" prefix to accept any
1.1, 1.2 and 1.3 spec, so bundles produced by current tooling are no
longer rejected for their declared ociVersion while remaining within the major
version the runtime implements.

The version was stated twice, once for ujail and once for uxc. Move it to the
shared container.h so the two cannot drift apart.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
uxc_set() dereferenced the parse table of the configuration loop after
that loop had ended, so creating a container which has no configuration
yet read uninitialised stack and crashed. The name being looked up is
already available as the function argument.

Fixes: df1123e ("uxc: add support for user-defined settings")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
An OCI runtime gives the container the standard descriptors of the
process that created it: that is how conmon collects a container's output
and how the runtime-tools validation suite reads its results. Until now
procd could only relay container output to syslog, so everything a
container wrote was lost to its caller.

ubus carries a single descriptor per message, so a new top-level
stdio-fds.h passes all three as SCM_RIGHTS over a socket pair. procd
receives the socket with the add request and installs the descriptors on
the instance in place of the syslog pipes, falling back to the previous
behaviour when no socket accompanies the request; ujail does the same for
the processes it starts on behalf of exec. The header also carries the
sending helper the uxc side uses.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Send the caller's stdin, stdout and stderr as SCM_RIGHTS over a socket
pair and pass its receiving end with the create and exec requests, so a
container's output reaches the process that created it and podman logs
and the validation suite see it.

The three descriptors are chosen explicitly rather than taken from
whatever uxc happens to hold at the time, because --log redirects our own
stderr into the log file and the container's error output does not belong
there. The caller's stderr is kept aside before that redirection and
handed over.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
INFO, WARNING and DEBUG printed to stdout and ERROR to stderr, which now
carry the container's own output, so every diagnostic ujail produced was
mixed into the stream its caller collects. That corrupts anything structured,
the TAP output of the runtime validation suite for one.

Route the macros through ulog, which picks the terminal only when one is
attached, and pin the OCI container case to syslog where these messages
belong.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
The runtime spec requires the state of the container to reach every hook over
its standard input so the hook can act on it; ours were started with whatever
stdin the runtime happened to have, and the validation suite failed all three
hooks_stdin assertions with "unexpected end of JSON input".

Feed each hook the same state document the state method reports, from a pipe
prepared before the fork. The document construction moves to oci_state_fill()
so both paths render exactly the same thing.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
runc and crun write the bare decimal into the pid file they are asked for, and
that is what readers of the file expect to find. ujail appended a newline,
which a caller comparing the contents byte for byte, or reading the file with
a parser that accepts nothing but digits, does not agree with.

Fixes: 602b8fa ("jail: add option for pidfile")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Mounts were kept in a tree keyed by their target and applied while walking
it, so they were established in alphabetical order of the destination rather
than the order given in the configuration. The runtime spec requires the
listed order, and the validation suite reported every entry that came after
an alphabetically later one as out of order, /dev landing before /proc for
instance.

Keep the tree for lookups and add a list which preserves insertion order for
applying them.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
setns_open() reports why a join failed and every caller threw that away, so
a bundle naming a namespace path of the wrong type, or one that cannot be
entered at all, was created as if the path had not been given. The runtime
spec requires an error there, and the validation suite checks it for each
type in turn.

Propagate the failures. The time namespace is still not joined at all, which
is a separate gap.

Fixes: c482c5d ("jail: add support for referencing existing namespaces")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
conmon learns a container's exit status by waiting for the pid it reads from
the runtime's pid file, but with ujail that process is a child of procd and
never of conmon, so conmon never obtains a status at all: podman reports 0
for a container that exited 42, and podman exec fails even when the command
succeeded. A shim would give conmon something to wait for at the price of
putting a process other than the container's own in the pid file, which
tooling needs for introspecting namespaces, cgroups and seccomp state.

Write the decoded status, WEXITSTATUS or 128 plus the terminating signal, to
an exit_status file in the directory the pid file lives in. The value goes to
a temporary file and is renamed into place, so a reader either sees the
previous value or the whole new one, and it is written before the SIGCHLD
that ends conmon's loop. Exec sessions do the same with their own pid file,
so podman exec has a status to report as well. A stale value from an earlier
run is removed when a pid file is written, and nothing is written when no pid
file was asked for, which leaves the behaviour of a caller that does not want
any of this unchanged.

The status file on its own ends no wait: the container is procd's child, so
its death raises no SIGCHLD in conmon, and a detached container's streams
never reach end of file, leaving conmon to sit until its own timeout. The
runtime therefore signals the process that asked for the container. conmon
execs the runtime, so uxc runs as conmon's child and its parent is the
waiting process; uxc opens a pidfd on getppid() with pidfd_open(), called
through syscall() since musl wraps neither it nor pidfd_send_signal(), and
sends the descriptor along with the create and exec requests. procd keeps it
on the instance beside the stdio descriptors and lets ujail inherit it
across the execve() that starts the jail, -a naming the descriptor number,
and ujail sends SIGCHLD through it with pidfd_send_signal() once the
container is gone, after the status has been written. An exec session
signals through the descriptor its own request carried once the session has
been reaped, and a respawned instance carries the same descriptor into the
next ujail, so the invoker also learns when a later incarnation dies. This
holds for any invoker instead of leaning on the conmon.pid filename, which
is merely podman's default for --conmon-pidfile and silently defeated by
overriding it.

A pid would identify the invoker only for as long as it lives. Callers such
as uxc.init and interactive shells routinely exit long before their
container does, leaving behind a number the kernel may hand out again, and
any scheme that re-checks the pid before the kill still leaves a window
between the check and the signal. A pidfd pins the identity at the instant
it is opened: from that instant on it is the only process a signal through it
can ever reach, however much later it is sent. One window remains, and it is
not one a descriptor can close: getppid() is read before the descriptor
exists, so an invoker that died first yields the reaper instead, and a number
already recycled by then names a stranger. uxc therefore re-reads getppid()
after opening and sends nothing if it changed, which leaves only the two
adjacent syscalls in between, against the whole lifetime of a container in
the old scheme. ujail still polls the descriptor for POLLIN first, the same
way exec_jail() watches the pidfd of its own parent, and stays silent for an
invoker that has already gone. A caller that supplies no descriptor is never
signalled; when pidfd_open() fails uxc warns and sends none rather than
falling back to a pid, since a caller which then waits for a wake-up that
never comes deserves to see why.

The descriptor set now carries its own count, so procd and uxc must be
upgraded together: the previous revision of this commit accepted exactly
three descriptors and nothing else. Losing the carrier now loses the
notification with it, where the pid had travelled separately in the request.

Two limits are worth naming. The signal is sent as ujail begins tearing
down, before cgroups and the network are dismantled, so a woken manager can
observe a container whose traces have not all gone yet. And an exec session
still in flight when the container itself dies is never signalled, because
the supervisor exits first; such a session is left to end on stream EOF, as
it was before.

ubus carries a single descriptor per request, and the three standard
descriptors already travel as SCM_RIGHTS over a socket pair whose receiving
end goes to ubus_invoke_fd(). That carrier now takes a counted set instead
of a fixed trio, the count riding in the payload byte, because the stdio
descriptors are only sent when pass-through is wanted while the notification
descriptor is wanted independently of that: a create sends stdio and, when
the invoker could be named, the pidfd; an exec session with a terminal sends
the pidfd alone. The receiver tells the layouts apart by the count, which
stays unambiguous because stdio is all or nothing.

The invoker's identity is thereby no longer configuration. procd used to
compare the notifypid attribute like the other jail attributes, restarting a
running instance when a re-add named a different invoker; the descriptor is
runtime state like the stdio descriptors, so a re-add replaces the stored
descriptor for the next start while the running jail keeps the one it
inherited. The descriptor stays close-on-exec everywhere except across the
one execve() that starts the instance's own ujail, and ujail marks it
close-on-exec again as soon as it parses the option, so neither hooks nor
the container itself ever inherit it.

conmon ends its loop when the streams it handed the runtime reach end of file
and only then looks for the status, so an exec session's descriptors stay with
the session and are closed once its status has been written. Recording it from
the process that waits for the session is no alternative: that one has joined
the container's mount namespace, where the path the status belongs at does not
exist.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
07e0f7d ("jail: fix /proc,/sys mounting under CLONE_NEWUSER") did two
things: it replaced the hardcoded MS_NOATIME on the procfs and sysfs mounts
with detect_atime_flag(), which is what actually made them mountable and
which is kept, and it moved the creation of the user namespace to after the
mounts were built, which left the pid, network, ipc, uts and cgroup
namespaces owned by the initial user namespace. ujail itself, being
privileged, never had a problem with that; the container at runtime did.
The kernel resolves a container's privileges against the owner of the
namespace being used, so a container holding CAP_NET_BIND_SERVICE could not
bind a privileged port, __inet_bind() asking ns_capable(net->user_ns,
CAP_NET_BIND_SERVICE), and it could mount neither its own procfs, which
wants CAP_SYS_ADMIN in the user namespace owning the pid namespace, nor its
own sysfs, which wants the same in the one owning the network namespace.
runc and crun both establish the user namespace before everything else for
this reason. Undo only the reordering half of that commit.

Let clone() create it together with the rest, which the kernel attributes
to the new user namespace since the credentials are copied before the
namespaces, and have the child wait for its uid and gid maps before it does
anything that needs privilege. Creating it late is kept for the one case
crun also keeps it for, a container joining existing namespaces, whether an
OCI bundle names them by path or -j on the command line names them by pid,
because entering those needs privilege in the user namespace owning them.
The time namespace is not yet handed over: CLONE_NEWTIME stays masked out
of the clone3() flags and the parent still unshares it before the clone, so
it remains owned by the initial user namespace and the title only fully
holds once a following commit moves it into the child as well.

Redefining what defers the user namespace moves work between the two phases
without changing the end state. remask_after_unshare(),
remount_proc_sys_after_unshare(), the oci_deferred_* bookkeeping and the
JAIL_NOAFILE bind now serve only the deferred path; on the common path the
default masks and the read-only /proc/sys hack are applied while the mount
list is built. The deferred path in turn drops privileges with setregid(),
setreuid() and setgroups() before its second unshare(CLONE_NEWNS) rather
than after.

The inherited-mount detach introduced by a1c5633 ("jail: detach inherited
mounts under /proc,/sys before mounting own") and guarded by 3fc9d11
("jail: run inherited-mount detach before joining an external userns") now
runs in two cases only, the deferred user namespace and the join of an
external one. In a mount namespace owned by our own user namespace the
inherited mounts are locked and cannot be detached; for mounts sitting on
the kernel's permanently empty mount points it is not needed either, since
mount_too_revealing() ignores those, but a locked mount covering an
ordinary path, which our own masking and OCI maskedPaths create, still
disqualifies the reference mount and can no longer be detached there.

The command line could combine -j <pid>:user with -f, which the old code
tolerated because CLONE_NEWUSER was always stripped from the clone flags
and the join simply won. Now that the flag reaching clone3() creates a
namespace of its own, -f leaves it unset when a user namespace has already
been joined; an OCI bundle cannot express the combination, as
parseOCIlinuxns() rejects the duplicate in both directions. Comments left
over from the late-creation scheme, now stating the opposite of what the
code does, are dropped.

One detail follows from the new order: the gid 5 the standard /dev/pts
options carry cannot be resolved by a mapping that holds a single id, and
devpts refuses a mount whose gid does not map, so an unmapped gid is
dropped from the options. crun likewise omits gid=5 for its rootless
containers, though it does not filter bundle-supplied options this way.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
6212139 ("jail: give the container's namespaces to its own user
namespace") left one namespace behind: the time namespace was still
created by the parent, with unshare(CLONE_NEWTIME) before the clone, and
therefore stayed owned by the initial user namespace. clone3() cannot
simply take the flag either, since a child created with CLONE_NEWTIME
sits in the namespace from birth, which freezes its offsets before
anyone can write them.

Create it in the child instead, once the uid and gid maps are in place.
A namespace made by unshare() is owned by the user namespace of its
creator, so it now belongs to the container's user namespace, where
mapped root holds CAP_SYS_TIME, exactly what writing
/proc/self/timens_offsets asks for. unshare(CLONE_NEWTIME) does not move
the caller, and the offsets of an inhabited namespace are sealed, so the
child writes the offsets first and then enters through
/proc/self/ns/time_for_children with setns(), which the kernel allows
while the process is still single-threaded. A bundle that defers its own
user namespace creates the time namespace in enter_userns() for the same
ownership reason; /proc is present there, as a new time namespace can
only be configured through OCI and an OCI jail always mounts it. The
createContainer hooks of such a bundle now run before the time namespace
exists; everything else runs inside it as before.

A time namespace joined by path moves to the child as well, alongside
the other setns() joins and before any user namespace is created or
joined, because entering needs privilege in the user namespace owning
the target. The parent keeps only the probe for kernels without time
namespace support, and no longer switches its own time namespace around
the clone: the old join path setns()'d the supervisor into the
container's time namespace and back, briefly running it on shifted
clocks.

Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

UXC: OCI version mismatch error

3 participants