Compare commits

...

10 commits

Author SHA1 Message Date
239b00fc3b config: more comments 2023-06-06 09:21:42 -04:00
637d610333 dobu-run: Allow more fine-grained input device passthrough control 2023-06-06 09:17:21 -04:00
27eb066260 dobu-run: Implement /dev/input access 2023-06-05 22:31:48 -04:00
0ea062b5e4 dobu-run: Implement support for persistent HOME inside containers 2023-06-05 22:22:36 -04:00
6cabe49345 dobu-run.sh: Enforce apps/ prefix 2023-06-05 21:27:13 -04:00
6ff457da3e deps/sommelier: Disable MIT-SHM extension for X11 2023-06-05 21:02:49 -04:00
cddf07bf4d dobu-run: Set TZ as well 2023-06-05 20:32:58 -04:00
e035ee10dd Import seccomp filter from Docker
This blocks the use of sub-namespaces within the containers, which
should improve security as now userns-related bugs cannot be exploited
from within.
2023-06-05 20:28:27 -04:00
4c1f1e4a78 base-archlinux: Add pulseaudio deps 2023-06-05 17:51:13 -04:00
343990e2f8 Add first app: shattered-pixel-dungeon 2023-06-05 17:42:03 -04:00
10 changed files with 989 additions and 18 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
config.sh

View file

@ -0,0 +1,8 @@
FROM dobu/deps-base-archlinux:latest
RUN pacman -Syu --noconfirm \
&& pacman -S --noconfirm shattered-pixel-dungeon
USER user
ENTRYPOINT [ "/usr/bin/shattered-pixel-dungeon" ]

829
assets/seccomp.json Normal file
View file

@ -0,0 +1,829 @@
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
"subArchitectures": [
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
]
},
{
"architecture": "SCMP_ARCH_AARCH64",
"subArchitectures": [
"SCMP_ARCH_ARM"
]
},
{
"architecture": "SCMP_ARCH_MIPS64",
"subArchitectures": [
"SCMP_ARCH_MIPS",
"SCMP_ARCH_MIPS64N32"
]
},
{
"architecture": "SCMP_ARCH_MIPS64N32",
"subArchitectures": [
"SCMP_ARCH_MIPS",
"SCMP_ARCH_MIPS64"
]
},
{
"architecture": "SCMP_ARCH_MIPSEL64",
"subArchitectures": [
"SCMP_ARCH_MIPSEL",
"SCMP_ARCH_MIPSEL64N32"
]
},
{
"architecture": "SCMP_ARCH_MIPSEL64N32",
"subArchitectures": [
"SCMP_ARCH_MIPSEL",
"SCMP_ARCH_MIPSEL64"
]
},
{
"architecture": "SCMP_ARCH_S390X",
"subArchitectures": [
"SCMP_ARCH_S390"
]
},
{
"architecture": "SCMP_ARCH_RISCV64",
"subArchitectures": null
}
],
"syscalls": [
{
"names": [
"accept",
"accept4",
"access",
"adjtimex",
"alarm",
"bind",
"brk",
"capget",
"capset",
"chdir",
"chmod",
"chown",
"chown32",
"clock_adjtime",
"clock_adjtime64",
"clock_getres",
"clock_getres_time64",
"clock_gettime",
"clock_gettime64",
"clock_nanosleep",
"clock_nanosleep_time64",
"close",
"close_range",
"connect",
"copy_file_range",
"creat",
"dup",
"dup2",
"dup3",
"epoll_create",
"epoll_create1",
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
"eventfd2",
"execve",
"execveat",
"exit",
"exit_group",
"faccessat",
"faccessat2",
"fadvise64",
"fadvise64_64",
"fallocate",
"fanotify_mark",
"fchdir",
"fchmod",
"fchmodat",
"fchown",
"fchown32",
"fchownat",
"fcntl",
"fcntl64",
"fdatasync",
"fgetxattr",
"flistxattr",
"flock",
"fork",
"fremovexattr",
"fsetxattr",
"fstat",
"fstat64",
"fstatat64",
"fstatfs",
"fstatfs64",
"fsync",
"ftruncate",
"ftruncate64",
"futex",
"futex_time64",
"futex_waitv",
"futimesat",
"getcpu",
"getcwd",
"getdents",
"getdents64",
"getegid",
"getegid32",
"geteuid",
"geteuid32",
"getgid",
"getgid32",
"getgroups",
"getgroups32",
"getitimer",
"getpeername",
"getpgid",
"getpgrp",
"getpid",
"getppid",
"getpriority",
"getrandom",
"getresgid",
"getresgid32",
"getresuid",
"getresuid32",
"getrlimit",
"get_robust_list",
"getrusage",
"getsid",
"getsockname",
"getsockopt",
"get_thread_area",
"gettid",
"gettimeofday",
"getuid",
"getuid32",
"getxattr",
"inotify_add_watch",
"inotify_init",
"inotify_init1",
"inotify_rm_watch",
"io_cancel",
"ioctl",
"io_destroy",
"io_getevents",
"io_pgetevents",
"io_pgetevents_time64",
"ioprio_get",
"ioprio_set",
"io_setup",
"io_submit",
"io_uring_enter",
"io_uring_register",
"io_uring_setup",
"ipc",
"kill",
"landlock_add_rule",
"landlock_create_ruleset",
"landlock_restrict_self",
"lchown",
"lchown32",
"lgetxattr",
"link",
"linkat",
"listen",
"listxattr",
"llistxattr",
"_llseek",
"lremovexattr",
"lseek",
"lsetxattr",
"lstat",
"lstat64",
"madvise",
"membarrier",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
"mknod",
"mknodat",
"mlock",
"mlock2",
"mlockall",
"mmap",
"mmap2",
"mprotect",
"mq_getsetattr",
"mq_notify",
"mq_open",
"mq_timedreceive",
"mq_timedreceive_time64",
"mq_timedsend",
"mq_timedsend_time64",
"mq_unlink",
"mremap",
"msgctl",
"msgget",
"msgrcv",
"msgsnd",
"msync",
"munlock",
"munlockall",
"munmap",
"nanosleep",
"newfstatat",
"_newselect",
"open",
"openat",
"openat2",
"pause",
"pidfd_open",
"pidfd_send_signal",
"pipe",
"pipe2",
"pkey_alloc",
"pkey_free",
"pkey_mprotect",
"poll",
"ppoll",
"ppoll_time64",
"prctl",
"pread64",
"preadv",
"preadv2",
"prlimit64",
"process_mrelease",
"pselect6",
"pselect6_time64",
"pwrite64",
"pwritev",
"pwritev2",
"read",
"readahead",
"readlink",
"readlinkat",
"readv",
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
"rename",
"renameat",
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
"rt_sigqueueinfo",
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_getaffinity",
"sched_getattr",
"sched_getparam",
"sched_get_priority_max",
"sched_get_priority_min",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
"sched_setscheduler",
"sched_yield",
"seccomp",
"select",
"semctl",
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"setfsgid",
"setfsgid32",
"setfsuid",
"setfsuid32",
"setgid",
"setgid32",
"setgroups",
"setgroups32",
"setitimer",
"setpgid",
"setpriority",
"setregid",
"setregid32",
"setresgid",
"setresgid32",
"setresuid",
"setresuid32",
"setreuid",
"setreuid32",
"setrlimit",
"set_robust_list",
"setsid",
"setsockopt",
"set_thread_area",
"set_tid_address",
"setuid",
"setuid32",
"setxattr",
"shmat",
"shmctl",
"shmdt",
"shmget",
"shutdown",
"sigaltstack",
"signalfd",
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
"splice",
"stat",
"stat64",
"statfs",
"statfs64",
"statx",
"symlink",
"symlinkat",
"sync",
"sync_file_range",
"syncfs",
"sysinfo",
"tee",
"tgkill",
"time",
"timer_create",
"timer_delete",
"timer_getoverrun",
"timer_gettime",
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
"timerfd_settime",
"timerfd_settime64",
"times",
"tkill",
"truncate",
"truncate64",
"ugetrlimit",
"umask",
"uname",
"unlink",
"unlinkat",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"vmsplice",
"wait4",
"waitid",
"waitpid",
"write",
"writev"
],
"action": "SCMP_ACT_ALLOW"
},
{
"names": [
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"minKernel": "4.8"
}
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 40,
"op": "SCMP_CMP_NE"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 0,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 8,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 131072,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 131080,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"personality"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 4294967295,
"op": "SCMP_CMP_EQ"
}
]
},
{
"names": [
"sync_file_range2",
"swapcontext"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"ppc64le"
]
}
},
{
"names": [
"arm_fadvise64_64",
"arm_sync_file_range",
"sync_file_range2",
"breakpoint",
"cacheflush",
"set_tls"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"arm",
"arm64"
]
}
},
{
"names": [
"arch_prctl"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"amd64",
"x32"
]
}
},
{
"names": [
"modify_ldt"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"amd64",
"x32",
"x86"
]
}
},
{
"names": [
"s390_pci_mmio_read",
"s390_pci_mmio_write",
"s390_runtime_instr"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"riscv_flush_icache"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"arches": [
"riscv64"
]
}
},
{
"names": [
"open_by_handle_at"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_DAC_READ_SEARCH"
]
}
},
{
"names": [
"bpf",
"clone",
"clone3",
"fanotify_init",
"fsconfig",
"fsmount",
"fsopen",
"fspick",
"lookup_dcookie",
"mount",
"mount_setattr",
"move_mount",
"name_to_handle_at",
"open_tree",
"perf_event_open",
"quotactl",
"quotactl_fd",
"setdomainname",
"sethostname",
"setns",
"syslog",
"umount",
"umount2",
"unshare"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 2114060288,
"op": "SCMP_CMP_MASKED_EQ"
}
],
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
],
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 1,
"value": 2114060288,
"op": "SCMP_CMP_MASKED_EQ"
}
],
"comment": "s390 parameter ordering for clone is different",
"includes": {
"arches": [
"s390",
"s390x"
]
},
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"clone3"
],
"action": "SCMP_ACT_ERRNO",
"errnoRet": 38,
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"reboot"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_BOOT"
]
}
},
{
"names": [
"chroot"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_CHROOT"
]
}
},
{
"names": [
"delete_module",
"init_module",
"finit_module"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_MODULE"
]
}
},
{
"names": [
"acct"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_PACCT"
]
}
},
{
"names": [
"kcmp",
"pidfd_getfd",
"process_madvise",
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_PTRACE"
]
}
},
{
"names": [
"iopl",
"ioperm"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_RAWIO"
]
}
},
{
"names": [
"settimeofday",
"stime",
"clock_settime",
"clock_settime64"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_TIME"
]
}
},
{
"names": [
"vhangup"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_TTY_CONFIG"
]
}
},
{
"names": [
"get_mempolicy",
"mbind",
"set_mempolicy"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYS_NICE"
]
}
},
{
"names": [
"syslog"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_SYSLOG"
]
}
},
{
"names": [
"bpf"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_BPF"
]
}
},
{
"names": [
"perf_event_open"
],
"action": "SCMP_ACT_ALLOW",
"includes": {
"caps": [
"CAP_PERFMON"
]
}
}
]
}

View file

@ -4,16 +4,6 @@ script_path="$(dirname "$(realpath "$0")")"
. "$script_path/functions.sh" . "$script_path/functions.sh"
assert_prerequisites assert_prerequisites
context_path="$1" image_name="$(path_to_image_name "$1")"
[ -d "$context_path" ] || die "$context_path does not exist" podman build -t "$image_name" "$1"
relative_path="$(realpath --relative-to="$script_path" "$context_path")"
if [[ "$relative_path" =~ \.\. ]]; then
die "Image build path must be a subdirectory of the dobu project"
fi
image_name="dobu/${relative_path/\//-}"
podman build -t "$image_name" "$context_path"

17
config-default.sh Normal file
View file

@ -0,0 +1,17 @@
# Where should the home directories used in containers be persisted
HOMEDIR_STORAGE=$HOME/.local/share/dobu/homedir
# Whether btrfs subvolumes should be used for homes instead of
# normal subdirectories
HOMEDIR_IS_BTRFS=false
# Array of app containers that are granted full /dev/input access
DEV_INPUT_APP_ALLOWLIST=()
# Array of devices that will be passed through to apps with input access
# If undefined, all input devices will be passed through
# Note that some devices expose multiple /dev/input interfaces,
# and all of them have to be in this allowlist to grant access
#DEV_INPUT_DEVICE_ALLOWLIST=(
# js0
# # or use device IDs -- in case device paths change across reboots or hotplug
# by-id/usb-XXX-xxx
#)

View file

@ -7,13 +7,13 @@ RUN pacman -Syu --noconfirm \
libxcursor libxdamage libxext libxfixes \ libxcursor libxdamage libxext libxfixes \
libxi libxinerama libxrandr libxrender \ libxi libxinerama libxrandr libxrender \
mesa vulkan-intel vulkan-radeon vulkan-swrast \ mesa vulkan-intel vulkan-radeon vulkan-swrast \
vulkan-virtio libva libva-mesa-driver \ vulkan-virtio libva libva-mesa-driver libpulse \
noto-fonts noto-fonts-extra noto-fonts-cjk noto-fonts-emoji \ noto-fonts noto-fonts-extra noto-fonts-cjk noto-fonts-emoji pulseaudio-alsa \
lib32-wayland lib32-libxkbcommon-x11 lib32-libx11 lib32-libxcomposite \ lib32-wayland lib32-libxkbcommon-x11 lib32-libx11 lib32-libxcomposite \
lib32-libxcursor lib32-libxdamage lib32-libxext lib32-libxfixes \ lib32-libxcursor lib32-libxdamage lib32-libxext lib32-libxfixes \
lib32-libxi lib32-libxinerama lib32-libxrandr lib32-libxrender \ lib32-libxi lib32-libxinerama lib32-libxrandr lib32-libxrender \
lib32-mesa lib32-vulkan-intel lib32-vulkan-radeon lib32-vulkan-swrast \ lib32-mesa lib32-vulkan-intel lib32-vulkan-radeon lib32-vulkan-swrast \
lib32-vulkan-virtio lib32-libva lib32-libva-mesa-driver lib32-vulkan-virtio lib32-libva lib32-libva-mesa-driver lib32-libpulse
RUN groupadd -g 1100 user && useradd -g user -u 1100 -m user \ RUN groupadd -g 1100 user && useradd -g user -u 1100 -m user \
&& mkdir /xdg_runtime && mkdir /tmp/.X11-unix \ && mkdir /xdg_runtime && mkdir /tmp/.X11-unix \

View file

@ -14,6 +14,7 @@ RUN mkdir /build && cd /build \
RUN cd /build/platform2/vm_tools/sommelier \ RUN cd /build/platform2/vm_tools/sommelier \
&& sed -i 's/#define XDG_SHELL_VERSION 3u/#define XDG_SHELL_VERSION 2u/' sommelier.h \ && sed -i 's/#define XDG_SHELL_VERSION 3u/#define XDG_SHELL_VERSION 2u/' sommelier.h \
&& sed -i 's/args\[i++\] = "-rootless";/args[i++] = "-rootless"; args[i++] = "-extension"; args[i++] = "MIT-SHM";/' sommelier.cc \
&& meson build -Dwith_tests=false && ninja -C build && meson build -Dwith_tests=false && ninja -C build
# Build from ../base-archlinux # Build from ../base-archlinux

View file

@ -2,7 +2,9 @@
# X Sommelier (will listen on :1) # X Sommelier (will listen on :1)
echo "Starting X11 Sommelier" echo "Starting X11 Sommelier"
sommelier --display=wayland-0 --xwayland-path=/usr/bin/Xwayland -X --glamor --x-display=1 --xwayland-gl-driver-path=/usr/lib/dri --force-drm-device=$WM_RENDER_NODE --direct-scale --noop-driver sleep infinity & sommelier --display=wayland-0 --xwayland-path=/usr/bin/Xwayland \
-X --glamor --x-display=1 --xwayland-gl-driver-path=/usr/lib/dri \
--force-drm-device=$WM_RENDER_NODE --direct-scale --noop-driver sleep infinity &
SOMMELIER_X=$! SOMMELIER_X=$!
for i in $(seq 1 5); do for i in $(seq 1 5); do

View file

@ -4,5 +4,89 @@ script_path="$(dirname "$(realpath "$0")")"
. "$script_path/functions.sh" . "$script_path/functions.sh"
assert_prerequisites assert_prerequisites
[ -z "$1" ] && die "Expecting 1 argument"
image_name="$(path_to_image_name "apps/$1")"
# image_name is of the form dobu/xxxx, while for containers we want dobu-xxx
container_name="${image_name/\//-}"
home_path="$HOMEDIR_STORAGE/$1"
log "Image name: $image_name"
log "Container name: $container_name"
log "Home directory path: $home_path"
assert_image_exists "$image_name"
if container_exists "$container_name"; then
die "$container_name is already running; stop the application or run \`podman stop $container_name\` manually"
fi
if [[ -n $PULSE_SERVER ]]; then # remove prefix
host_pulse=${PULSE_SERVER#unix:}
else # default guess
host_pulse=$XDG_RUNTIME_DIR/pulse/native
fi
# Make sure we have Sommelier running first # Make sure we have Sommelier running first
ensure_sommelier ensure_sommelier
# Prepare $HOME for the container
if [ ! -d "$home_path" ]; then
if [ "$HOMEDIR_IS_BTRFS" == "true" ]; then
log "Creating $home_path as a btrfs subvolume"
btrfs subvol create "$home_path"
else
log "Creating $home_path"
mkdir -p "$home_path"
fi
fi
extra_args=""
# Check if we should allow /dev/input access
if is_in_array "$1" "${DEV_INPUT_APP_ALLOWLIST[@]}"; then
if [ -z "${DEV_INPUT_DEVICE_ALLOWLIST+x}" ]; then
log "Granting full /dev/input access"
log "Set DEV_INPUT_DEVICE_ALLOWLIST for more fine-grained control"
extra_args="$extra_args -v /dev/input:/dev/input"
else
for device in "${DEV_INPUT_DEVICE_ALLOWLIST[@]}"; do
device=$(realpath /dev/input/"$device")
[[ ! "$device" =~ ^/dev/input/ ]] && continue
log "Granting access to input device $device"
extra_args="$extra_args -v $device:$device"
done
fi
fi
# The fun part: start the container!
# Don't detach like we did with Sommelier, though
podman run --rm --userns=keep-id:uid=1100,gid=1100 \
`# Use seccomp.json from Docker which blocks sub-namespaces (including sub-userns)` \
--security-opt seccomp="$script_path/assets/seccomp.json" --name "$container_name" \
`# Create tmpfs mountpoints for runtime directories` \
--mount type=tmpfs,destination=/xdg_runtime \
--mount type=tmpfs,destination=/tmp/.X11-unix \
`# Pass through Sommelier Wayland socket` \
`# Note that XDG_RUNTIME_DIR is already set in the image` \
-v "$DOBU_TMP/xdg_runtime/wayland-1":/xdg_runtime/wayland-0 \
-e WAYLAND_DISPLAY=wayland-0 \
`# Pass through Sommelier X11 socket` \
-v "$DOBU_TMP/X11-unix/X1":/tmp/.X11-unix/X0 \
-e DISPLAY=:0 \
`# DRM render nodes` \
-v /dev/dri:/dev/dri \
`# Pass through PulseAudio` \
-v "$host_pulse":/xdg_runtime/pulse/native \
-e PULSE_SERVER=unix:/xdg_runtime/pulse/native \
`# $HOME` \
-v "$home_path":/home/user \
`# Miscellaneous` \
-e XDG_SESSION_TYPE=wayland \
-e TZ="$(date +%Z)" \
`# Scaling parameters` \
-e GDK_SCALE="$GDK_SCALE" \
-e QT_SCALE_FACTOR="$QT_SCALE_FACTOR" \
-e QT_SCREEN_SCALE_FACTORS="$QT_SCREEN_SCALE_FACTORS" \
-e QT_AUTO_SCREEN_SCALE_FACTOR="$QT_AUTO_SCREEN_SCALE_FACTOR" \
$extra_args "$image_name"

View file

@ -2,8 +2,13 @@
set -e set -e
script_path="$(dirname "$(realpath "$0")")"
DOBU_TMP=/tmp/dobu DOBU_TMP=/tmp/dobu
. "$script_path/config-default.sh"
[ -f "$script_path/config.sh" ] && . "$script_path/config.sh"
assert_prerequisites() { assert_prerequisites() {
command -v podman >/dev/null 2>&1 || die "Podman is required" command -v podman >/dev/null 2>&1 || die "Podman is required"
[ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ] || die "Dobu must be run under a compliant Wayland compositor" [ -S "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ] || die "Dobu must be run under a compliant Wayland compositor"
@ -18,9 +23,39 @@ log() {
echo "[$(date +%Y-%m-%dT%H:%M:%S)][dobu] $1" echo "[$(date +%Y-%m-%dT%H:%M:%S)][dobu] $1"
} }
is_in_array() {
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
path_to_image_name() {
local context_path="$1"
[ -d "$context_path" ] || die "$context_path does not exist"
local relative_path="$(realpath --relative-to="$script_path" "$context_path")"
if [[ "$relative_path" =~ \.\. ]]; then
die "Image path must be a subdirectory of the dobu project"
fi
local image_name="dobu/${relative_path/\//-}"
echo "$image_name"
}
assert_image_exists() {
podman inspect "$1" > /dev/null 2>&1 || die "Image $1 does not exist; please build the image first"
}
container_exists() {
podman container exists $1 > /dev/null 2>&1
}
ensure_sommelier() { ensure_sommelier() {
podman inspect dobu/deps-sommelier > /dev/null 2>&1 || die "Sommelier image does not exist; build deps/sommelier first" assert_image_exists dobu/deps-sommelier
podman container exists dobu-deps-sommelier > /dev/null 2>&1 && return container_exists dobu-deps-sommelier && return
rm -rf "$DOBU_TMP/xdg_runtime" || true rm -rf "$DOBU_TMP/xdg_runtime" || true
rm -rf "$DOBU_TMP/X11-unix" || true rm -rf "$DOBU_TMP/X11-unix" || true
@ -38,4 +73,8 @@ ensure_sommelier() {
`# DRM render nodes` \ `# DRM render nodes` \
-v /dev/dri:/dev/dri \ -v /dev/dri:/dev/dri \
dobu/deps-sommelier dobu/deps-sommelier
while [ ! -S "$DOBU_TMP/xdg_runtime/wayland-1" ] || [ ! -S "$DOBU_TMP/X11-unix/X1" ]; do
sleep 0.5
done
} }