diff --git a/conty-start.sh b/conty-start.sh index 2ce3cc4..469844c 100755 --- a/conty-start.sh +++ b/conty-start.sh @@ -15,18 +15,62 @@ if (( EUID == 0 )) && [ -z "$ALLOW_ROOT" ]; then fi # Conty version -script_version="1.23.1" +script_version="1.24" # Important variables to manually adjust after modification! # Needed to avoid problems with mounting due to an incorrect offset. -script_size=34762 -utils_size=2564623 +# +# If you build Conty without some of the components, you can set their +# size to 0 +init_size=40000 +bash_size=1339208 +script_size=35842 +busybox_size=1161112 +utils_size=4049807 # Full path to the script -script_literal="${BASH_SOURCE[0]}" +if [ -n "${BASH_SOURCE[0]}" ]; then + script_literal="${BASH_SOURCE[0]}" +else + script_literal="${0}" +fi script_name="$(basename "${script_literal}")" script="$(readlink -f "${script_literal}")" +# MD5 of the last 1 MB of the file +script_md5="$(tail -c 1000000 "${script}" | md5sum | head -c 7)" +script_id="$$" + +# Working directory where the utils will be extracted +# And where the image will be mounted +# The default path is /tmp/scriptname_username_scriptmd5 +# And if /tmp is mounted with noexec, the default path +# is ~/.local/share/Conty/scriptname_username_scriptmd5 +conty_dir_name="$(basename "${script}")"_"${USER}"_"${script_md5}" + +if [ -z "${BASE_DIR}" ]; then + export working_dir=/tmp/"${conty_dir_name}" +else + export working_dir="${BASE_DIR}"/"${conty_dir_name}" +fi + +if [ "${USE_SYS_UTILS}" != 1 ] && [ "${busybox_size}" -gt 0 ]; then + busybox_bin_dir="${working_dir}"/busybox_bins + busybox_path="${busybox_bin_dir}"/busybox + + if [ ! -f "${busybox_bin_dir}"/echo ]; then + mkdir -p "${busybox_bin_dir}" + tail -c +$((init_size+bash_size+script_size+1)) "${script}" | head -c "${busybox_size}" > "${busybox_path}" + + chmod +x "${busybox_path}" 2>/dev/null + "${busybox_path}" --install -s "${busybox_bin_dir}" &>/dev/null + fi + + if "${busybox_bin_dir}"/echo &>/dev/null; then + export PATH="${busybox_bin_dir}:${PATH}" + fi +fi + # Help output msg_help=" Usage: ${script_name} [COMMAND] [ARGUMENTS] @@ -63,10 +107,6 @@ Arguments: Additional disk space (about 6x the size of the current file) is needed during the update process. - -U Same as -u with the addition of updating the init script and - the integrated utils. This option may break Conty in some cases, - use with caution! - -v Display version of this script -V Display version of the image @@ -135,6 +175,12 @@ Environment variables: XEPHYR_SIZE Sets the size of the Xephyr window. The default is 800x600. + CUSTOM_MNT Sets a custom mount point for the Conty. This allows + Conty to be used with already mounted filesystems. + Conty will not mount its image on this mount point, + but it will use files that are already present + there. + Additional notes: System directories/files will not be available inside the container if you set the SANDBOX variable but don't bind (mount) any items or set @@ -150,8 +196,8 @@ example, from a file manager) will automatically launch the Conty's graphical interface. Besides updating all packages, you can also install and remove packages -using the same -u (or -U) argument. To install packages add them as -additional arguments, to remove add a minus sign (-) before their names. +using the same -u argument. To install packages add them as additional +arguments, to remove add a minus sign (-) before their names. To install: ${script_name} -u pkgname1 pkgname2 pkgname3 ... To remove: ${script_name} -u -pkgname1 -pkgname2 -pkgname3 ... In this case Conty will update all packages and additionally install @@ -161,31 +207,17 @@ If you are using an Nvidia GPU, please read the following: https://github.com/Kron4ek/Conty#known-issues " -# MD5 of the last 1 MB of the file -script_md5="$(tail -c 1000000 "${script}" | md5sum | head -c 7)" - -script_id="${RANDOM}" - -# Working directory where the utils will be extracted -# And where the image will be mounted -# The default path is /tmp/scriptname_username_scriptmd5 -# And if /tmp is mounted with noexec, the default path -# is ~/.local/share/Conty/scriptname_username_scriptmd5 -conty_dir_name="$(basename "${script}")"_"${USER}"_"${script_md5}" - -if [ -z "${BASE_DIR}" ]; then - export working_dir=/tmp/"${conty_dir_name}" +if [ -n "${CUSTOM_MNT}" ] && [ -d "${CUSTOM_MNT}" ]; then + mount_point="${CUSTOM_MNT}" else - export working_dir="${BASE_DIR}"/"${conty_dir_name}" + mount_point="${working_dir}"/mnt fi -mount_point="${working_dir}"/mnt - export overlayfs_dir="${HOME}"/.local/share/Conty/overlayfs_"${script_md5}" export nvidia_drivers_dir="${HOME}"/.local/share/Conty/nvidia_"${script_md5}" # Offset where the image is stored -offset=$((script_size+utils_size)) +offset=$((init_size+bash_size+script_size+busybox_size+utils_size)) # Detect if the image is compressed with DwarFS or SquashFS if [ "$(tail -c +$((offset+1)) "${script}" | head -c 6)" = "DWARFS" ]; then @@ -201,6 +233,9 @@ dwarfs_comp_arguments=(-l7 -C zstd:level=19 --metadata-compression null \ -S 22 -B 2 --order nilsimsa:255:60000:60000 \ --bloom-filter-size 11 -W 15 -w 3 --no-create-timestamp) +# Enable NVIDIA_HANDLER by default +NVIDIA_HANDLER="${NVIDIA_HANDLER:-1}" + unset script_is_symlink if [ -L "${script_literal}" ]; then script_is_symlink=1 @@ -323,27 +358,45 @@ mount_overlayfs () { } nvidia_driver_handler () { - if lsmod | grep nvidia 1>/dev/null || nvidia-smi 1>/dev/null; then - if [ "$(ls /tmp/hostlibs/x86_64-linux-gnu/libGLX_nvidia.so.* 2>/dev/null)" ]; then - nvidia_driver_version="$(basename /tmp/hostlibs/x86_64-linux-gnu/libGLX_nvidia.so.*.* | tail -c +18)" - elif [ "$(ls /tmp/hostlibs/libGLX_nvidia.so.* 2>/dev/null)" ]; then - nvidia_driver_version="$(basename /tmp/hostlibs/libGLX_nvidia.so.*.* | tail -c +18)" - elif nvidia-smi 1>/dev/null; then - nvidia_driver_version="$(nvidia-smi --query-gpu=driver_version --format=csv,noheader)" - elif modinfo nvidia &>/dev/null; then - nvidia_driver_version="$(modinfo -F version nvidia 2>/dev/null)" - fi - else - echo "Seems like your system has no Nvidia driver loaded" + if [ -f /sys/module/nvidia/version ]; then + nvidia_driver_version="$(cat /sys/module/nvidia/version)" + fi + + if [ -z "${nvidia_driver_version}" ] && \ + [ "$(ls /tmp/hostlibs/x86_64-linux-gnu/libGLX_nvidia.so.* 2>/dev/null)" ]; then + nvidia_driver_version="$(basename /tmp/hostlibs/x86_64-linux-gnu/libGLX_nvidia.so.*.* | tail -c +18)" + fi + + if ([ "${nvidia_driver_version}" = "*.*" ] || [ -z "${nvidia_driver_version}" ]) && \ + [ "$(ls /tmp/hostlibs/x86_64-linux-gnu/nvidia/current/libGLX_nvidia.so.* 2>/dev/null)" ]; then + nvidia_driver_version="$(basename /tmp/hostlibs/x86_64-linux-gnu/nvidia/current/libGLX_nvidia.so.*.* | tail -c +18)" + fi + + if ([ "${nvidia_driver_version}" = "*.*" ] || [ -z "${nvidia_driver_version}" ]) && \ + [ "$(ls /tmp/hostlibs/libGLX_nvidia.so.* 2>/dev/null)" ]; then + nvidia_driver_version="$(basename /tmp/hostlibs/libGLX_nvidia.so.*.* | tail -c +18)" + fi + + if ([ "${nvidia_driver_version}" = "*.*" ] || [ -z "${nvidia_driver_version}" ]) && nvidia-smi &>/dev/null; then + nvidia_driver_version="$(nvidia-smi --query-gpu=driver_version --format=csv,noheader)" + fi + + if ([ "${nvidia_driver_version}" = "*.*" ] || [ -z "${nvidia_driver_version}" ]) && modinfo nvidia &>/dev/null; then + nvidia_driver_version="$(modinfo -F version nvidia 2>/dev/null)" + fi + + if [ "$(ls /usr/lib/libGLX_nvidia.so.* 2>/dev/null)" ]; then + container_nvidia_version="$(basename /usr/lib/libGLX_nvidia.so.*.* | tail -c +18)" + fi + + if [ -z "${nvidia_driver_version}" ] || [ "${nvidia_driver_version}" = "" ]; then + echo "Unable to determine Nvidia driver version" + rm -f "${nvidia_drivers_dir}"/current-nvidia-version exit 1 fi - if [ -z "${nvidia_driver_version}" ]; then - echo "Unable to determine the Nvidia driver version" - exit 1 - fi - - if [ "$(cat "${nvidia_drivers_dir}"/current-nvidia-version 2>/dev/null)" = "${nvidia_driver_version}" ]; then + if [ "$(cat "${nvidia_drivers_dir}"/current-nvidia-version 2>/dev/null)" = "${nvidia_driver_version}" ] || \ + [ "${nvidia_driver_version}" = "${container_nvidia_version}" ]; then exit fi @@ -361,7 +414,7 @@ nvidia_driver_handler () { curl -#Lo nvidia.run "${driver_url}" # If the previous download failed, get the URL from FlatHub repo - if [ ! -s nvidia.run ]; then + if [ ! -s nvidia.run ] || [ "$(stat -c%s nvidia.run)" -lt 30000000 ]; then rm -f nvidia.run driver_url="https:$(curl -#Lo - "https://raw.githubusercontent.com/flathub/org.freedesktop.Platform.GL.nvidia/master/data/nvidia-${nvidia_driver_version}-i386.data" | cut -d ':' -f 6)" curl -#Lo nvidia.run "${driver_url}" @@ -391,6 +444,27 @@ nvidia_driver_handler () { cd "${OLD_PWD}" } +update_conty () { + reflector --protocol https --score 5 --sort rate --save /etc/pacman.d/mirrorlist + fakeroot -- pacman -Syy 2>/dev/null + date -u +"%d-%m-%Y %H:%M (DMY UTC)" > /version + fakeroot -- pacman --noconfirm -S archlinux-keyring 2>/dev/null + fakeroot -- pacman --noconfirm -S chaotic-keyring 2>/dev/null + rm -rf /etc/pacman.d/gnupg/* + fakeroot -- pacman-key --init + echo "keyserver hkps://keyserver.ubuntu.com" >> /etc/pacman.d/gnupg/gpg.conf + fakeroot -- pacman-key --populate archlinux + fakeroot -- pacman-key --populate chaotic + fakeroot -- pacman --noconfirm --overwrite "*" -Su 2>/dev/null + fakeroot -- pacman --noconfirm -Runs ${pkgsremove} 2>/dev/null + fakeroot -- pacman --noconfirm -S ${pkgsinstall} 2>/dev/null + ldconfig -C /etc/ld.so.cache + rm -f /var/cache/pacman/pkg/* + pacman -Q > /pkglist.x86_64.txt + update-ca-trust + locale-gen +} + # Check if FUSE is installed if ! command -v fusermount3 1>/dev/null && ! command -v fusermount 1>/dev/null; then echo "Please install fuse2 or fuse3 and run the script again." @@ -434,12 +508,15 @@ fi # Extract utils.tar.gz mkdir -p "${working_dir}" -if [ "${USE_SYS_UTILS}" != 1 ]; then +if ([ "${USE_SYS_UTILS}" != 1 ] && [ "${utils_size}" -gt 0 ]) || [ "$1" = "-u" ]; then # Check if filesystem of the working_dir is mounted without noexec if ! exec_test; then if [ -z "${BASE_DIR}" ]; then export working_dir="${HOME}"/.local/share/Conty/"${conty_dir_name}" - mount_point="${working_dir}"/mnt + + if [ -z "${CUSTOM_MNT}" ]; then + mount_point="${working_dir}"/mnt + fi fi if ! exec_test; then @@ -458,16 +535,18 @@ if [ "${USE_SYS_UTILS}" != 1 ]; then if [ "${dwarfs_image}" = 1 ]; then mount_tool="${working_dir}"/utils/dwarfs"${fuse_version}" extraction_tool="${working_dir}"/utils/dwarfsextract + compression_tool="${working_dir}"/utils/mkdwarfs else mount_tool="${working_dir}"/utils/squashfuse"${fuse_version}" extraction_tool="${working_dir}"/utils/unsquashfs + compression_tool="${working_dir}"/utils/mksquashfs fi bwrap="${working_dir}"/utils/bwrap fuse_overlayfs="${working_dir}"/utils/fuse-overlayfs if [ ! -f "${mount_tool}" ] || [ ! -f "${bwrap}" ]; then - tail -c +$((script_size+1)) "${script}" | head -c "${utils_size}" | tar -C "${working_dir}" -zxf - + tail -c +$((init_size+bash_size+script_size+busybox_size+1)) "${script}" | head -c "${utils_size}" | tar -C "${working_dir}" -zxf - if [ ! -f "${mount_tool}" ] || [ ! -f "${bwrap}" ]; then clear @@ -481,6 +560,7 @@ if [ "${USE_SYS_UTILS}" != 1 ]; then chmod +x "${bwrap}" 2>/dev/null chmod +x "${extraction_tool}" 2>/dev/null chmod +x "${fuse_overlayfs}" 2>/dev/null + chmod +x "${compression_tool}" 2>/dev/null fi else if ! command -v bwrap 1>/dev/null; then @@ -546,212 +626,6 @@ if [ "$1" = "-H" ] && [ -z "${script_is_symlink}" ]; then exit fi -if { [ "$1" = "-u" ] || [ "$1" = "-U" ]; } && [ -z "${script_is_symlink}" ]; then - OLD_PWD="${PWD}" - - # Check if the current directory is writable - # And if it's not, use ~/.local/share/Conty as a working directory - if ! touch test_rw 2>/dev/null; then - update_temp_dir="${HOME}"/.local/share/Conty/conty_update_temp - else - update_temp_dir="${OLD_PWD}"/conty_update_temp - fi - rm -f test_rw - - # Remove conty_update_temp directory if it already exists - chmod -R 700 "${update_temp_dir}" 2>/dev/null - rm -rf "${update_temp_dir}" - - mkdir -p "${update_temp_dir}" - cd "${update_temp_dir}" || exit 1 - - if command -v awk 1>/dev/null; then - current_file_size="$(stat -c "%s" "${script}")" - available_disk_space="$(df -P -B1 "${update_temp_dir}" | awk 'END {print $4}')" - required_disk_space="$((current_file_size*7))" - - if [ "${available_disk_space}" -lt "${required_disk_space}" ]; then - echo "Not enough free disk space" - echo "You need at least $((required_disk_space/1024/1024)) MB of free space" - exit 1 - fi - fi - - tail -c +$((script_size+1)) "${script}" | head -c "${utils_size}" | tar -C "${update_temp_dir}" -zxf - - - if [ "${dwarfs_image}" = 1 ]; then - chmod +x utils/dwarfsextract 2>/dev/null - chmod +x utils/mkdwarfs 2>/dev/null - - if [ ! -x "utils/dwarfsextract" ] || [ ! -x "utils/mkdwarfs" ]; then - missing_utils="dwarfsextract and/or mkdwarfs" - fi - else - chmod +x utils/unsquashfs 2>/dev/null - chmod +x utils/mksquashfs 2>/dev/null - - if [ ! -x "utils/unsquashfs" ] || [ ! -x "utils/mksquashfs" ]; then - missing_utils="unsquashfs and/or mksquashfs" - fi - fi - - if [ -n "${missing_utils}" ]; then - echo "The integrated utils don't contain ${missing_utils}." - echo "Or your file system is mounted with noexec." - exit 1 - fi - - tools_wrapper () { - "${update_temp_dir}"/utils/ld-linux-x86-64.so.2 --library-path "${update_temp_dir}"/utils "$@" - } - - # Since Conty is used here to update itself, it's necessary to disable - # some environment variables for this to work properly - unset DISABLE_NET - unset HOME_DIR - unset SANDBOX_LEVEL - - # Enable SANDBOX - export SANDBOX=1 - - export QUIET_MODE=1 - - # Extract the image - clear - echo "Extracting the image" - if [ "${dwarfs_image}" = 1 ]; then - mkdir sqfs - tools_wrapper "${update_temp_dir}"/utils/dwarfsextract \ - -i "${script}" -o sqfs -O "${offset}" --cache-size "${dwarfs_cache_size}" \ - --num-workers "${dwarfs_num_workers}" - else - tools_wrapper "${update_temp_dir}"/utils/unsquashfs \ - -o "${offset}" -user-xattrs -d sqfs "${script}" - fi - - # Download or extract the utils.tar.gz and the init script depending - # on what command line argument is used (-u or -U) - clear - if [ "$1" = "-U" ] && command -v curl 1>/dev/null; then - if [ "${dwarfs_image}" = 1 ]; then - utils="utils_dwarfs.tar.gz" - else - utils="utils.tar.gz" - fi - - echo "Downloading the init script and the utils" - curl -#LO "https://github.com/Kron4ek/Conty/raw/master/conty-start.sh" - curl -#Lo utils.tar.gz "https://github.com/Kron4ek/Conty/raw/master/${utils}" - fi - - if [ ! -s conty-start.sh ] || [ ! -s utils.tar.gz ]; then - echo "Extracting the init script and the integrated utils" - tail -c +$((script_size+1)) "${script}" | head -c "${utils_size}" > utils.tar.gz - head -c "${script_size}" "${script}" > conty-start.sh - fi - - # Check if there are additional arguments passed - shift - if [ -n "$1" ]; then - packagelist=("$@") - - # Check which packages to install and which ones to remove - for i in "${packagelist[@]}"; do - if [ "$(echo "${i}" | head -c 1)" = "-" ]; then - pkgsremove+=" ${i:1}" - else - pkgsinstall+=" ${i}" - fi - done - - export pkgsremove - export pkgsinstall - fi - - # Generate a script to perform inside Conty - # It updates Arch mirrorlist - # Updates keyrings - # Updates all installed packages - # Installs additional packages (if requested) - # Removes packages (if requested) - # Clears package cache - # Updates SSL CA certificates - # Generates locales - cat < container-update.sh -reflector --protocol https --score 5 --sort rate --save /etc/pacman.d/mirrorlist -fakeroot -- pacman -Syy 2>/dev/null -date -u +"%d-%m-%Y %H:%M (DMY UTC)" > /version -fakeroot -- pacman --noconfirm -S archlinux-keyring 2>/dev/null -fakeroot -- pacman --noconfirm -S chaotic-keyring 2>/dev/null -rm -rf /etc/pacman.d/gnupg -fakeroot -- pacman-key --init -echo "keyserver hkps://keyserver.ubuntu.com" >> /etc/pacman.d/gnupg/gpg.conf -fakeroot -- pacman-key --populate archlinux -fakeroot -- pacman-key --populate chaotic -fakeroot -- pacman --noconfirm --overwrite "*" -Su 2>/dev/null -fakeroot -- pacman --noconfirm -Runs ${pkgsremove} 2>/dev/null -fakeroot -- pacman --noconfirm -S ${pkgsinstall} 2>/dev/null -ldconfig -C /etc/ld.so.cache -rm -f /var/cache/pacman/pkg/* -pacman -Qn > /pkglist.x86_64.txt -pacman -Qm >> /pkglist.x86_64.txt -update-ca-trust -locale-gen -EOF - - rm -f sqfs/etc/resolv.conf - cp /etc/resolv.conf sqfs/etc/resolv.conf - mkdir -p sqfs/run/shm - - # Execute the previously generated script - clear - echo "Updating and installing packages" - bash "${script}" --bind sqfs / --ro-bind /sys /sys --dev-bind /dev /dev \ - --proc /proc --bind "${update_temp_dir}" "${update_temp_dir}" \ - bash container-update.sh - - # Create an image - clear - echo "Creating an image" - if [ "${dwarfs_image}" = 1 ]; then - tools_wrapper "${update_temp_dir}"/utils/mkdwarfs \ - -i sqfs -o image "${dwarfs_comp_arguments[@]}" - else - tools_wrapper "${update_temp_dir}"/utils/mksquashfs \ - sqfs image "${squashfs_comp_arguments[@]}" - fi - - # Combine into a single executable - clear - echo "Combining everything into a single executable" - cat conty-start.sh utils.tar.gz image > conty_updated.sh - chmod +x conty_updated.sh - - mv -f "${script}" "${script}".old."${script_md5}" 2>/dev/null - mv -f conty_updated.sh "${script}" 2>/dev/null || move_failed=1 - - if [ "${move_failed}" = 1 ]; then - mv -f conty_updated.sh "${OLD_PWD}" 2>/dev/null || \ - mv -f conty_updated.sh "${HOME}" 2>/dev/null - fi - - chmod -R 700 sqfs 2>/dev/null - rm -rf "${update_temp_dir}" - - clear - echo "Conty has been updated!" - - if [ "${move_failed}" = 1 ]; then - echo - echo "Replacing ${script} with the new one failed!" - echo - echo "You can find conty_updated.sh in the current working" - echo "directory or in your HOME." - fi - - exit -fi - run_bwrap () { unset sandbox_params unset unshare_net @@ -759,6 +633,7 @@ run_bwrap () { unset non_standard_home unset xsockets unset mount_opt + unset pacman_dirs if [ -n "${WAYLAND_DISPLAY}" ]; then wayland_socket="${WAYLAND_DISPLAY}" @@ -887,10 +762,20 @@ run_bwrap () { if ([ "${NVIDIA_HANDLER}" = 1 ] || [ "${USE_OVERLAYFS}" = 1 ]) && \ [ "$(ls "${overlayfs_dir}"/merged 2>/dev/null)" ]; then newroot_path="${overlayfs_dir}"/merged + pacman_dirs=(--bind-try "${overlayfs_dir}"/merged/var/lib/pacman /var/lib/pacman \ + --bind-try "${overlayfs_dir}"/merged/var/cache/pacman /var/cache/pacman) else newroot_path="${mount_point}" fi + conty_variables="BASE_DIR DISABLE_NET DISABLE_X11 HOME_DIR QUIET_MODE \ + SANDBOX SANDBOX_LEVEL USE_OVERLAYFS NVIDIA_HANDLER \ + USE_SYS_UTILS XEPHYR_SIZE CUSTOM_MNT" + + for v in ${conty_variables}; do + unset_vars+=(--unsetenv "${v}") + done + show_msg launch_wrapper "${bwrap}" \ @@ -916,9 +801,11 @@ run_bwrap () { "${non_standard_home[@]}" \ "${sandbox_params[@]}" \ "${custom_home[@]}" \ + "${pacman_dirs[@]}" \ "${mount_opt[@]}" \ "${xsockets[@]}" \ "${unshare_net[@]}" \ + "${unset_vars[@]}" \ --setenv PATH "${CUSTOM_PATH}" \ "$@" } @@ -932,10 +819,12 @@ trap_exit () { umount --lazy "${overlayfs_dir}"/merged 2>/dev/null fi - fusermount"${fuse_version}" -uz "${mount_point}" 2>/dev/null || \ - umount --lazy "${mount_point}" 2>/dev/null + if [ -z "${CUSTOM_MNT}" ]; then + fusermount"${fuse_version}" -uz "${mount_point}" 2>/dev/null || \ + umount --lazy "${mount_point}" 2>/dev/null + fi - if [ ! "$(ls "${mount_point}" 2>/dev/null)" ]; then + if [ ! "$(ls "${mount_point}" 2>/dev/null)" ] || [ -n "${CUSTOM_MNT}" ]; then rm -rf "${working_dir}" fi fi @@ -1048,6 +937,118 @@ if [ "$(ls "${mount_point}" 2>/dev/null)" ] || \ exit fi + if [ "$1" = "-u" ] && [ -z "${script_is_symlink}" ] && [ -z "${CUSTOM_MNT}" ]; then + export overlayfs_dir="${HOME}"/.local/share/Conty/update_overlayfs + rm -rf "${overlayfs_dir}" + + if mount_overlayfs; then + USE_OVERLAYFS=1 + QUIET_MODE=1 + unset DISABLE_NET + unset HOME_DIR + unset SANDBOX_LEVEL + unset SANDBOX + unset DISABLE_X11 + + if ! touch test_rw 2>/dev/null; then + cd "${HOME}" || exit 1 + fi + rm -f test_rw + + OLD_PWD="${PWD}" + mkdir conty_update_temp + cd conty_update_temp || exit 1 + + if command -v awk 1>/dev/null; then + current_file_size="$(stat -c "%s" "${script}")" + available_disk_space="$(df -P -B1 "${PWD}" | awk 'END {print $4}')" + required_disk_space="$((current_file_size*7))" + + if [ "${available_disk_space}" -lt "${required_disk_space}" ]; then + echo "Not enough free disk space" + echo "You need at least $((required_disk_space/1024/1024)) MB of free space" + exit 1 + fi + fi + + # Check if there are additional arguments passed + shift + if [ -n "$1" ]; then + packagelist=("$@") + + # Check which packages to install and which ones to remove + for i in "${packagelist[@]}"; do + if [ "$(echo "${i}" | head -c 1)" = "-" ]; then + pkgsremove+=" ${i:1}" + else + pkgsinstall+=" ${i}" + fi + done + + export pkgsremove + export pkgsinstall + fi + + clear + echo "Updating and installing packages..." + cp -r "${mount_point}"/etc/pacman.d/gnupg "${overlayfs_dir}"/gnupg + export -f update_conty + run_bwrap --bind "${overlayfs_dir}"/gnupg /etc/pacman.d/gnupg \ + --bind "${overlayfs_dir}"/merged/var/lib/pacman /var/lib/pacman \ + --bind-try "${overlayfs_dir}"/merged/var/cache/pacman /var/cache/pacman \ + bash -c update_conty + + if [ "${dwarfs_image}" = 1 ]; then + compression_command=("${compression_tool}" -i "${overlayfs_dir}"/merged -o image "${dwarfs_comp_arguments[@]}") + else + compression_command=("${compression_tool}" "${overlayfs_dir}"/merged image "${squashfs_comp_arguments[@]}") + fi + + clear + echo "Creating an image..." + launch_wrapper "${compression_command[@]}" + + if [ "${init_size}" -gt 0 ]; then + tail -c +$((init_size+bash_size+1)) "${script}" | head -c "${script_size}" > conty-start.sh + else + head -c "${script_size}" "${script}" > conty-start.sh + fi + + tail -c +$((init_size+bash_size+script_size+busybox_size+1)) "${script}" | head -c "${utils_size}" > utils.tar.gz + + # Combine into a single executable + clear + echo "Combining everything into a single executable..." + cat "${working_dir}"/utils/init "${working_dir}"/utils/bash \ + conty-start.sh "${working_dir}"/utils/busybox utils.tar.gz \ + image > conty_updated.sh + chmod +x conty_updated.sh + + mv -f "${script}" "${script}".old."${script_md5}" 2>/dev/null + mv -f conty_updated.sh "${script}" 2>/dev/null || move_failed=1 + + fusermount"${fuse_version}" -uz "${overlayfs_dir}"/merged 2>/dev/null || \ + umount --lazy "${overlayfs_dir}"/merged 2>/dev/null + rm -rf "${overlayfs_dir}" "${OLD_PWD}"/conty_update_temp + + clear + echo "Conty has been updated!" + + if [ "${move_failed}" = 1 ]; then + echo + echo "Replacing ${script} with the new one failed!" + echo + echo "You can find conty_updated.sh in the current working" + echo "directory or in your HOME." + fi + else + echo "Failed to mount overlayfs" + echo "Cannot update Conty" + fi + + exit + fi + if [ "${USE_OVERLAYFS}" = 1 ]; then if mount_overlayfs; then show_msg "Using overlayfs" @@ -1058,23 +1059,31 @@ if [ "$(ls "${mount_point}" 2>/dev/null)" ] || \ fi if [ "${NVIDIA_HANDLER}" = 1 ]; then - if mount_overlayfs; then - show_msg "Nvidia driver handler is enabled" + if [ -f /sys/module/nvidia/version ] || lsmod | grep nvidia 1>/dev/null; then + if mount_overlayfs; then + show_msg "Nvidia driver handler is enabled" - if [ -f "${nvidia_drivers_dir}"/current-nvidia-version ] && \ - [ ! "$(ls "${overlayfs_dir}"/up 2>/dev/null)" ]; then - rm -f "${nvidia_drivers_dir}"/current-nvidia-version - fi + if [ -f "${nvidia_drivers_dir}"/current-nvidia-version ] && \ + [ ! "$(ls "${overlayfs_dir}"/up 2>/dev/null)" ]; then + rm -f "${nvidia_drivers_dir}"/current-nvidia-version + fi - mkdir -p "${nvidia_drivers_dir}" - export -f nvidia_driver_handler - DISABLE_NET=0 run_bwrap --tmpfs /tmp --tmpfs /var --tmpfs /run \ + mkdir -p "${nvidia_drivers_dir}" + export -f nvidia_driver_handler + DISABLE_NET=0 QUIET_MODE=1 run_bwrap --tmpfs /tmp --tmpfs /var --tmpfs /run \ --bind-try /usr/lib /tmp/hostlibs \ --bind-try /usr/lib64 /tmp/hostlibs \ - --bind-try "${nvidia_drivers_dir}" "${nvidia_drivers_dir}" \ + --bind "${nvidia_drivers_dir}" "${nvidia_drivers_dir}" \ bash -c nvidia_driver_handler + else + echo "Nvidia driver handler disabled due to overlayfs errors" + unset NVIDIA_HANDLER + fi else - echo "Nvidia driver handler disabled due to overlayfs errors" + unset NVIDIA_HANDLER + fi + + if [ ! -f "${nvidia_drivers_dir}"/current-nvidia-version ]; then unset NVIDIA_HANDLER fi fi diff --git a/create-arch-bootstrap.sh b/create-arch-bootstrap.sh index 4cdb90c..cec1b63 100755 --- a/create-arch-bootstrap.sh +++ b/create-arch-bootstrap.sh @@ -69,6 +69,7 @@ ar_EG.UTF-8 UTF-8 en_US.UTF-8 UTF-8 en_GB.UTF-8 UTF-8 en_CA.UTF-8 UTF-8 +en_SG.UTF-8 UTF-8 es_MX.UTF-8 UTF-8 zh_CN.UTF-8 UTF-8 fr_FR.UTF-8 UTF-8 @@ -78,6 +79,7 @@ es_ES.UTF-8 UTF-8 de_DE.UTF-8 UTF-8 pt_BR.UTF-8 UTF-8 it_IT.UTF-8 UTF-8 +id_ID.UTF-8 UTF-8 ja_JP.UTF-8 UTF-8 bg_BG.UTF-8 UTF-8 pl_PL.UTF-8 UTF-8 @@ -87,6 +89,7 @@ tr_TR.UTF-8 UTF-8 hu_HU.UTF-8 UTF-8 cs_CZ.UTF-8 UTF-8 bn_IN UTF-8 +hi_IN UTF-8 EOF } diff --git a/create-conty.sh b/create-conty.sh index bb831a6..1f5061b 100755 --- a/create-conty.sh +++ b/create-conty.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -# Dependencies: squashfs-tools or dwarfs +# Dependencies: sed, squashfs-tools or dwarfs script_dir="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" @@ -31,7 +31,6 @@ dwarfs_compressor_arguments=(-l7 -C zstd:level=19 --metadata-compression null \ use_existing_image="false" image_path="${script_dir}"/image - bootstrap="${script_dir}"/root.x86_64 launch_wrapper () { @@ -47,6 +46,11 @@ launch_wrapper () { fi } +if ! command -v sed 1>/dev/null; then + echo "sed is required" + exit 1 +fi + cd "${script_dir}" || exit 1 if [ "${dwarfs}" = "true" ]; then @@ -57,7 +61,7 @@ else compressor_command=(mksquashfs "${bootstrap}" "${image_path}" "${squashfs_compressor_arguments[@]}") fi -if [ ! -f "${utils}" ] || [ "$(wc -c < "${utils}")" -lt 1000 ]; then +if [ ! -f "${utils}" ] || [ "$(wc -c < "${utils}")" -lt 100000 ]; then rm -f "${utils}" curl -#LO "https://github.com/Kron4ek/Conty/raw/master/${utils}" fi @@ -75,10 +79,6 @@ if [ $? != 0 ]; then exit 1 fi -if [ "${USE_SYS_UTILS}" != 0 ]; then - rm -rf utils -fi - # Check if selected compression algorithm is supported by mksquashfs if [ "${USE_SYS_UTILS}" != 0 ] && [ "${dwarfs}" != "true" ] && command -v grep 1>/dev/null; then # mksquashfs writes its output to stderr instead of stdout @@ -112,8 +112,41 @@ if [ ! -f "${image_path}" ] || [ "${use_existing_image}" != "true" ]; then launch_wrapper "${compressor_command[@]}" fi +if command -v sed 1>/dev/null; then + utils_size="$(stat -c%s "${utils}")" + init_size=0 + bash_size=0 + busybox_size=0 + + if [ -s utils/init ]; then + init_size="$(stat -c%s utils/init)" + fi + + if [ -s utils/bash ]; then + bash_size="$(stat -c%s utils/bash)" + fi + + if [ -s utils/busybox ]; then + busybox_size="$(stat -c%s utils/busybox)" + fi + + if [ "${init_size}" = 0 ] || [ "${bash_size}" = 0 ]; then + init_size=0 + bash_size=0 + rm -f utils/init utils/bash + fi + + sed -i "s/init_size=.*/init_size=${init_size}/" conty-start.sh + sed -i "s/bash_size=.*/bash_size=${bash_size}/" conty-start.sh + sed -i "s/busybox_size=.*/busybox_size=${busybox_size}/" conty-start.sh + sed -i "s/utils_size=.*/utils_size=${utils_size}/" conty-start.sh + + sed -i "s/script_size=.*/script_size=$(stat -c%s conty-start.sh)/" conty-start.sh + sed -i "s/script_size=.*/script_size=$(stat -c%s conty-start.sh)/" conty-start.sh +fi + # Combine the files into a single executable using cat -cat conty-start.sh "${utils}" "${image_path}" > conty.sh +cat utils/init utils/bash conty-start.sh utils/busybox "${utils}" "${image_path}" > conty.sh chmod +x conty.sh clear diff --git a/create-utils.sh b/create-utils.sh index a34c1ef..a3c8836 100755 --- a/create-utils.sh +++ b/create-utils.sh @@ -1,7 +1,8 @@ #!/usr/bin/env bash # General build dependencies: gawk grep lz4 zstd curl gcc make autoconf -# libtool pkgconf libcap fuse2 (or fuse3) lzo xz zlib findutils +# libtool pkgconf libcap fuse2 (or fuse3) lzo xz zlib findutils musl +# kernel-headers-musl sed # # Dwarfs build dependencies: fuse2 (or fuse3) openssl jemalloc # xxhash boost lz4 xz zstd libarchive libunwind google-glod gtest fmt @@ -20,6 +21,8 @@ lz4_version="1.9.4" zstd_version="1.5.5" squashfs_tools_version="4.6.1" fuse_overlayfs_version="1.12" +busybox_version="1.36.1" +bash_version="5.2.15" export CC=gcc export CXX=g++ @@ -35,11 +38,16 @@ curl -#Lo lz4.tar.gz https://github.com/lz4/lz4/archive/refs/tags/v${lz4_version curl -#Lo zstd.tar.gz https://github.com/facebook/zstd/archive/refs/tags/v${zstd_version}.tar.gz curl -#Lo bwrap.tar.gz https://github.com/containers/bubblewrap/archive/refs/tags/v${bwrap_version}.tar.gz curl -#Lo fuse-overlayfs.tar.gz https://github.com/containers/fuse-overlayfs/archive/refs/tags/v${fuse_overlayfs_version}.tar.gz +curl -#Lo busybox.tar.bz2 https://busybox.net/downloads/busybox-${busybox_version}.tar.bz2 +curl -#Lo bash.tar.gz https://ftp.gnu.org/gnu/bash/bash-${bash_version}.tar.gz +cp "${script_dir}"/init.c init.c tar xf lz4.tar.gz tar xf zstd.tar.gz tar xf bwrap.tar.gz tar xf fuse-overlayfs.tar.gz +tar xf busybox.tar.bz2 +tar xf bash.tar.gz if [ "${build_dwarfs}" != "true" ]; then curl -#Lo squashfuse.tar.gz https://github.com/vasi/squashfuse/archive/refs/tags/${squashfuse_version}.tar.gz @@ -65,6 +73,19 @@ make -j"$(nproc)" DESTDIR="${script_dir}"/build-utils/bin install cd ../zstd-"${zstd_version}" || exit 1 ZSTD_LEGACY_SUPPORT=0 HAVE_ZLIB=0 HAVE_LZMA=0 HAVE_LZ4=0 BACKTRACE=0 make -j"$(nproc)" DESTDIR="${script_dir}"/build-utils/bin install +cd ../busybox-${busybox_version} || exit 1 +make defconfig +sed -i 's/# CONFIG_STATIC is not set/CONFIG_STATIC=y/g' .config +make CC=musl-gcc -j"$(nproc)" + +cd ../bash-${bash_version} +curl -#Lo bash.patch "https://raw.githubusercontent.com/robxu9/bash-static/master/custom/bash-musl-strtoimax-debian-1023053.patch" +patch -Np1 < ./bash.patch +CFLAGS="${CFLAGS} -static" CC=musl-gcc ./configure --without-bash-malloc +autoconf -f +CFLAGS="${CFLAGS} -static" CC=musl-gcc ./configure --without-bash-malloc +CFLAGS="${CFLAGS} -static" CC=musl-gcc make -j"$(nproc)" + if [ "${build_dwarfs}" != "true" ]; then cd ../squashfuse-"${squashfuse_version}" || exit 1 ./autogen.sh @@ -89,6 +110,9 @@ mv bin/usr/local/lib/liblz4.so."${lz4_version}" utils/liblz4.so.1 mv bin/usr/local/lib/libzstd.so."${zstd_version}" utils/libzstd.so.1 mv bin/usr/local/lib/libfuseprivate.so.0.0.0 utils/libfuseprivate.so.0 mv bin/usr/local/lib/libsquashfuse.so.0.0.0 utils/libsquashfuse.so.0 +mv "${script_dir}"/build-utils/busybox-${busybox_version}/busybox utils +mv "${script_dir}"/build-utils/bash-${bash_version}/bash utils +mv "${script_dir}"/build-utils/init utils if ! ldd utils/squashfuse | grep -q libfuse.so.2; then mv utils/squashfuse utils/squashfuse3 @@ -99,6 +123,8 @@ if [ "${build_dwarfs}" = "true" ]; then if command -v clang++ 1>/dev/null; then export CC=clang export CXX=clang++ + export CFLAGS="${CFLAGS} -O3" + export CXXFLAGS="${CFLAGS}" fi git clone https://github.com/mhx/dwarfs.git --recursive @@ -148,9 +174,33 @@ fi find utils -type f -exec strip --strip-unneeded {} \; 2>/dev/null +init_program_size=40000 +conty_script_size="$(($(stat -c%s "${script_dir}"/conty-start.sh)+1000))" +bash_size="$(stat -c%s utils/bash)" + +sed -i "s/#define SCRIPT_SIZE 0/#define SCRIPT_SIZE ${conty_script_size}/g" init.c +sed -i "s/#define BASH_SIZE 0/#define BASH_SIZE ${bash_size}/g" init.c +sed -i "s/#define PROGRAM_SIZE 0/#define PROGRAM_SIZE ${init_program_size}/g" init.c + +musl-gcc -o init -static init.c +strip --strip-unneeded init + +padding_size="$((init_program_size-$(stat -c%s init)))" + +if [ "${padding_size}" -gt 0 ]; then + dd if=/dev/zero of=padding bs=1 count="${padding_size}" &>/dev/null + cat init padding > init_new + rm -f init padding + mv init_new init +fi + +mv init utils + cat < utils/info bubblewrap ${bwrap_version} fuse-overlayfs ${fuse_overlayfs_version} +busybox ${busybox_version} +bash ${bash_version} lz4 ${lz4_version} zstd ${zstd_version} EOF diff --git a/init.c b/init.c new file mode 100644 index 0000000..ba49ffa --- /dev/null +++ b/init.c @@ -0,0 +1,61 @@ +// This is a bash-static initializer for Conty + +#define _GNU_SOURCE + +#include +#include +#include + +// Replace all 0 below before compilation + +// The size of our statically compiled bash binary +#define BASH_SIZE 0 + +// The size of conty-start.sh script +// It can be bigger than the actual size of the script +#define SCRIPT_SIZE 0 + +// The size of this program itself after compilation +// It can be bigger than the actual size of the program +#define PROGRAM_SIZE 0 + +// Bubblewrap can handle up to 9000 arguments +// And we reserve 1000 for internal use in Conty +#define MAX_ARGS_NUMBER 8000 + +int main(int argc, char* argv[]) +{ + if (argc > MAX_ARGS_NUMBER) { + printf("Too many arguments"); + return 1; + } + + char program_path[8192] = { 0 }; + int binary_code[BASH_SIZE + 1]; + char bash_script[SCRIPT_SIZE + 1]; + + readlink("/proc/self/exe", program_path, sizeof program_path); + FILE *current_program = fopen(program_path, "rb"); + int bash_binary = memfd_create("bash-static", 0); + + fseek(current_program, PROGRAM_SIZE, 0); + fread(binary_code, BASH_SIZE, 1, current_program); + write(bash_binary, binary_code, BASH_SIZE); + + fseek(current_program, PROGRAM_SIZE + BASH_SIZE, 0); + fread(bash_script, SCRIPT_SIZE, 1, current_program); + fclose(current_program); + + char * bash_args[MAX_ARGS_NUMBER + 5] = {program_path, "-c", "--", bash_script, argv[0]}; + + int k = 5; + for (int i = 1; i < argc; i++, k++) { + bash_args[k] = argv[i]; + } + bash_args[k] = NULL; + + fexecve(bash_binary, bash_args, environ); + printf("Failed to execute builtin bash-static"); + + return 0; +} diff --git a/utils.tar.gz b/utils.tar.gz index d268e33..9dd9b74 100644 --- a/utils.tar.gz +++ b/utils.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20b855587b07fa71c8be8e73220495fcee7c9ec290914e982b76fd184fa67094 -size 2564623 +oid sha256:eefb9b12b71605d0cc2925c497f2447b2eaf383540522d4967ccd8e2053b29c5 +size 4049807 diff --git a/utils_dwarfs.tar.gz b/utils_dwarfs.tar.gz index 3c35cef..7372112 100644 --- a/utils_dwarfs.tar.gz +++ b/utils_dwarfs.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef56bd72810c1c05a6f4cd77e816b2741ab014b421c00eb0337602c5a11d0959 -size 9431944 +oid sha256:bb6d97c95baab37870c80f73e190de4dbb4a29327054c8d24e3ecf7122b10ed2 +size 10917484