ybox 0.9.11.1__py3-none-any.whl → 0.9.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ybox/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  """`ybox` is a tool to easily manage linux distributions in containers"""
2
- __version__ = "0.9.11.1"
2
+ __version__ = "0.9.12"
@@ -50,12 +50,12 @@ recommended_deps = intel-media-driver libva-mesa-driver vulkan-intel vulkan-mesa
50
50
  # optional packages for enhanced experience in shell and GUI apps
51
51
  # (for some reason TERMINFO_DIRS does not work for root user, so explicitly installing terminfo
52
52
  # packages for other terminal emulators available in arch which occupy only a tiny space)
53
- suggested = cantarell-fonts ttf-fira-code noto-fonts neovim eza ncdu fd bat
53
+ suggested = cantarell-fonts ttf-fira-code noto-fonts neovim eza ncdu fd bat gnome-settings-daemon
54
54
  kitty-terminfo rxvt-unicode-terminfo realtime-privileges tree starship
55
55
  # dependencies of the `suggested` packages
56
56
  suggested_deps = xsel
57
57
  # additional packages that are in AUR and installed by paru in init-user.sh
58
- extra = neovim-symlinks tinyxxd autojump
58
+ extra = neovim-symlinks tinyxxd
59
59
 
60
60
 
61
61
  # The commands here will be run as normal userns mapped user, so use sudo if the command
@@ -53,7 +53,7 @@ recommended_deps = xauth netbase xdg-user-dirs intel-media-va-driver-non-free me
53
53
  mesa-vulkan-drivers libfribidi0 fonts-dejavu-core sensible-utils libpam-cap
54
54
  # optional packages for enhanced experience in shell and GUI apps
55
55
  suggested = fonts-cantarell fonts-firacode fonts-noto-core neovim ncdu fd-find bat
56
- kitty-terminfo tree autojump
56
+ gnome-settings-daemon-common kitty-terminfo tree
57
57
  # dependencies of the `suggested` packages
58
58
  suggested_deps = xsel xxd
59
59
 
@@ -151,6 +151,12 @@ log_opts = max-size=10m,max-file=3
151
151
  # the --device option to podman/docker run. Example: devices = /dev/video0,/dev/ttyUSB0
152
152
  devices =
153
153
 
154
+ # Additional options passed as such to the podman/docker run command.
155
+ # These are parsed using `shlex.split()`, so use POSIX shell quotes and escape characters for
156
+ # spaces and other special characters in an option.
157
+ custom_options =
158
+
159
+
154
160
  # The security-opt and other security options passed to podman/docker.
155
161
  # You should restrict these as required.
156
162
  # If these have to be relaxed for some apps, then it is highly recommended to put
@@ -189,10 +195,29 @@ label = type:container_runtime_t
189
195
  # --ulimit=host is only available for podman which copies host ulimits by default
190
196
  # ulimits = host
191
197
  # default is private IPC
192
- # WARNING: setting this to 'host' can open a major security attack vector
198
+ # WARNING: setting this to 'host' can open a significant security attack vector
193
199
  ipc = private
194
200
 
195
201
 
202
+ # Networking related options passed to podman/docker. Available keys are:
203
+ # mode: set the networking mode, passed as --network=...
204
+ # expose: comma-separated container ports to expose, passed as --expose=...
205
+ # publish: comma-separated container ports to publish to the host, passed as --publish=...
206
+ # publish_all: publish all exposed ports to the host, value can be empty which means enable
207
+ # or explicit boolean (true/false, on/off, 1/0) and is passed as -P when enabled
208
+ # host: host name for the container, passed as --hostname=...
209
+ # ip: ipv4 address of the container, passed as --ip=...
210
+ # ip6: ipv6 address of the container, passed as --ip6=...
211
+ # dns: custom DNS servers, passed as --dns=...
212
+ # dns_option: custom DNS options, passed as --dns-option=...
213
+ # dns_search: custom DNS search domain, passed as --dns-search=...
214
+ # mac: custom MAC address for the network interface of the container, passed as --mac-address=...
215
+ # alias: network scoped alias for the container, passed as --network-alias=...
216
+ [network]
217
+ # WARNING: setting this to 'host' can open significant security attack vectors
218
+ mode =
219
+
220
+
196
221
  # These are podman/docker volumes that can use either the format of --mount or -v options
197
222
  # (the scripts make a quick guess by searching for = or ,).
198
223
  # These will typically include some directories from your home like Downloads.
@@ -256,6 +281,7 @@ zsh_p10 = $HOME/.p10k.zsh -> .p10k.zsh
256
281
  dir_colors = $HOME/.dir_colors -> .dir_colors
257
282
  vimrc = $HOME/.vimrc -> .vimrc
258
283
  nvimrc = $HOME/.config/nvim -> .config/nvim
284
+ dconf = $HOME/.config/dconf -> .config/dconf
259
285
  themes = $HOME/.themes -> .themes
260
286
  cursors = $HOME/.icons -> .icons
261
287
  icons = $HOME/.local/share/icons -> .local/share/icons
@@ -264,8 +290,7 @@ gitconf = $HOME/.gitconfig -> .gitconfig
264
290
  gtk2rc = $HOME/.gtkrc-2.0 -> .gtkrc-2.0
265
291
  gtk3rc = $HOME/.config/gtk-3.0 -> .config/gtk-3.0
266
292
  gtk4rc = $HOME/.config/gtk-4.0 -> .config/gtk-4.0
267
- qt5conf = $HOME/.config/Trolltech.conf -> .config/Trolltech.conf
268
- qt5ctconf = $HOME/.config/qt5ct/qt5ct.conf -> .config/qt5ct/qt5ct.conf
293
+ qtconf = $HOME/.config/Trolltech.conf -> .config/Trolltech.conf
269
294
  ariaconf = $HOME/.config/aria2/aria2.conf -> .config/aria2/aria2.conf
270
295
  speechconf = $HOME/.config/speech-dispatcher -> .config/speech-dispatcher
271
296
 
@@ -325,11 +350,14 @@ XMODIFIERS
325
350
  chromium = !p --enable-chrome-browser-cloud-management !a
326
351
 
327
352
 
328
- # Startup programs you want to run when starting the container. These are run using
329
- # /bin/bash shell of the container, so you can use shell variables if required.
353
+ # Startup programs to run in background when starting the container. Each command is run using
354
+ # `nohup` and in background with logs in `$HOME/.local/share/ybox/logs/app-<number>_{out|err}.log`
355
+ # (`_out.log` suffix for standard output and `_err.log` for standard error). The number is the
356
+ # 1-based index of the command in the list in the section below.
357
+ #
330
358
  # These programs are run as normal container user, so if you need to run using root
331
- # account then add 'sudo' before the command.
359
+ # account then add 'sudo' before the command. The commands (i.e. the values of the keys)
360
+ # should be in a single line and cannot contain newlines.
332
361
  [startup]
333
362
  # for example you can avoid sharing dbus of the original session rather start a separate one
334
363
  #dbus = /usr/bin/dbus-daemon --session
335
- #dbus_sys = sudo /usr/bin/dbus-daemon --system
@@ -10,7 +10,7 @@ current_user="$(id -un)"
10
10
  user_home="$(getent passwd "$current_user" | cut -d: -f6)"
11
11
  # set gpg keyserver to an available one
12
12
  mkdir -p "$user_home/.gnupg" && chmod 0700 "$user_home/.gnupg"
13
- echo "keyserver $DEFAULT_GPG_KEY_SERVER" > "$user_home/.gnupg/dirmngr.conf"
13
+ echo "keyserver $DEFAULT_GPG_KEY_SERVER" > "$user_home/.gnupg/dirmngr.conf" || /bin/true
14
14
  rm -f "$user_home"/.gnupg/*/*.lock
15
15
 
16
16
  if [ ! -e "$user_home/.config/pip/pip.conf" ]; then
@@ -110,11 +110,12 @@ function install_apps() {
110
110
  function invoke_startup_apps() {
111
111
  log_dir="$HOME/.local/share/ybox/logs"
112
112
  log_no=1
113
+ mkdir -p "$log_dir"
113
114
  # start apps in the order listed in the file
114
115
  while read -r app_line; do
115
- mkdir -p "$log_dir"
116
116
  echo_color "$fg_orange" "Starting: ${app_line:0:40} ..." >> $status_file
117
117
  nohup $app_line >> "$log_dir/app-${log_no}_out.log" 2>> "$log_dir/app-${log_no}_err.log" &
118
+ ((log_no++))
118
119
  sleep 1
119
120
  done < "$startup_list"
120
121
  }
@@ -176,6 +177,7 @@ done
176
177
  # change ownership of user's /run/user/<uid> tree which may have root ownership due to the
177
178
  # docker bind mounts
178
179
  run_dir=${XDG_RUNTIME_DIR:-/run/user/$uid}
180
+ host_run_dir=${run_dir}-host
179
181
  if [ -d $run_dir ]; then
180
182
  $SUDO chown $uid:$gid $run_dir 2>/dev/null || true
181
183
  fi
@@ -183,6 +185,11 @@ if compgen -G "$run_dir/*" >/dev/null; then
183
185
  $SUDO chown $uid:$gid $run_dir/* 2>/dev/null || true
184
186
  fi
185
187
 
188
+ # link wayland sockets/locks if enabled
189
+ if [ "$ENABLE_WAYLAND" = true ] && compgen -G "$host_run_dir/wayland-*" >/dev/null; then
190
+ ln -sf $host_run_dir/wayland-* $run_dir/.
191
+ fi
192
+
186
193
  # run actions requiring root access
187
194
  $SUDO /bin/bash "$SCRIPT_DIR/entrypoint-root.sh"
188
195
 
@@ -210,13 +217,13 @@ if [ ! -e "$SCRIPT_DIR/ybox-init.done" ]; then
210
217
  fi
211
218
 
212
219
  # process config files, application installs and invoke startup apps
213
- if [ -n "$config_list" ]; then
220
+ if [ -n "$config_list" -a -s "$config_list" ]; then
214
221
  replicate_config_files
215
222
  fi
216
- if [ -n "$app_list" ]; then
223
+ if [ -n "$app_list" -a -s "$app_list" ]; then
217
224
  install_apps
218
225
  fi
219
- if [ -n "$startup_list" ]; then
226
+ if [ -n "$startup_list" -a -s "$startup_list" ]; then
220
227
  invoke_startup_apps
221
228
  fi
222
229
  # update the status file to indicate successful startup
@@ -64,7 +64,6 @@ if [ -e "$nvidia_setup" ]; then
64
64
  sudo /bin/bash "$nvidia_setup" || /bin/true
65
65
  fi
66
66
  ) 100>"$lock_file"
67
- break
68
67
  fi
69
68
  fi
70
69
 
ybox/config.py CHANGED
@@ -44,6 +44,7 @@ class StaticConfiguration:
44
44
  self._status_file = f"{container_dir}/status"
45
45
  self._config_list = f"{self._scripts_dir}/config.list"
46
46
  self._app_list = f"{self._scripts_dir}/app.list"
47
+ self._startup_list = f"{self._scripts_dir}/startup.list"
47
48
 
48
49
  @property
49
50
  def env(self) -> Environ:
@@ -123,8 +124,6 @@ class StaticConfiguration:
123
124
  """local status file to communicate when the container is ready for use"""
124
125
  return self._status_file
125
126
 
126
- # file containing list of configuration files to be linked on that container to host
127
- # as mentioned in the [configs] section
128
127
  @property
129
128
  def config_list(self) -> str:
130
129
  """file containing list of configuration files to be linked on that container to host
@@ -136,6 +135,11 @@ class StaticConfiguration:
136
135
  """file containing list of applications to be installed in the container"""
137
136
  return self._app_list
138
137
 
138
+ @property
139
+ def startup_list(self) -> str:
140
+ """file containing list of commands to be executed in the container on startup"""
141
+ return self._startup_list
142
+
139
143
 
140
144
  class Consts:
141
145
  """
ybox/pkg/clean.py CHANGED
@@ -5,23 +5,39 @@ Clean package cache and related intermediate files of an active ybox container.
5
5
  import argparse
6
6
  from configparser import SectionProxy
7
7
 
8
- from ybox.cmd import PkgMgr, build_shell_command, run_command
8
+ from ybox.cmd import (PkgMgr, build_shell_command, check_active_ybox,
9
+ run_command)
9
10
  from ybox.config import StaticConfiguration
10
11
  from ybox.print import print_info
12
+ from ybox.state import RuntimeConfiguration, YboxStateManagement
11
13
 
12
14
 
15
+ # noinspection PyUnusedLocal
13
16
  def clean_cache(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
14
- conf: StaticConfiguration) -> int:
17
+ conf: StaticConfiguration, runtime_conf: RuntimeConfiguration,
18
+ state: YboxStateManagement) -> int:
19
+ # pylint: disable=unused-argument
15
20
  """
16
21
  Clean package cache and related intermediate files.
17
22
 
18
- :param args: arguments having `quiet` and all other attributes passed by the user
23
+ :param args: arguments having `package` and all other attributes passed by the user
19
24
  :param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
20
25
  :param docker_cmd: the podman/docker executable to use
21
26
  :param conf: the :class:`StaticConfiguration` for the container
27
+ :param runtime_conf: the `RuntimeConfiguration` of the container
28
+ :param state: instance of `YboxStateManagement` having the state of all ybox containers
22
29
  :return: integer exit status of clean command where 0 represents success
23
30
  """
24
- print_info(f"Cleaning package cache in container '{conf.box_name}'")
25
- clean_cmd = pkgmgr[PkgMgr.CLEAN_QUIET.value] if args.quiet else pkgmgr[PkgMgr.CLEAN.value]
26
- return int(run_command(build_shell_command(docker_cmd, conf.box_name, clean_cmd),
27
- exit_on_error=False, error_msg="cleaning package cache"))
31
+ clean_cmd = pkgmgr[PkgMgr.CLEAN.value] if args.ask else pkgmgr[PkgMgr.CLEAN_QUIET.value]
32
+ # run clean for each container since it can involve actions that are container specific
33
+ # apart from the generic ones for the shared root (e.g. AUR cache cleaning for Arch Linux)
34
+ code = 0
35
+ for container in state.get_containers(shared_root=runtime_conf.shared_root):
36
+ if not check_active_ybox(docker_cmd, container):
37
+ continue
38
+ print_info(f"Cleaning package cache in container '{container}'")
39
+ if (c := int(run_command(build_shell_command(docker_cmd, container, clean_cmd),
40
+ exit_on_error=False, error_msg="cleaning package cache"))) != 0:
41
+ code = c
42
+ print()
43
+ return code
ybox/pkg/inst.py CHANGED
@@ -32,10 +32,16 @@ _EXEC_ICON_RE = re.compile(f"{_EXEC_PATTERN}|{_ICON_PATH_PATTERN}")
32
32
  # match !p and !a to replace executable program (third group above) and arguments respectively
33
33
  _FLAGS_RE = re.compile("![ap]")
34
34
  # environment variables passed through from host environment to podman/docker executable
35
- _PASSTHROUGH_ENVVARS = ("XAUTHORITY", "DISPLAY", "WAYLAND_DISPLAY", "FREETYPE_PROPERTIES",
35
+ _PASSTHROUGH_ENVVARS = ("XAUTHORITY", "DISPLAY", "XDG_SESSION_TYPE", "FREETYPE_PROPERTIES",
36
36
  "SSH_AUTH_SOCK", "GPG_AGENT_INFO",
37
37
  "__NV_PRIME_RENDER_OFFLOAD", "__GLX_VENDOR_LIBRARY_NAME",
38
38
  "__VK_LAYER_NV_optimus", "VK_ICD_FILES", "VK_ICD_FILENAMES")
39
+ # environment variables passed from host to podman/docker executable with empty if not set;
40
+ # note that these variables are assumed to have values that don't need quoting by /bin/sh
41
+ # else code will need to be updated to quote $<var> value when passing to the shell
42
+ _PASSTHRU_EMPTY_ENVVARS = ("WAYLAND_DISPLAY", "QT_QPA_PLATFORM")
43
+ # characters that need to be escaped in quoted string of Exec/TryExec line
44
+ _DESKTOP_ESCAPE_RE = re.compile(r'["`$\\]')
39
45
 
40
46
 
41
47
  def install_package(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
@@ -356,9 +362,9 @@ def wrap_container_files(package: str, copy_type: CopyType, app_flags: dict[str,
356
362
  # clear EXECUTABLE mask so that no wrapper executable is created
357
363
  copy_type &= ~CopyType.EXECUTABLE
358
364
 
359
- # the "-it" flag is used for both desktop file and executable for podman/docker exec
360
- # since it is safe (unless the app may need stdin in which case Terminal must be true
361
- # in its desktop file in which case a terminal will be opened during execution)
365
+ # the "-i" flag is used in the wrapper executable for podman/docker exec which is safe;
366
+ # if the app needs stdin then Terminal must be true in its desktop file in which case
367
+ # a terminal will be opened during execution
362
368
  for file_dir, filename, file in file_paths:
363
369
  # check if this is a .desktop directory and copy it over adding appropriate
364
370
  # "docker exec" prefix to the command
@@ -454,8 +460,6 @@ def _wrap_desktop_file(filename: str, file: str, docker_cmd: str, conf: StaticCo
454
460
  container configuration
455
461
  :param wrapper_files: the accumulated list of all wrapper files so far
456
462
  """
457
- # container name is added to desktop file to make it unique
458
- wrapper_name = f"ybox.{conf.box_name}.{filename}"
459
463
 
460
464
  def replace_exec_icon(match: re.Match[str]) -> str:
461
465
  """replace Exec, TryExec and Icon lines appropriately for the host system"""
@@ -475,14 +479,20 @@ def _wrap_desktop_file(filename: str, file: str, docker_cmd: str, conf: StaticCo
475
479
  else:
476
480
  full_cmd = program
477
481
  # pseudo-tty cannot be allocated with rootless docker outside of a terminal app
478
- env_vars = " -e=".join(_PASSTHROUGH_ENVVARS)
479
- return (f'{exec_word}{docker_cmd} exec -e={env_vars} {conf.box_name} '
480
- f'/usr/local/bin/run-in-dir "" {full_cmd}\n')
482
+ ev1 = " -e=".join(_PASSTHROUGH_ENVVARS)
483
+ ev2 = " -e=".join((f"{k}=\\\\${k}" for k in _PASSTHRU_EMPTY_ENVVARS))
484
+ full_cmd = _DESKTOP_ESCAPE_RE.sub(r'\\\g<0>', full_cmd)
485
+ # TODO: SW: add "-i" flag if Terminal is true
486
+ return (f'{exec_word}/bin/sh -c "{docker_cmd} exec -e={ev1} -e={ev2} {conf.box_name} '
487
+ f'/usr/local/bin/run-in-dir \\\\"\\\\" {full_cmd}"\n')
481
488
 
482
489
  # the destination will be $HOME/.local/share/applications
483
490
  os.makedirs(conf.env.user_applications_dir, mode=Consts.default_directory_mode(),
484
491
  exist_ok=True)
485
- wrapper_file = f"{conf.env.user_applications_dir}/{wrapper_name}"
492
+ wrapper_file = f"{conf.env.user_applications_dir}/{filename}"
493
+ if os.path.exists(wrapper_file):
494
+ # container name is added to desktop file to make it unique
495
+ wrapper_file = f"{conf.env.user_applications_dir}/ybox.{conf.box_name}.{filename}"
486
496
  print_notice(f"Linking container desktop file {file} to {wrapper_file}")
487
497
 
488
498
  def write_desktop_file(src: str) -> None:
@@ -622,9 +632,12 @@ def _wrap_executable(filename: str, file: str, docker_cmd: str, conf: StaticConf
622
632
  lambda f_match: _replace_flags(f_match, flags, f'"{file}"', '"$@"'), flags)
623
633
  else:
624
634
  full_cmd = f'/usr/local/bin/run-in-dir "`pwd`" "{file}" "$@"'
625
- env_vars = " -e=".join(_PASSTHROUGH_ENVVARS)
635
+ ev1 = " -e=".join(_PASSTHROUGH_ENVVARS)
636
+ ev2 = " -e=".join((f"{k}=${k}" for k in _PASSTHRU_EMPTY_ENVVARS))
637
+ # TODO: some apps like those asking for password from terminal (e.g. ssh/ykman) also
638
+ # need pseudo-terminal i.e. "-t", so allow for an cmdline option to add "-t" here
626
639
  exec_content = ("#!/bin/sh\n",
627
- f"exec {docker_cmd} exec -it -e={env_vars} {conf.box_name} ", full_cmd)
640
+ f"exec {docker_cmd} exec -i -e={ev1} -e={ev2} {conf.box_name} ", full_cmd)
628
641
  with open(wrapper_exec, "w", encoding="utf-8") as wrapper_fd:
629
642
  wrapper_fd.writelines(exec_content)
630
643
  os.chmod(wrapper_exec, mode=0o755, follow_symlinks=True)
ybox/run/create.py CHANGED
@@ -8,6 +8,7 @@ import grp
8
8
  import os
9
9
  import pwd
10
10
  import re
11
+ import shlex
11
12
  import shutil
12
13
  import stat
13
14
  import subprocess
@@ -491,16 +492,20 @@ def process_sections(profile: PathName, conf: StaticConfiguration, pkgmgr: Secti
491
492
  for section in config.sections():
492
493
  if section == "security":
493
494
  process_security_section(config["security"], profile, docker_args)
495
+ elif section == "network":
496
+ process_network_section(config["network"], profile, docker_args)
494
497
  elif section == "mounts":
495
498
  process_mounts_section(config["mounts"], docker_args)
496
499
  elif section == "env":
497
- process_env_section(config["env"], docker_args)
500
+ process_env_section(config["env"], conf, docker_args)
498
501
  elif section == "configs":
499
502
  if config_hardlinks is not None:
500
503
  process_configs_section(config["configs"], config_hardlinks, conf, docker_args)
501
504
  elif section == "apps":
502
505
  apps_with_deps = process_apps_section(config["apps"], conf, pkgmgr)
503
- elif section not in ("base", "app_flags", "startup"):
506
+ elif section == "startup":
507
+ process_startup_section(config["startup"], conf)
508
+ elif section not in ("base", "app_flags"):
504
509
  raise NotSupportedError(f"Unknown section [{section}] in '{profile}' "
505
510
  "or one of its includes")
506
511
  return shared_root, config, apps_with_deps
@@ -649,8 +654,11 @@ def process_base_section(base_section: SectionProxy, profile: PathName, conf: St
649
654
  elif key == "devices":
650
655
  if val:
651
656
  add_multi_opt(docker_args, "device", val)
657
+ elif key == "custom_options":
658
+ if val:
659
+ docker_args.extend(shlex.split(val))
652
660
  elif key not in ("name", "dbus_sys", "includes"):
653
- raise NotSupportedError(f"Unknown key '{key}' in the [base] of {profile} "
661
+ raise NotSupportedError(f"Unknown key '{key}' in the [base] section of {profile} "
654
662
  "or its includes")
655
663
  if config_locale:
656
664
  for lang_var in ("LANG", "LANGUAGE"):
@@ -796,7 +804,38 @@ def process_security_section(sec_section: SectionProxy, profile: PathName,
796
804
  if _get_boolean(val):
797
805
  docker_args.append("--security-opt=no-new-privileges")
798
806
  else:
799
- raise NotSupportedError(f"Unknown key '{key}' in the [security] of {profile} "
807
+ raise NotSupportedError(f"Unknown key '{key}' in the [security] section of {profile} "
808
+ "or its includes")
809
+
810
+
811
+ def process_network_section(net_section: SectionProxy, profile: PathName,
812
+ docker_args: list[str]) -> None:
813
+ """
814
+ Process the `[network]` section in the container profile to append required podman/docker
815
+ options in the list that has been passed.
816
+
817
+ :param net_section: an object of :class:`SectionProxy` from parsing the `[network]` section
818
+ :param profile: the profile file returned by :func:`select_profile` to use for ybox container
819
+ configuration as a `Path` or resource file from importlib (`Traversable`)
820
+ :param docker_args: list of podman/docker arguments to which required options as per the
821
+ configuration in the `[network]` section are appended
822
+ :raises NotSupportedError: if there is an unknown key in the `[network]` section
823
+ """
824
+ net_options = {"mode": "network", "host": "hostname", "dns": "dns", "dns_option": "dns-option",
825
+ "dns_search": "dns-search", "ip": "ip", "ip6": "ip6", "mac": "mac-address",
826
+ "alias": "network-alias"}
827
+ multi_options = {"expose": "expose", "publish": "publish"}
828
+ for key, val in net_section.items():
829
+ if opt := net_options.get(key):
830
+ if val:
831
+ docker_args.append(f"--{opt}={val}")
832
+ elif opt := multi_options.get(key):
833
+ add_multi_opt(docker_args, opt, val)
834
+ elif key == "publish_all":
835
+ if not val or _get_boolean(val): # empty value means enable
836
+ docker_args.append("-P")
837
+ else:
838
+ raise NotSupportedError(f"Unknown key '{key}' in the [network] section of {profile} "
800
839
  "or its includes")
801
840
 
802
841
 
@@ -811,7 +850,7 @@ def process_mounts_section(mounts_section: SectionProxy, docker_args: list[str])
811
850
  """
812
851
  # keys here are only symbolic names and serve no purpose other than allowing
813
852
  # later profile files to override previous ones
814
- for _, val in mounts_section.items():
853
+ for val in mounts_section.values():
815
854
  if val:
816
855
  if "=" in val or "," in val:
817
856
  docker_args.append(f"--mount={val}")
@@ -861,7 +900,10 @@ def process_configs_section(configs_section: SectionProxy, config_hardlinks: boo
861
900
  dest_path = f"{conf.configs_dir}/{dest_rel_path}"
862
901
  if os.access(src_path, os.R_OK):
863
902
  if os.path.exists(dest_path):
864
- shutil.rmtree(dest_path, ignore_errors=True)
903
+ if os.path.isdir(dest_path):
904
+ shutil.rmtree(dest_path)
905
+ else:
906
+ os.unlink(dest_path)
865
907
  else:
866
908
  os.makedirs(os.path.dirname(dest_path),
867
909
  mode=Consts.default_directory_mode(), exist_ok=True)
@@ -891,15 +933,27 @@ def process_configs_section(configs_section: SectionProxy, config_hardlinks: boo
891
933
  add_mount_option(docker_args, conf.configs_dir, conf.target_configs_dir)
892
934
 
893
935
 
894
- def process_env_section(env_section: SectionProxy, docker_args: list[str]) -> None:
936
+ def process_env_section(env_section: SectionProxy, conf: StaticConfiguration,
937
+ docker_args: list[str]) -> None:
895
938
  """
896
939
  Process the `[env]` section in the container profile to append required podman/docker
897
940
  options in the list that has been passed.
898
941
 
899
942
  :param env_section: an object of :class:`SectionProxy` from parsing the `[env]` section
943
+ :param conf: the :class:`StaticConfiguration` for the container
900
944
  :param docker_args: list of podman/docker arguments to which required options as per the
901
945
  configuration in the `[env]` section are appended
902
946
  """
947
+ # add a TMPDIR to share between host and container since some apps require TMPDIR to be
948
+ # visible on the host (e.g. electron uses it to expose the app/tray icon available via dbus)
949
+ if "TMPDIR" not in env_section:
950
+ # use /var/tmp rather than /tmp since latter is a tmpfs system in many setups that will not
951
+ # retain the created tmpdir after a reboot
952
+ tmpdir = f"/var/tmp/ybox.{conf.box_name}"
953
+ os.makedirs(tmpdir, exist_ok=True)
954
+ os.chmod(tmpdir, 0o1777)
955
+ add_mount_option(docker_args, tmpdir, tmpdir)
956
+ add_env_option(docker_args, "TMPDIR", tmpdir)
903
957
  for key, val in env_section.items():
904
958
  add_env_option(docker_args, key, val)
905
959
 
@@ -939,7 +993,7 @@ def process_apps_section(apps_section: SectionProxy, conf: StaticConfiguration,
939
993
  return dep
940
994
 
941
995
  with open(conf.app_list, "w", encoding="utf-8") as apps_fd:
942
- for _, val in apps_section.items():
996
+ for val in apps_section.values():
943
997
  apps = [app.strip() for app in val.split(",")]
944
998
  deps = [capture_dep(match) for dep in apps if (match := _DEP_SUFFIX.match(dep))]
945
999
  if deps:
@@ -952,6 +1006,21 @@ def process_apps_section(apps_section: SectionProxy, conf: StaticConfiguration,
952
1006
  return apps_with_deps
953
1007
 
954
1008
 
1009
+ def process_startup_section(startup_section: SectionProxy, conf: StaticConfiguration) -> None:
1010
+ """
1011
+ Process the `[startup]` section in the container profile to write the list of commands to be
1012
+ executed (in background) in the container on startup.
1013
+
1014
+ :param startup_section: an object of :class:`SectionProxy` from parsing the `[startup]` section
1015
+ :param conf: the :class:`StaticConfiguration` for the container
1016
+ """
1017
+ if not startup_section:
1018
+ return
1019
+ with open(conf.startup_list, "w", encoding="utf-8") as startup_fd:
1020
+ for val in startup_section.values():
1021
+ startup_fd.write(val + "\n")
1022
+
1023
+
955
1024
  # The shutil.copytree(...) method does not work correctly for "symlinks=False" (or at least
956
1025
  # not like 'cp -aL' or 'cp -alL') where it does not create the source symlinked file rather
957
1026
  # only the target one in the destination directory, and neither does it provide the option to
@@ -1236,6 +1305,9 @@ def run_container(docker_full_cmd: list[str], current_user: str, shared_root: st
1236
1305
  if os.access(conf.app_list, os.R_OK):
1237
1306
  docker_full_cmd.append("-a")
1238
1307
  docker_full_cmd.append(f"{conf.target_scripts_dir}/app.list")
1308
+ if os.access(conf.startup_list, os.R_OK):
1309
+ docker_full_cmd.append("-s")
1310
+ docker_full_cmd.append(f"{conf.target_scripts_dir}/startup.list")
1239
1311
  docker_full_cmd.append(conf.box_name)
1240
1312
 
1241
1313
  if (code := int(run_command(docker_full_cmd, exit_on_error=False,
ybox/run/destroy.py CHANGED
@@ -56,6 +56,11 @@ def main_argv(argv: list[str]) -> None:
56
56
  rm_args.append(container_name)
57
57
  run_command(rm_args, error_msg=f"removing '{container_name}'")
58
58
 
59
+ # remove shared TMPDIR if present
60
+ tmpdir = f"/var/tmp/ybox.{container_name}"
61
+ if os.path.isdir(tmpdir) and os.access(tmpdir, os.W_OK):
62
+ shutil.rmtree(tmpdir)
63
+
59
64
  # remove systemd service file and reload daemon
60
65
  if systemctl:
61
66
  print_color(f"Removing systemd service '{ybox_svc}' and reloading daemon", fg=fgcolor.cyan)
ybox/run/graphics.py CHANGED
@@ -126,12 +126,11 @@ def enable_wayland(docker_args: list[str], env: Environ) -> None:
126
126
  :param docker_args: list of podman/docker arguments to which the options have to be appended
127
127
  :param env: an instance of the current :class:`Environ`
128
128
  """
129
- if env.xdg_rt_dir and (wayland_display := os.environ.get("WAYLAND_DISPLAY")):
130
- add_env_option(docker_args, "WAYLAND_DISPLAY", wayland_display)
131
- wayland_sock = f"{env.xdg_rt_dir}/{wayland_display}"
132
- if os.access(wayland_sock, os.W_OK):
133
- add_mount_option(docker_args, wayland_sock,
134
- f"{env.target_xdg_rt_dir}/{wayland_display}")
129
+ if env.xdg_rt_dir:
130
+ add_env_option(docker_args, "WAYLAND_DISPLAY")
131
+ # don't bind wayland sockets rather link in entrypoint so that it works even
132
+ # when run in X11 setup after being created in Wayland setup
133
+ add_env_option(docker_args, "ENABLE_WAYLAND", "true")
135
134
 
136
135
 
137
136
  def enable_dri(docker_args: list[str]) -> None:
ybox/run/pkg.py CHANGED
@@ -376,7 +376,8 @@ def add_clean(subparser: argparse.ArgumentParser) -> None:
376
376
 
377
377
  :param subparser: the :class:`argparse.ArgumentParser` object for the sub-command
378
378
  """
379
- subparser.set_defaults(needs_state=False)
379
+ subparser.add_argument("-A", "--ask", action="store_true",
380
+ help="ask before each action else silently clean everything")
380
381
  subparser.set_defaults(group_by_shared_root=True)
381
382
  subparser.set_defaults(func=clean_cache)
382
383
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ybox
3
- Version: 0.9.11.1
3
+ Version: 0.9.12
4
4
  Summary: Securely run Linux distribution inside a container
5
5
  Author-email: Sumedh Wale <sumwale@yahoo.com>, Vishal Rao <vishalrao@gmail.com>
6
6
  License: Copyright (c) 2024-2025 Sumedh Wale and contributors
@@ -1,6 +1,6 @@
1
- ybox/__init__.py,sha256=47nYoSFJMxMZxzaum5Ao3cassZ5ZNLXP6zt45UEiawc,99
1
+ ybox/__init__.py,sha256=euvdPF2ShKWgI_hWeHofsb2H88d7__eqDvD84FRr8IE,97
2
2
  ybox/cmd.py,sha256=RaNZ7LBqUNwpqQkitR29WLoItjkMfZmaFEeryLTR_tM,14962
3
- ybox/config.py,sha256=inmuUhlAZb6EKLGYWdymsqoHggtiLd58kc125l25ACA,9943
3
+ ybox/config.py,sha256=tK0iNJSpb_2So_XtyqE-fWFgoczVME6Kh03lU9qorFs,10049
4
4
  ybox/env.py,sha256=6o0tJ163KWhVZHnhDRWw_16nMMYoy4-2yAHSJYBSL-0,9420
5
5
  ybox/filelock.py,sha256=nWBp3jvbtrNziRzNcWm6FVVA_lhMccLwgLCVT2IDK5c,3185
6
6
  ybox/print.py,sha256=hAQjTb6JmtjWh0sF4GdZHcKRph5iMKP5x23s8LE85q4,4343
@@ -9,14 +9,14 @@ ybox/util.py,sha256=D4OgNCH0LXyHR9x0dCXCgkLFGvfgy--mqUbDbZRPobg,16520
9
9
  ybox/conf/completions/ybox.fish,sha256=zTfCb0XvTzzZUbYuIxV1BNSSpUZ1qbkbA5L4SfReLhc,6035
10
10
  ybox/conf/distros/supported.list,sha256=KAN7lbNfbRqF20KqEd0-BzF8U5uPx4n9h0W9zVh9z5o,52
11
11
  ybox/conf/distros/arch/add-gpg-key.sh,sha256=kLMCT15hYadjhInYwQiiyF4u6rJl8n4gGfzEKuMKvTg,644
12
- ybox/conf/distros/arch/distro.ini,sha256=NlCnhU4mmO9nE08BcO7hEQ83osMbHgIhLYybGG5rCNc,11026
12
+ ybox/conf/distros/arch/distro.ini,sha256=zOJ_XhJzbMbcqEIc3xxuZuRwyDevtM0s9g4Jcg8Ekqo,11039
13
13
  ybox/conf/distros/arch/init-base.sh,sha256=eqdVzrMqP0hP52S_xxYZJ8n1lStlPn-eSP2e4DpHvWc,311
14
14
  ybox/conf/distros/arch/init-user.sh,sha256=tb_NX4xNRU2pSuuVf2jSb7019uLfxBDbKI7ZyOtzSA4,1127
15
15
  ybox/conf/distros/arch/init.sh,sha256=7f96YVjgQnOWItaxDjllgOl4LD9ibqzuhSk2QbDutvU,3578
16
16
  ybox/conf/distros/arch/list_fmt_long.py,sha256=Ws9Mt9CKInxqUSn98P6-Csik2QqEjyBHp5NyUHlbcoU,2825
17
17
  ybox/conf/distros/arch/pkgdeps.py,sha256=Y77N1xHmHZRyZ9_diFVvoqBd44lZXIIJ2ipX7YVlrT8,14081
18
18
  ybox/conf/distros/deb-generic/check-package.sh,sha256=bMiOi6Cp_UrtXm_sXOWBq4Xng2EwXYerqBuRLtGYIlo,2621
19
- ybox/conf/distros/deb-generic/distro.ini,sha256=ZDWJG-cvx1BuI2RwynIvDwxzuzvVaYNv68PD-J57t84,11361
19
+ ybox/conf/distros/deb-generic/distro.ini,sha256=11iz-Ab5kwHg9n_bXHws6mB258GSkc0On5ouz-BtX0U,11381
20
20
  ybox/conf/distros/deb-generic/fetch-gpg-key-id.sh,sha256=sTsLWILZosYJcGV1u8qBdczQAFBda2Zlsp1xbpy8nzo,836
21
21
  ybox/conf/distros/deb-generic/init-base.sh,sha256=rim7UckGos0phyOAiNYbIjOVG9C4gKQSB-YbZcTFIms,191
22
22
  ybox/conf/distros/deb-generic/init-user.sh,sha256=RDt9sZuv6cSX1WBaRGcLMq2ZTBhVZmZVWD1t0wjvizU,20
@@ -28,24 +28,24 @@ ybox/conf/distros/deb-stable/distro.ini,sha256=xyQ31mLOpj3Z1MK-UYuc_9NfEkb7Gy10M
28
28
  ybox/conf/distros/ubuntu2204/distro.ini,sha256=3Pw1Q30USyKMUcHp_cvlqhwyU0FPo8O2AHWkgd0cdE4,1105
29
29
  ybox/conf/distros/ubuntu2404/distro.ini,sha256=ra4_EteDsHrw9UcehP4qLGTn98gAHc4JCb56CDzz1Wo,1102
30
30
  ybox/conf/profiles/apps.ini,sha256=vxVVy9oUw31to8CaagNZj9MQpGHIK2xARN4nzHy4vZ0,1270
31
- ybox/conf/profiles/basic.ini,sha256=FGSDOooI4meXhQlwFpPo_RxZ62it3bvdjqWkpVVDcA4,19713
31
+ ybox/conf/profiles/basic.ini,sha256=7RAJUtQzpUbyJSynoxSaC-l-unQbB3pOtG5DMBOvs9I,21322
32
32
  ybox/conf/profiles/dev.ini,sha256=Qicas_96ZoG3nsm1MSE1urarlBzIj9kBGmUqrk_cD3g,976
33
33
  ybox/conf/profiles/games.ini,sha256=FiBILNFOMpjKhrIR9nPbPj9QDUeI5h9wZaNXIb6Z7dc,1792
34
34
  ybox/conf/resources/entrypoint-base.sh,sha256=hcW8ZLHM-jlHx7McoKTo8HIFz_VBBPD0I3WqlX7LjHs,4890
35
35
  ybox/conf/resources/entrypoint-common.sh,sha256=fMopKBLeGuVV0ukANXh_ZHGTR1yGq108CTN_M2MNPyQ,461
36
36
  ybox/conf/resources/entrypoint-cp.sh,sha256=jemWFCqfme9blVtfVxqBzCsCp7b-bN2aFLajruF0GGQ,814
37
37
  ybox/conf/resources/entrypoint-root.sh,sha256=58xcDX58ZtEFSr7ZSUFmzKt1OyGU29OrxjOmDiR6V8Q,706
38
- ybox/conf/resources/entrypoint-user.sh,sha256=Tps_UyQGyGn30LbgKm3gpzZtjOHnJpnlKx-px2GAd7A,698
39
- ybox/conf/resources/entrypoint.sh,sha256=jbfFfUprU7ycp0moZLXNdnxk_bhtDxAk6byEiPMNz-s,8560
38
+ ybox/conf/resources/entrypoint-user.sh,sha256=aH_bPcXbmevIsAlFwFT0Npucxi5lRHfpwH_lP4cB_ZY,711
39
+ ybox/conf/resources/entrypoint.sh,sha256=yOLEcujvawDtxrVbHbNsTZ4egobjO_F_BNR5m8pHdB0,8842
40
40
  ybox/conf/resources/prime-run,sha256=en8wEspc2Hzod9rq8KKnPQrjIPTLjUk44TqmtX-g0sw,339
41
- ybox/conf/resources/run-in-dir,sha256=-Xnwp4n6TK_2yPeBhGX6agenztyOtZ0316-vUlfOIag,2299
41
+ ybox/conf/resources/run-in-dir,sha256=1Lt1LeIRcyFb18OHtzqo0SH72GSz1-o_igtJjBT5BJk,2289
42
42
  ybox/conf/resources/run-user-bash-cmd,sha256=LMlPoHtzYNDcOI885ouBha9xGRnQ6AWCFVsSu2dxy10,1065
43
43
  ybox/conf/resources/ybox-systemd.template,sha256=bA-bJOmc07Be-mQYtoFzl0yq4SJACv-FkAoKscPTh3g,779
44
44
  ybox/migrate/0.9.0-0.9.10:0.9.11.py,sha256=WHvIWvUhXnruzSYwWYnv2zPNOlkXfAyaaJ_nZwG2wwE,1627
45
45
  ybox/pkg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
- ybox/pkg/clean.py,sha256=UlsrJLfZOyKg-7BE8s9xPvAyuUXSwvU5x-E7MMjGJSE,1219
46
+ ybox/pkg/clean.py,sha256=yYzw8pdH8ipkxi_Z4PK0qO53VeCy3xVxqzIEVj8X8ig,2062
47
47
  ybox/pkg/info.py,sha256=HoEsiAi-iPEnAOWBMy9SsA4TOfyqHonRURU8k5EeAWA,1607
48
- ybox/pkg/inst.py,sha256=SJFysYqEepc_NvAmMH32OesXtP3eYmGJsctra6jF1go,36914
48
+ ybox/pkg/inst.py,sha256=GKbkfq1Nfrrk48RnlnBRqDVoMatZCQhfA4r_QRIV9ew,37864
49
49
  ybox/pkg/list.py,sha256=Sk5THAmF132HKEZxJVDlqLLR8Z2w1wRA_E-gBsxlesQ,10097
50
50
  ybox/pkg/mark.py,sha256=Hb1FaowdBx8x3GFcakXsq4ERNqI60Gjljr24qXsDGR8,3748
51
51
  ybox/pkg/repair.py,sha256=rCwXXJbilJYU73feIIVWGFdbkh6SDcquxgiEnYV6pNs,8080
@@ -56,12 +56,12 @@ ybox/pkg/update.py,sha256=M-1MC8oh-baTHdSPWUUSTU_AhN3eu2nTCSge3bb6GmI,3269
56
56
  ybox/run/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  ybox/run/cmd.py,sha256=a77RMC14Vovx2NV3LgdAzXVZXStC1TucgjcHPz4x03A,2105
58
58
  ybox/run/control.py,sha256=Lvew2tqtWy5xcZMPXuh5sTrM2K92MykXfdMfBuKsIDc,7540
59
- ybox/run/create.py,sha256=N2ULSoBsMWamBcm_JMLeZQdvMPhilhzFeM1aMkPDw7s,73062
60
- ybox/run/destroy.py,sha256=i3N2zRCgrQM_lGA6W28O6Y-D1NIwBP64PA1e6vhOkL4,6315
61
- ybox/run/graphics.py,sha256=mKQFU0la83Rv2V5l9TS25KIbqYmMnZjGQgGghLlfQp8,20309
59
+ ybox/run/create.py,sha256=W-CHKSMV4w-hHnpeYT17H_4x2TzXL29bOCGMo45z7pg,76732
60
+ ybox/run/destroy.py,sha256=JF7LwS0yBUwXUS_McEojsBM1roXoJrRf6wOdTK2QU1c,6492
61
+ ybox/run/graphics.py,sha256=H1_tJEgiW8n_SsYb5icPYCkAK6nwHDl5O337HP38JIg,20212
62
62
  ybox/run/logs.py,sha256=pIdMWgNBNl-MgixArbMryUuBNNbi5JvDFP62IZ7jwr8,2050
63
63
  ybox/run/ls.py,sha256=7ylyxOOYEsVWK8baM0GaZcUlVQBwpdGiF7EhU09xf2s,2787
64
- ybox/run/pkg.py,sha256=6pes_wpVmPDiFYGfr8568GpyMByzor4dK5DSWaYmdsE,27219
64
+ ybox/run/pkg.py,sha256=ahBsw7hQ50cK6Kh6E3jxyBt3dDS5o8aGGoEziItSz0I,27325
65
65
  ybox/schema/0.9.1-added.sql,sha256=1rGp2DczZmmC_xwjmheeZNPSbDpFzasu6LO3tpTy3zI,1049
66
66
  ybox/schema/0.9.6-added.sql,sha256=Qcho6dP5OUpPUW3IBWl_kv88agMPHzueUAKqnZPnt3U,809
67
67
  ybox/schema/init.sql,sha256=fei8lPvjb-EIjm5zuA_XkEdjsIE3vtROhgRPt7QMlSs,1599
@@ -69,9 +69,9 @@ ybox/schema/migrate/0.9.0:0.9.1.sql,sha256=e9JGwrjFZXdWKGv2JQZlKcWz8DmOuUARpToSs
69
69
  ybox/schema/migrate/0.9.1:0.9.2.sql,sha256=X5J3unDS0eLeVvYKxQgx-iUBoAOk9T2suO34pWlQ-lE,362
70
70
  ybox/schema/migrate/0.9.2:0.9.3.sql,sha256=Y7GeBSuEEs7Hs9hh-KYDARgeeMgwQwercvTB5P_-k6I,102
71
71
  ybox/schema/migrate/0.9.5:0.9.6.sql,sha256=wqYhmputlUQzBI5zfP7O5kqIFWAbZQ05kolyHK4714A,70
72
- ybox-0.9.11.1.dist-info/licenses/LICENSE,sha256=7GbFgERMXSwD1VyLA5bo_XHvJipmNEUhDW5Sy51TFeY,1077
73
- ybox-0.9.11.1.dist-info/METADATA,sha256=b4u_-amCHtBGQUIqgCCHR5Pr2PqykswthwDL-7CMc2g,25774
74
- ybox-0.9.11.1.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
75
- ybox-0.9.11.1.dist-info/entry_points.txt,sha256=xDlI_84Hl3ytYO_ERyt0rkJ4ioUF8Z1r49Hx1xL28Rk,243
76
- ybox-0.9.11.1.dist-info/top_level.txt,sha256=DYX7jvndHcBaJXLJ8vDyKrq0_KWoSeXXFq8r0d5L6Nk,5
77
- ybox-0.9.11.1.dist-info/RECORD,,
72
+ ybox-0.9.12.dist-info/licenses/LICENSE,sha256=7GbFgERMXSwD1VyLA5bo_XHvJipmNEUhDW5Sy51TFeY,1077
73
+ ybox-0.9.12.dist-info/METADATA,sha256=YUT1FLiwOdvY-4q8qJz06LLgt-ljwCTS4BcbHPCuTyU,25772
74
+ ybox-0.9.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
75
+ ybox-0.9.12.dist-info/entry_points.txt,sha256=xDlI_84Hl3ytYO_ERyt0rkJ4ioUF8Z1r49Hx1xL28Rk,243
76
+ ybox-0.9.12.dist-info/top_level.txt,sha256=DYX7jvndHcBaJXLJ8vDyKrq0_KWoSeXXFq8r0d5L6Nk,5
77
+ ybox-0.9.12.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5