diff --git a/apptainer-images/distributions/al8/spec.sin b/apptainer-images/distributions/al8/spec.sin new file mode 100644 index 00000000..bd51a833 --- /dev/null +++ b/apptainer-images/distributions/al8/spec.sin @@ -0,0 +1,89 @@ +BootStrap: docker +From: gitlab-registry.cern.ch/linuxsupport/alma8-base:latest + +%labels + maintainer "CMS Data Preservation and Open Access Group " + org.label-schema.build-date $BUILD_DATE \ + org.label-schema.name "AlmaLinux 8 CMS base OS" \ + org.label-schema.description "AlmaLinux8 OS capable of using/running CMS software release(s)." \ + org.label-schema.url "http://cms-sw.github.io/" \ + org.label-schema.vcs-ref $VCS_REF \ + org.label-schema.vcs-url $VCS_URL \ + org.label-schema.vendor "CERN" \ + org.label-schema.version $VERSION \ + org.label-schema.schema-version "1.0" + +%files + ../../dotfiles/dot-pythonrc.py /etc/pythonrc.py + ../../dotfiles/dot-bashrc /etc/profile.d/bashrc.sh + ../../entrypoints/entrypoint-nd.sh /opt/cms/entrypoint.sh + cvmfsexec-mnt /opt/cms/cvmfsexec-mnt + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat8/cctools/current/bin/work_queue_worker /usr/bin/work_queue_worker + + # parrot from redhat7, it is ok because the binary is statically linked. + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/lobster-171-cd5e3e2c-cvmfs-70dfa0d6/bin/parrot_cvmfs_static_run /usr/bin/parrot_run + +%environment + export LC_ALL=en_US.UTF-8 + export LANG=en_US.UTF-8 + export USER=cmsusr + export HOME=/home/cmsusr + +%post + yum install -y libX11-devel libXext-devel mesa-libGLU-devel \ + mesa-libGL-devel libSM libXft libXext \ + pciutils glx-utils mesa-dri-drivers libX11 libXi libXrender \ + tcsh zsh tcl tk e2fsprogs perl-ExtUtils-Embed libXmu e2fsprogs-libs libXpm bc libaio \ + tar patch krb5-devel perl-Data-Dumper perl-CGI perl-DBI perl-YAML gcc unzip zip perl-libwww-perl libXpm-devel libXft-devel svn \ + gcc-c++ strace cern-wrappers krb5-workstation wget hostname readline-devel nano bzip2 perl-Switch perl-Storable \ + perl-Env perl-Thread-Queue CERN-CA-certs tk-devel tcl-devel which \ + java-1.8.0-openjdk java-1.8.0-openjdk-devel popt popt-devel libXcursor libXrandr libXinerama nspr nss nss-util nss-devel file file-libs \ + readline bzip2-libs libgfortran time \ + git \ + openssl \ + glibc-devel.i686 glibc-devel \ + glibc-headers \ + sudo nano && \ + yum clean -y all + + wget http://repository.egi.eu/sw/production/cas/1/current/repo-files/egi-trustanchors.repo && \ + mv egi-trustanchors.repo /etc/yum.repos.d/ && \ + wget http://repository.egi.eu/sw/production/cas/1/GPG-KEY-EUGridPMA-RPM-3 && \ + mv GPG-KEY-EUGridPMA-RPM-3 /etc/pki/rpm-gpg/ && \ + wget http://linuxsoft.cern.ch/wlcg/wlcg-centos8.repo && \ + mv wlcg-centos8.repo /etc/yum.repos.d/ && \ + wget http://linuxsoft.cern.ch/wlcg/RPM-GPG-KEY-wlcg && \ + mv RPM-GPG-KEY-wlcg /etc/pki/rpm-gpg/ && \ + yum install -y ca-policy-egi-core wlcg-repo.noarch wlcg-iam-lsc-cms HEP_OSlibs && \ + yum install -y glibc-langpack-en glibc-locale-source + mkdir /etc/vomses && \ + echo '"cms" "voms-cms-auth.app.cern.ch" "443" "/DC=ch/DC=cern/OU=computers/CN=cms-auth.web.cern.ch" "cms"' > /etc/vomses/voms-cms-auth.app.cern.ch.vomses && \ + yum clean -y all + + yum --enablerepo=extras install -y epel-release && \ + yum install -y voms-clients-cpp && \ + yum clean -y all + + echo "LC_ALL=en_US.UTF-8" >> /etc/environment && \ + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \ + echo "LANG=en_US.UTF-8" > /etc/locale.conf && \ + localedef -c -i en_US -f UTF-8 en_US.UTF-8 + + groupadd -g 1000 cmsusr && adduser -u 1000 -g 1000 -G root cmsusr && \ + echo "cmsusr ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers && \ + groupadd -g 1001 cmsinst && adduser -u 1001 -g 1001 cmsinst && \ + install -d /opt && install -d -o cmsinst /opt/cms + + mkdir -p /cvmfs /afs /eos /pool /code /tmp && \ + chmod 1777 /afs /eos /pool /code /tmp && \ + chown -R cmsusr:cmsusr /code + + chmod 755 /opt/cms/entrypoint.sh && \ + chown -R cmsusr /home/cmsusr && \ + chmod 755 /home/cmsusr + +%runscript + exec /opt/cms/entrypoint.sh "$@" + +%startscript + exec /opt/cms/entrypoint.sh "$@" diff --git a/apptainer-images/distributions/al9/spec.sin b/apptainer-images/distributions/al9/spec.sin new file mode 100644 index 00000000..f7d75ad6 --- /dev/null +++ b/apptainer-images/distributions/al9/spec.sin @@ -0,0 +1,87 @@ +BootStrap: docker +From: gitlab-registry.cern.ch/linuxsupport/alma9-base:latest + +%labels + maintainer "CMS Data Preservation and Open Access Group " + org.label-schema.build-date $BUILD_DATE + org.label-schema.name "AlmaLinux 9 CMS base OS" + org.label-schema.description "AlmaLinux 9 OS capable of using/running CMS software release(s)." + org.label-schema.url "http://cms-sw.github.io/" + org.label-schema.vcs-ref $VCS_REF + org.label-schema.vcs-url $VCS_URL + org.label-schema.vendor "CERN" + org.label-schema.version $VERSION + org.label-schema.schema-version "1.0" + + +%files + ../../dotfiles/dot-pythonrc.py /etc/pythonrc.py + ../../dotfiles/dot-bashrc /etc/profile.d/bashrc.sh + ../../entrypoints/entrypoint-nd.sh /opt/cms/entrypoint.sh + cvmfsexec-mnt /opt/cms/cvmfsexec-mnt + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat9/cctools/current/bin/work_queue_worker /usr/bin/work_queue_worker + + # parrot from redhat7, it is ok because tht binary is statically linked. + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/lobster-171-cd5e3e2c-cvmfs-70dfa0d6/bin/parrot_cvmfs_static_run /usr/bin/parrot_run + + +%environment + export LC_ALL=en_US.UTF-8 + export LANG=en_US.UTF-8 + export USER=cmsusr + export HOME=/home/cmsusr + +%post + dnf install -y bash && \ + dnf install -y automake bzip2 bzip2-libs bzip2-devel coreutils-single cmake3 e2fsprogs \ + e2fsprogs-libs perl file file-libs fontconfig freetype gcc-c++ git \ + glibc glibc-common glibc-langpack-en glibc-locale-source krb5-libs \ + libaio libcom_err libcom_err-devel libgomp libICE \ + libSM libX11 libX11-devel libxcrypt libXcursor libXext \ + libXext-devel libXft libXft-devel libXi libXinerama \ + libXmu libXpm libXpm-devel libXrandr libXrender \ + libglvnd-opengl libtirpc mesa-libGL mesa-libGLU mesa-libGLU-devel \ + java-1.8.0-openjdk-devel libtool m4 make \ + ncurses ncurses-libs ncurses-devel nspr nss nss-devel nss-util \ + openssl openssl-devel openssl-libs libtirpc-devel pcre pcre-devel \ + patch popt popt-devel python3 python3-pip ninja-build readline readline-devel rpm-build \ + rsync tcl tcsh time tk wget which zlib zsh tcl-devel tk-devel krb5-devel \ + bc strace tar zip unzip hostname nano libnsl procps-ng environment-modules yum-utils && \ + dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \ + dnf install -y voms-clients-cpp krb5-workstation python3-psutil myproxy apptainer \ + python3-requests sudo && \ + dnf update -y ca-certificates && \ + dnf install -y dnf-plugins-core yum-plugin-versionlock && \ + ([ "@EXTRA_PACKAGES@" != "" ] && dnf -y install @EXTRA_PACKAGES@ || true) && \ + yum clean all + + curl https://storage-ci.web.cern.ch/storage-ci/storageci.key | gpg --import && \ + yum-config-manager --add-repo "https://storage-ci.web.cern.ch/storage-ci/eos/diopside-depend/el-9/x86_64/" && \ + yum-config-manager --add-repo "https://storage-ci.web.cern.ch/storage-ci/eos/diopside/tag/testing/el-9/x86_64/" && \ + dnf update -y && \ + dnf install --nogpgcheck -y eos-client eos-fusex && \ + yum clean all + + echo "LC_ALL=en_US.UTF-8" >> /etc/environment && \ + echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \ + echo "LANG=en_US.UTF-8" > /etc/locale.conf && \ + localedef -c -i en_US -f UTF-8 en_US.UTF-8 + + groupadd -g 1000 cmsusr && adduser -u 1000 -g 1000 -G root cmsusr && \ + echo "cmsusr ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers && \ + groupadd -g 1001 cmsinst && adduser -u 1001 -g 1001 cmsinst && \ + install -d /opt && install -d -o cmsinst /opt/cms + + mkdir -p /cvmfs /afs /eos /pool /code /tmp && \ + chmod 1777 /afs /eos /pool /code /tmp && \ + chown -R cmsusr:cmsusr /code + + chmod 755 /opt/cms/entrypoint.sh && \ + chown -R cmsusr /home/cmsusr && \ + chmod 755 /home/cmsusr + +%runscript + exec /opt/cms/entrypoint.sh "$@" + +%startscript + exec /opt/cms/entrypoint.sh "$@" diff --git a/apptainer-images/distributions/cc7/spec.sin b/apptainer-images/distributions/cc7/spec.sin new file mode 100644 index 00000000..a0b2a0dd --- /dev/null +++ b/apptainer-images/distributions/cc7/spec.sin @@ -0,0 +1,94 @@ +Bootstrap: docker +From: gitlab-registry.cern.ch/linuxsupport/cc7-base:latest + +#From: gitlab-registry.cern.ch/linuxsupport/cc7-base:20220601-1.x86_64 + +# https://gitlab.cern.ch/cms-cloud/cmssw-docker/-/tree/master/cc7-cms?ref_type=heads + +%labels + maintainer "CMS Data Preservation and Open Access Group " + org.label-schema.build-date $BUILD_DATE + org.label-schema.name "CC7 CMS base OS" + org.label-schema.description "CC7 OS capable of using/running CMS software release(s)." + org.label-schema.url "http://cms-sw.github.io/" + org.label-schema.vcs-ref $VCS_REF + org.label-schema.vcs-url $VCS_URL + org.label-schema.vendor "CERN" + org.label-schema.version $VERSION + org.label-schema.schema-version "1.0" + +%post + mkdir -p /home/cmsusr + yum-config-manager --save --setopt=epel.skip_if_unavailable=true + + + yum install -y libX11-devel libXext-devel mesa-libGLU-devel \ + mesa-libGL-devel libSM libXft libXext \ + pciutils glx-utils mesa-dri-drivers libX11 libXi libXrender \ + tcsh zsh tcl tk e2fsprogs perl-ExtUtils-Embed compat-libstdc++-33 libXmu e2fsprogs-libs libXpm bc libaio \ + tar patch krb5-devel perl-Data-Dumper gcc-4.9 libstdc++6 unzip zip perl-libwww-perl libXpm-devel libXft-devel svn cvs \ + gcc-c++ gdb strace cern-wrappers krb5-workstation wget hostname readline-devel nano bzip2 perl-Switch perl-Storable \ + perl-Env packages perl-Thread-Queue CERN-CA-certs tk-devel tcl-devel which python-pip perl voms-clients-cpp \ + java-1.8.0-openjdk java-1.8.0-openjdk-devel popt libXcursor libXrandr libXinerama nspr nss nss-util file file-libs \ + readline bzip2-libs python-requests-kerberos libgfortran time python2-psutil python3 \ + HEP_OSlibs_CC7 git \ + yum-plugin-ovl openssl \ + glibc-devel.i686 glibc-devel \ + glibc-headers \ + sudo nano && \ + yum clean -y all + + wget http://repository.egi.eu/sw/production/cas/1/current/repo-files/egi-trustanchors.repo && \ + mv egi-trustanchors.repo /etc/yum.repos.d/ && \ + wget http://repository.egi.eu/sw/production/cas/1/gpg-key-eugridpma-rpm-3 && \ + mv gpg-key-eugridpma-rpm-3 /etc/pki/rpm-gpg/ && \ + wget http://linuxsoft.cern.ch/wlcg/wlcg-centos7.repo && \ + mv wlcg-centos7.repo /etc/yum.repos.d/ && \ + wget http://linuxsoft.cern.ch/wlcg/rpm-gpg-key-wlcg && \ + mv rpm-gpg-key-wlcg /etc/pki/rpm-gpg/ && \ + yum install -y ca-policy-egi-core wlcg-repo.noarch wlcg-voms-cms && \ + yum clean -y all + + yum install -y eos-client && yum clean -y all + + groupadd -g 1000 cmsusr && useradd -u 1000 -g 1000 -G root cmsusr && \ + echo "cmsusr ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers && \ + groupadd -g 1001 cmsinst && useradd -u 1001 -g 1001 cmsinst && \ + mkdir -p /opt && mkdir -p /opt/cms && chown cmsinst /opt/cms + + mkdir -p /cms /cvmfs /afs /eos /pool /code /tmp /var/tmp && \ + chmod 1777 /cms /cvmfs /afs /eos /pool /code /tmp /var/tmp && \ + chown -R cmsusr:cmsusr /code + +%environment + export USER=cmsusr + export HOME=/home/cmsusr + +%files + ../../entrypoints/entrypoint-nd-rh7.sh /opt/cms/entrypoint.sh + + ../../dotfiles/dot-pythonrc.py /etc/pythonrc.py + ../../dotfiles/dot-bashrc /etc/profile.d/bashrc.sh + + cvmfsexec-mnt /opt/cms/cvmfsexec-mnt + + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/7.12.0/bin/work_queue_worker /usr/bin/work_queue_worker + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/7.12.0/bin/work_queue_worker /usr/bin/vine_worker + + # /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/7.3.3/bin/work_queue_worker /usr/bin/work_queue_worker + # /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/7.1.9/bin/work_queue_worker /usr/bin/work_queue_worker + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/7.1.0/bin/work_queue_worker /usr/bin/work_queue_worker_old + /afs/crc.nd.edu/group/ccl/software/x86_64/redhat7/cctools/lobster-171-cd5e3e2c-cvmfs-70dfa0d6/bin/parrot_cvmfs_static_run /usr/bin/parrot_run + + eos.repo /etc/yum.repos.d/eos.repo + +%runscript + exec /opt/cms/entrypoint.sh "$@" + +%startscript + exec /opt/cms/entrypoint.sh "$@" + +%post + chmod 755 /opt/cms/entrypoint.sh + chown -R cmsusr /home/cmsusr + chmod 755 /home/cmsusr diff --git a/apptainer-images/dotfiles/dot-bashrc b/apptainer-images/dotfiles/dot-bashrc new file mode 100644 index 00000000..1f747eed --- /dev/null +++ b/apptainer-images/dotfiles/dot-bashrc @@ -0,0 +1,7 @@ +## .bashrc/.zshrc +export PYTHONSTARTUP=/etc/pythonrc.py +export PS1="(\w) " +export PROMPT='[%*] %B%n@%m %3~%b $ ' +# define aliases +alias cmsenv='eval `scramv1 runtime -sh`' +alias cmsrel='scramv1 project CMSSW' diff --git a/apptainer-images/dotfiles/dot-pythonrc.py b/apptainer-images/dotfiles/dot-pythonrc.py new file mode 100644 index 00000000..082ee7a2 --- /dev/null +++ b/apptainer-images/dotfiles/dot-pythonrc.py @@ -0,0 +1,19 @@ +## +## for tab-completion +## +import rlcompleter, readline +readline.parse_and_bind('tab: complete') +readline.parse_and_bind( 'set show-all-if-ambiguous On' ) + +## +## for history +## +import os, atexit +histfile = os.path.join(os.environ["HOME"], ".python_history") +try: + readline.read_history_file(histfile) +except IOError: + pass +atexit.register(readline.write_history_file, histfile) +del os, atexit, histfile +del readline diff --git a/apptainer-images/entrypoints/entrypoint-cms.sh b/apptainer-images/entrypoints/entrypoint-cms.sh new file mode 100644 index 00000000..e9e15241 --- /dev/null +++ b/apptainer-images/entrypoints/entrypoint-cms.sh @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +echo "::: Setting up CMS environment\ + (works only if /cvmfs is mounted on host) ..." +if [ -f /cvmfs/cms.cern.ch/cmsset_default.sh ]; then + source /cvmfs/cms.cern.ch/cmsset_default.sh + echo "::: Setting up CMS environment... [done]" +else + echo "::: Could not set up CMS environment... [ERROR]" + echo "::: /cvmfs/cms.cern.ch/cmsset_default.sh not found/available" +fi + +exec "$@" diff --git a/apptainer-images/entrypoints/entrypoint-nd-rh7.sh b/apptainer-images/entrypoints/entrypoint-nd-rh7.sh new file mode 100755 index 00000000..54318eb4 --- /dev/null +++ b/apptainer-images/entrypoints/entrypoint-nd-rh7.sh @@ -0,0 +1,67 @@ +#! /bin/bash + +trap clean_cvmfsexec EXIT + +function clean_cvmfsexec () +{ + if [[ -z "${NDCMS_ENTRYPOINT_RECURSIVE}" && -n "${CVMFS_TMP_EXEC_DIR}" ]] + then + if [[ -e "${CVMFS_TMP_EXEC_DIR}/mnt/afs/crc.nd.edu" ]] + then + # safety check, do not remove if afs is still mounted! + return + else + rm -rf "${CVMFS_TMP_EXEC_DIR}" + fi + fi +} + + +if [[ -z "${NDCMS_ENTRYPOINT_RECURSIVE}" ]] +then + echo "::: Setting up CMS environment\ + (works only if /cvmfs is mounted on host) ..." +fi + + +SETUP=/cvmfs/cms.cern.ch/cmsset_default.sh +if [ -f "${SETUP}" ] +then + source "${SETUP}" + source /cvmfs/oasis.opensciencegrid.org/osg-software/osg-wn-client/3.6/current/el7-x86_64/setup.sh + echo "::: Setting up CMS environment... [done]" + + if [[ -n "${CVMFS_TMP_EXEC_DIR}" ]] + then + touch ${CVMFS_TMP_EXEC_DIR}/success + fi + exec "$@" +elif [ -n "${NDCMS_ENTRYPOINT_RECURSIVE}" ]; +then + echo "::: /cvmfs/cms.cern.ch/cmsset_default.sh not found/available" + echo "::: Could not set up CMS environment... [ERROR]" +else + REPOSITORIES="cms.cern.ch oasis.opensciencegrid.org" + if [ -n "${CVMFS_REPOSITORIES}" ]; + then + REPOSITORIES="${REPOSITORIES} $(echo ${CVMFS_REPOSITORIES} | sed 's/,/ /')" + fi + + export NDCMS_ENTRYPOINT_RECURSIVE=yes + export CVMFS_TMP_EXEC_DIR=$(mktemp -d -t cvmfsexec-XXXXXX) + + cp /opt/cms/cvmfsexec-mnt ${CVMFS_TMP_EXEC_DIR} + ${CVMFS_TMP_EXEC_DIR}/cvmfsexec-mnt ${REPOSITORIES} -- "${BASH_SOURCE[0]}" "$@" + status=$? + + # if exec failed, unset so that the cleanup knows the temp dir is not needed anymore + unset NDCMS_ENTRYPOINT_RECURSIVE + + # fall back only if cvmfsexec failed to set env in recursive call + if [[ -f ${CVMFS_TMP_EXEC_DIR}/success ]] + then + exit ${status} + else + exec "$@" + fi +fi diff --git a/apptainer-images/entrypoints/entrypoint-nd.sh b/apptainer-images/entrypoints/entrypoint-nd.sh new file mode 100755 index 00000000..9ae18768 --- /dev/null +++ b/apptainer-images/entrypoints/entrypoint-nd.sh @@ -0,0 +1,68 @@ +#! /bin/bash + +trap clean_cvmfsexec EXIT + +function clean_cvmfsexec () +{ + if [[ -z "${NDCMS_ENTRYPOINT_RECURSIVE}" && -n "${CVMFS_TMP_EXEC_DIR}" ]] + then + if [[ -e "${CVMFS_TMP_EXEC_DIR}/mnt/afs/crc.nd.edu" ]] + then + # safety check, do not remove if afs is still mounted! + return + else + rm -rf "${CVMFS_TMP_EXEC_DIR}" + fi + fi +} + + +if [[ -z "${NDCMS_ENTRYPOINT_RECURSIVE}" ]] +then + echo "::: Setting up CMS environment\ + (works only if /cvmfs is mounted on host) ..." +fi + + +SETUP=/cvmfs/cms.cern.ch/cmsset_default.sh +if [ -f "${SETUP}" ] +then + version=$(cat /etc/redhat-release | grep -o "[0-9]"|head -1) + source "${SETUP}" + source /cvmfs/oasis.opensciencegrid.org/osg-software/osg-wn-client/23/current/el${version}-x86_64/setup.sh + echo "::: Setting up CMS environment... [done]" + + if [[ -n "${CVMFS_TMP_EXEC_DIR}" ]] + then + touch ${CVMFS_TMP_EXEC_DIR}/success + fi + exec "$@" +elif [ -n "${NDCMS_ENTRYPOINT_RECURSIVE}" ]; +then + echo "::: /cvmfs/cms.cern.ch/cmsset_default.sh not found/available" + echo "::: Could not set up CMS environment... [ERROR]" +else + REPOSITORIES="cms.cern.ch oasis.opensciencegrid.org" + if [ -n "${CVMFS_REPOSITORIES}" ]; + then + REPOSITORIES="${REPOSITORIES} $(echo ${CVMFS_REPOSITORIES} | sed 's/,/ /')" + fi + + export NDCMS_ENTRYPOINT_RECURSIVE=yes + export CVMFS_TMP_EXEC_DIR=$(mktemp -d -t cvmfsexec-XXXXXX) + + cp /opt/cms/cvmfsexec-mnt ${CVMFS_TMP_EXEC_DIR} + ${CVMFS_TMP_EXEC_DIR}/cvmfsexec-mnt ${REPOSITORIES} -- "${BASH_SOURCE[0]}" "$@" + status=$? + + # if exec failed, unset so that the cleanup knows the temp dir is not needed anymore + unset NDCMS_ENTRYPOINT_RECURSIVE + + # fall back only if cvmfsexec failed to set env in recursive call + if [[ -f ${CVMFS_TMP_EXEC_DIR}/success ]] + then + exit ${status} + else + exec "$@" + fi +fi diff --git a/apptainer-images/extra/cvmfsexec/cvmfsexec b/apptainer-images/extra/cvmfsexec/cvmfsexec new file mode 100755 index 00000000..4f243306 --- /dev/null +++ b/apptainer-images/extra/cvmfsexec/cvmfsexec @@ -0,0 +1,434 @@ +#!/bin/bash +# Mount cvmfs repository in a user namespace and change to that space. +# Requires being able to run unshare -rm and either fusermount or +# user namespace fuse mounts (kernel >= 4.18). +# Additional repositories can be mounted from within the command with +# $CVMFSMOUNT and repositories can be umounted with $CVMFSUMOUNT. +# Written by Dave Dykstra September 2019 + +#set -x +#PS4='c$$+ ' + +VERSION=4.40 + +usage() +{ + echo "Usage: cvmfsexec [-vN] [-m e2fs_image] [repo ...] -- [command]" >&2 + echo " -v: print current version and exit" >&2 + echo " -N: disable the option of nesting [u]mountrepo commands" >&2 + echo " -m: mount given ext2/3/4 filesystem image at /e2fs" >&2 + exit 1 +} + +HERE="$(cd `dirname $0` && pwd)" + +# needed for pivot_root +PATH=$PATH:/usr/sbin + +KERNEL_VERSION="$(uname -r)" +MAJORKERN=${KERNEL_VERSION/.*/} +RESTKERN=${KERNEL_VERSION#$MAJORKERN.} +MINORKERN=${RESTKERN/.*/} +REVKERN=${KERNEL_VERSION#*-} +REVKERN=${REVKERN/.*/} +USERFUSE=false +if [ "$MAJORKERN" -gt 4 ] || [ "$MAJORKERN" -eq 4 -a "$MINORKERN" -ge 18 ]; then + USERFUSE=true +elif [ "$MAJORKERN" -eq 3 -a "$MINORKERN" -eq 10 -a "$REVKERN" -ge 1127 ]; then + # RHEL 7.8+ kernel + USERFUSE=true +fi + +TMPDIR=$(mktemp -d) +trap "rm -rf $TMPDIR" 0 # note that trap does not carry past exec +CMDFIFO1=$TMPDIR/cmd1 +WAITFIFO1=$TMPDIR/wait1 +CMDFIFO2=$TMPDIR/cmd2 +WAITFIFO2=$TMPDIR/wait2 +FUNCS=$TMPDIR/funcs + +# create the fifos used for interprocess communication +mkfifo $CMDFIFO1 $WAITFIFO1 $CMDFIFO2 $WAITFIFO2 +export CVMFSEXEC_WAITFIFO=$WAITFIFO1 + +# bash syntax {NAME}<&N doesn't work on older bashes such as the +# version 3.2.x on macOS Big Sur, and in fact it fails with an error +# message but not an error code, so test for it first to be able to +# gracefully die + +if [ -n "$({TESTX}<&0 2>&1)" ]; then + echo "Cannot assign file descriptors to variables, bash version too old" >&2 + exit 1 +fi + +# make a copy of stdin fd, for use in '&' and by unshare +exec {STDINCOPYFD}<&0 + +# set up standard certificate & token paths if not already set, +# because the authorization helper runs as a fake root user. +if [ -z "$X509_USER_PROXY" ]; then + export X509_USER_PROXY="/tmp/x509up_u$UID" +fi +if [ -z "$BEARER_TOKEN_FILE" ]; then + export BEARER_TOKEN_FILE="${XDG_RUNTIME_DIR:-/tmp}/bt_u$UID" +fi + +ORIGPWD=$PWD + +NONESTING=false +E2FSIMAGE="" +# can't use OPTIND because it can't distinguish between -- there or missing +NOPTS=0 +while getopts "vNm:" OPTION; do + let NOPTS+=1 + case $OPTION in + v) echo "$VERSION" + exit + ;; + N) NONESTING=true + ;; + m) E2FSIMAGE=$OPTARG + shift + ;; + \?) usage + ;; + esac +done +shift $NOPTS + +REPOS="" +for ARG; do + if [ "$ARG" == "--" ]; then + break + fi + if [[ " $REPOS " != *" $ARG "* ]]; then + REPOS="$REPOS $ARG" + fi + shift +done + +if [ "$ARG" != "--" ]; then + usage +fi +shift + +# Add the config repo if not already asked for +if [ -n "`find $HERE/dist/etc/cvmfs/default.d -name "*.conf" 2>/dev/null`" ]; then + CONFIG_REPO="`grep -h '^CVMFS_CONFIG_REPOSITORY=' $HERE/dist/etc/cvmfs/default.d/*.conf 2>/dev/null|tail -1|sed 's/^CVMFS_CONFIG_REPOSITORY=//'`" + if [[ " $REPOS " != *" $CONFIG_REPO "* ]]; then + REPOS="$CONFIG_REPO $REPOS" + fi +fi + +MOUNTED_REPOS="" +# function to process mount/umount commands from child processes +proccmd() +{ + if [ "$1" = "-n" ]; then + NOTIFY=false + shift + else + NOTIFY=true + fi + REPO="$2" + RET=0 + if [ "$1" == MOUNTREPO ]; then + if [[ " $MOUNTED_REPOS " != *" $REPO "* ]]; then + # not already mounted + cd "$HERE" + ./mountrepo "$REPO" + RET=$? + if [ $? == 0 ]; then + if $USERFUSE; then + mkdir -p "/cvmfs/$REPO" + mount --bind "$HERE/dist/cvmfs/$REPO" "/cvmfs/$REPO" + RET=$? + fi + # put new one at the beginning so config repo will + # be unmounted last + MOUNTED_REPOS="$REPO $MOUNTED_REPOS" + fi + cd - >/dev/null + fi + elif [ "$1" == UMOUNTREPO ]; then + if [[ " $MOUNTED_REPOS " == *" $REPO "* ]]; then + # is mounted + if $USERFUSE; then + # first remove the bind mount at /cvmfs + umount "/cvmfs/$REPO" + RET=$? + rmdir "/cvmfs/$REPO" + fi + if [ "$RET" == 0 ]; then + cd "$HERE" + ./umountrepo "$REPO" + RET=$? + MOUNTED_REPOS="`echo " $MOUNTED_REPOS "|sed "s/ $REPO / /"`" + # remove extra blanks + MOUNTED_REPOS="`echo $MOUNTED_REPOS`" + cd - >/dev/null + fi + else + echo "$REPO not mounted" >&2 + RET=1 + fi + elif [ "$1" == UMOUNTALL ]; then + for R in $MOUNTED_REPOS; do + proccmd -n UMOUNTREPO $R >/dev/null + done + else + echo "Unrecognized command $1" >&2 + RET=1 + fi + if $NOTIFY; then + echo "$RET" >$CVMFSEXEC_WAITFIFO + fi +} +# this function is needed from within unshare so write it to a file +declare -f proccmd >$FUNCS + +if $USERFUSE; then + if mount|egrep -q " /proc/(sys |driver)"; then + # inside of docker or kubernetes with systempaths "masked" + # or using docker --gpus + UNSHAREOPTS="" + else + # use a separate PID namespace to always clean up fuse processes + UNSHAREOPTS="-pf" + fi + # the fakeroot process will process mount requests + CMDFIFO2=$CMDFIFO1 +else + # mount the repositories as the unprivileged user in the + # shared system namespace. + + # mount the repos, keeping track of them for cleanup + cd $HERE + for REPO in $REPOS; do + if ./mountrepo $REPO; then + # put new one at the beginning so config repo will + # be unmounted last + MOUNTED_REPOS="$REPO $MOUNTED_REPOS" + else + RET="$?" + for REPO in $MOUNTED_REPOS; do + ./umountrepo $REPO + done + exit $RET + fi + done + cd $ORIGPWD + + UNSHAREOPTS="--propagation unchanged" + + ( + # This is the background process for accepting mount/umount commands + # from children and for cleaning up on exit + trap "" 1 2 3 15 # ignore ordinary signals + # read from user namespace process and write to fakeroot process + while read -u $CMDINFD CMD PARAM; do + proccmd "$CMD" "$PARAM" + done <&$STDINCOPYFD {STDINCOPYFD}<&- {CMDINFD}<$CMDFIFO1 + # do unmounts and cleanup after user command exits + cd $HERE + for REPO in $MOUNTED_REPOS; do + if [ "$REPO" == "$CONFIG_REPO" ]; then + # give a little extra time for the others to exit + sleep 1 + fi + ./umountrepo $REPO >/dev/null + done + rm -rf $TMPDIR + ) & +fi + +rm -rf "$HERE/mnt" + +export STDINCOPYFD + +# Note that within the here document, unprotected $ substitutions are +# done by the surrounding shell, and \$ is within the unshare shell +unshare -rm $UNSHAREOPTS /bin/bash /dev/stdin "${@:-$SHELL}" </proc/"\$PARAM"/gid_map + echo "\$(awk '{print \$2; exit}' /proc/self/uid_map) 0 1" >/proc/"\$PARAM"/uid_map + echo "ready" >$WAITFIFO2 + elif $USERFUSE; then + proccmd "\$CMD" "\$PARAM" + fi + done <&$STDINCOPYFD {CMDINFD}<$CMDFIFO2 + # shutting down + if [ -n "$E2FSIMAGE" ] || ($USERFUSE && [ -z "$UNSHAREOPTS" ]); then + # automatic cleanup doesn't happen in these cases, so unmount all + proccmd -n UMOUNTALL + if [ -n "$E2FSIMAGE" ]; then + # wait a little for fuse processes to exit + umountwait() { + N=0 + while [ "\$N" -lt 20 ]; do + if [ -z "\$(ps -e|grep \$1)" ]; then + break + fi + sleep 0.1 + let N+=1 + done + } + umountwait cvmfs2 + umount /e2fs + umountwait fuse2fs + fi + fi + ) & + + # Change to the new root. Would use chroot but it doesn't work. + cd $HERE/mnt + mkdir -p .old-root + pivot_root . .old-root + cd $ORIGPWD + + if $USERFUSE; then + if [ -n "$UNSHAREOPTS" ]; then + # mount a new /proc for the new pid space + mount -t proc proc /proc + RET="\$?" + if [ "\$RET" != 0 ]; then + echo "mount /proc failed" + exit \$RET + fi + fi + + cd $HERE + + if [ "$E2FSIMAGE" != "" ]; then + echo "Mounting $E2FSIMAGE at /e2fs" + mkdir -p /e2fs + PATH=$PATH:$HERE/dist/usr/sbin LD_LIBRARY_PATH=$HERE/dist/usr/lib64:$HERE/dist/usr/lib fuse2fs $E2FSIMAGE /e2fs + fi + + # mount the initial repos + for REPO in $REPOS; do + ./mountrepo \$REPO + RET="\$?" + if [ "\$RET" != 0 ]; then + echo "mountrepo \$REPO failed" + exit \$RET + fi + + mkdir -p /cvmfs/\$REPO + mount --bind $HERE/dist/cvmfs/\$REPO /cvmfs/\$REPO + done + cd - >/dev/null + + else + # map cvmfs repos mounted by parent + mount --rbind $HERE/dist/cvmfs /cvmfs + fi + + export CVMFSEXEC_CMDFD + exec {CVMFSEXEC_CMDFD}>$CMDFIFO1 + + if [ -z "$UNSHAREOPTS" ] || [ $USERFUSE = false ]; then + # running without new process namespace, so more cleanup + # needs to be done and our exit needs to wait for the + # background process to finish cleaning up + EXEC="" + elif [ -n "$E2FSIMAGE" ]; then + # fuse2fs complains if this doesn't get unmounted cleanly + EXEC="" + else + EXEC=exec + fi + + # quoting the here document's delimeter makes this nested shell not + # interpret $ substitutions, but the previous one still does + \$EXEC unshare -U /bin/bash /dev/stdin "\${@:-$SHELL}" <<'!EOF-2!' + #set -x + #PS4='c\$$+ ' + # now in the user namespace + + if [ "$CMDFIFO1" == "$CMDFIFO2" ]; then + echo "PID \$$" >&\$CVMFSEXEC_CMDFD + else + # open & close the command fifo, so background process will exit + echo "PID \$$" >$CMDFIFO2 + fi + read X <$WAITFIFO2 + + if $NONESTING; then + exec {CVMFSEXEC_CMDFD}>&- + unset CVMFSEXEC_CMDFD + unset CVMFSEXEC_WAITFIFO + else + export CVMFSMOUNT="$HERE/mountrepo" + export CVMFSUMOUNT="$HERE/umountrepo" + fi + + if [ \$$ -eq 1 ]; then + # Leave this bash running as PID 1, because most other + # programs won't handle signals & child reaping correctly, + # and also for the cleanup trap because all other + # processes in the namespaces will get a SIGKILL when + # PID 1 exits. + EXEC="" + trap "rm -rf $TMPDIR" 0 + trap "" 1 2 3 15 # ignore all ordinary signals + else + EXEC=exec + fi + export -n STDINCOPYFD + \$EXEC "\$@" <&\$STDINCOPYFD {STDINCOPYFD}<&- +!EOF-2! + + RET="\$?" + exec {CVMFSEXEC_CMDFD}>&- + # wait for background process to clean up + wait + exit \$RET + +!EOF-1! + +RET="$?" +# wait for background process to clean up +wait +exit $RET diff --git a/apptainer-images/extra/cvmfsexec/makedist b/apptainer-images/extra/cvmfsexec/makedist new file mode 100755 index 00000000..199829d3 --- /dev/null +++ b/apptainer-images/extra/cvmfsexec/makedist @@ -0,0 +1,422 @@ +#!/bin/bash +# +# Create the cvmfs dist directory, downloading the latest rpms from +# the three major sources. +# Written by Dave Dykstra 17 April 2019 +# + +SUPPORTEDTYPES="rhel7-x86_64 rhel8-aarch64 rhel8-x86_64 rhel8-ppc64le rhel9-x86_64 suse15-x86_64" + +usage() +{ + ( + echo "Usage: makedist [-s] [ -m machinetype ] {osg|egi|default|none}" + echo " makedist [-s] -o " + echo " The first usage creates a distribution in 'dist' directory." + echo " The -m option selects machinetype for the distribution." + echo " The default is the current machine. Supported types:" + echo " $SUPPORTEDTYPES" + echo " The second usage puts 'dist' and cvmfsexec tools into one script with the" + echo " given file name that self extracts and executes cvmfsexec." + echo " After extraction, files are left behind under '.cvmfsexec' in the same" + echo " directory as the script." + echo " The -s option makes both operations work for singcvmfs instead" + echo " of cvmfsexec, and files are left in .singcvmfs" + ) >&2 + exit 2 +} + +SING=false +MACHTYPE="" +while true; do + if [ "$1" = "-s" ]; then + SING=true + shift + elif [ "$1" = "-m" ]; then + MACHTYPE="$2" + shift 2 + else + break + fi +done + +distroname() { + if [ -n "$MACHTYPE" ]; then + echo "$MACHTYPE"|sed 's/[0-9].*//' + elif [ -f /etc/os-release ]; then + source /etc/os-release + case " $ID $ID_LIKE " in + *" rhel "*) echo rhel;; + *" suse "*) echo suse;; + *) echo "Operating system in /etc/os-release not supported" >&2 + exit 2;; + esac + elif [ -f /etc/redhat-release ]; then + echo "rhel" + fi +} + +distroversion() { + if [ -n "$MACHTYPE" ]; then + echo "$MACHTYPE"|sed 's/^[^0-9]*\([0-9]*\)-.*/\1/' + elif [ -f /etc/os-release ]; then + source /etc/os-release + echo "${VERSION_ID/.*/}" + elif [ -f /etc/redhat-release ]; then + read LINE &2 + echo "Supported types are:" >&2 + echo " $SUPPORTEDTYPES" >&2 + exit 1 +fi + +EL=$VERS +MACH=el$EL +if [ "$DISTRO" = "suse" ]; then + EL=7 # we get some suse stuff from rhel7 + MACH=sle +fi + +HERE="$(cd `dirname $0` && pwd)" + +DIST="$HERE/dist" + +DISTTYPE="" +INCLUDEHELPER=true +case $1 in + -o) + if [ $# != 2 ]; then + usage + fi + BASENAME=cvmfsexec + TOOLS="cvmfsexec mountrepo umountrepo" + SEDOPTS="" + REQUIRES="makedist (without -s)" + if $SING; then + BASENAME=singcvmfs + TOOLS="singcvmfs cvmfs2-wrapper" + SEDOPTS="-e s/cvmfsexec/$BASENAME/" + REQUIRES="makedist -s" + fi + # For now (as of 6-21-23), only require $DIST/.cvmfsexecdist for -s + # mode, so those not using -s don't have to re-make their distribution. + # Eventually this can be changed to always require that file instead + # of $DISTLIB. + HASSING=false + if [ -f $DIST/.cvmfsexecdist ]; then + read X DISTTYPE Y <$DIST/.cvmfsexecdist + if [ "$DISTTYPE" = "sing" ]; then + HASSING=true + fi + fi + DISTLIB=libcvmfs_fuse.so + if ([ ! -f $DIST/usr/lib*/$DISTLIB ] && [ ! -f $DIST/lib*/$DISTLIB ]) \ + || [ $SING != $HASSING ]; then + echo "Must be run from where cvmfs distribution was made by $REQUIRES" >&2 + exit 1 + fi + sed -e 's/^[ \t]*//' $SEDOPTS >$2 <<'!EOF!' + #!/bin/bash + BASEDIR="$(cd `dirname $0` && pwd)" + BASE="$BASEDIR/.cvmfsexec" + if [ $0 -nt $BASE ]; then + rm -rf $BASE + mkdir $BASE + TAR_START="`awk '/^__TAR_BELOW__/ {print NR + 1; exit 0; }' $0`" + tail -n+$TAR_START $0 | tar -xzf - -C $BASE + fi + exec $BASE/cvmfsexec "$@" + __TAR_BELOW__ +!EOF! + tar --exclude 'dist/var/run/cvmfs/*' --exclude 'dist/var/lib/cvmfs/*' -czvf - -C $HERE $TOOLS dist >>"$2" + chmod +x "$2" + exit + ;; + osg) + if [ $EL -lt 8 ]; then + REL=3.6 + else + REL=23-main + fi + REPO=release + BASEURL="https://repo.opensciencegrid.org/osg/$REL/el$EL/$REPO/x86_64";; + egi) + OS=centos$EL + BASEURL="https://repository.egi.eu/sw/production/umd/4/$OS/x86_64/updates";; + default|none) + INCLUDEHELPER=false + BASEURL="https://cvmrepo.web.cern.ch/cvmrepo/yum/cvmfs/EL/$EL/x86_64";; + *) usage;; +esac +DISTTYPE=$1 + +if [ -d $DIST ]; then + echo "$DIST already exists" >&2 + exit 1 +fi + +SINGMSG="" +if $SING; then + SINGMSG="singcvmfs " +fi +echo "Making $SINGMSG$DISTTYPE distribution for $MACHTYPE" + +getcoprurl() { + typeset TMPF=$(mktemp) + typeset REPONAME=makedist-$1 + cat >$TMPF <&2 + fi + done + rm -f $TMPF +} + +if [ "$ARCH" != "x86_64" ]; then + # There's no cvmfs-x509-helper yet for non-x86 architectures, and + # we're looking at x86_64 version repositories for the config rpm + INCLUDEHELPER=false +fi + +URLS="" +BASELIST="`curl -Ls $BASEURL/|grep "cvmfs-"|sed 's/.*href="//;s/".*//'`" +CVMFSPKG="`echo "$BASELIST"|grep "^cvmfs-[0-9]"|tail -1`" +if [ -z "$CVMFSPKG" ]; then + if [ "$DISTTYPE" = egi ] && [ "$EL" = 8 ]; then + echo "egi's UMD does not yet support rhel8" 2>&1 + else + echo "No cvmfs package found from $BASEURL" >&2 + fi + exit 1 +fi + +CVMFSURL="" +if [ "$ARCH" = "ppc64le" ]; then + # Grab cvmfs package from copr, using yumdownloader to calculate + # the URL + CVMFSRPMURL="$(getcoprurl cvmfs)" + if [ -z "$CVMFSRPMURL" ]; then + echo "Failed to get $ARCH cvmfs rpm from copr" >&2 + exit 1 + fi + CVMFSURL="$(dirname $CVMFSRPMURL)" + CVMFSRPMNAME="$(basename $CVMFSRPMURL)" +else + # Now that we can figure out the latest cvmfs version, download the + # corresponding cvmfs packages from CERN instead + CVMFSVERSION="`echo "$CVMFSPKG"|sed 's/.*cvmfs-\([^-]*\)-.*/\1/'`" + CVMFSURL="https://ecsft.cern.ch/dist/cvmfs/cvmfs-$CVMFSVERSION" + CVMFSRPMNAME="`curl -Ls $CVMFSURL/|grep "cvmfs-$CVMFSVERSION-.*$MACH.*\.$ARCH"|sed 's/.*href="//;s/".*//'|tail -1`" + if [ -z "$CVMFSRPMNAME" ]; then + echo "No matching cvmfs rpm found at $CVMFSURL" >&2 + exit 1 + fi + CVMFSRPMURL="$CVMFSURL/$CVMFSRPMNAME" +fi +URLS="$CVMFSRPMURL" + +MINORVERSION="`echo "$CVMFSVERSION"|cut -d. -f2`" +if [ "$MINORVERSION" -ge 10 ]; then + # include cvmfs-libs + URLS="$URLS $CVMFSURL/`echo $CVMFSRPMNAME|sed 's/cvmfs-/cvmfs-libs-/'`" +fi + +if [ "$DISTTYPE" != "none" ]; then + CONFIGREPO="`echo "$BASELIST"|grep "^cvmfs-config-$DISTTYPE-[0-9]"|tail -1`" + if [ -z "$CONFIGREPO" ]; then + echo "cvmfs-config not found at $BASEURL!" >&2 + exit 1 + fi + URLS="$URLS $BASEURL/$CONFIGREPO" +fi + +# return the url of the latest version of a package given +# $1 - base repository url +# $2 - package name +# $3 - true if base url has one-character subdirectories, otherwise false +latesturl() +{ + typeset URL="$1" + if [ "$3" = true ]; then + URL="${URL%/}" + URL="$URL/${2:0:1}" + fi + typeset PKG="$(curl -Ls "$URL"|grep ${2}-[0-9].*$ARCH|grep -v 32bit|grep -v mirrorlist|tail -1|sed 's/.*href="//;s/".*//')" + if [ -n "$PKG" ]; then + echo "$URL/$PKG" + fi +} + +if $INCLUDEHELPER; then + HELPER="`echo "$BASELIST"|grep "^cvmfs-x509-helper-[0-9]"|tail -1`" + if [ -z "$HELPER" ]; then + echo "cvmfs-x509-helper not found at $BASEURL!" >&2 + exit 1 + fi + URLS="$URLS $BASEURL/$HELPER" + if [ "$EL" -lt 8 ]; then + EPELURL="https://download.fedoraproject.org/pub/epel/$EL/$ARCH/Packages" + else + EPELURL="https://download.fedoraproject.org/pub/epel/$EL/Everything/$ARCH/Packages" + fi + URL="`latesturl $EPELURL scitokens-cpp true`" + if [ -z "$URL" ]; then + echo "No scitokens-cpp package found from $EPELURL" >&2 + exit 1 + fi + URLS="$URLS $URL" +fi + +FUSESUBDIR=false +CVMFSFUSE3URL="$CVMFSURL/`echo $CVMFSRPMNAME|sed 's/cvmfs-/cvmfs-fuse3-/'`" +if $SING; then + URLS="$URLS $CVMFSFUSE3URL" + if [ "$EL" -lt 8 ]; then + FUSEURL="https://download.fedoraproject.org/pub/epel/$EL/$ARCH/Packages" + FUSESUBDIR=true + elif [ "$EL" -eq 8 ]; then + FUSEURL="https://repo.almalinux.org/almalinux/$EL/BaseOS/$ARCH/os/Packages" + else + FUSEURL="https://repo.almalinux.org/almalinux/$EL/AppStream/$ARCH/os/Packages" + fi + URL="`latesturl $FUSEURL fuse3-libs $FUSESUBDIR`" + if [ -z "$URL" ]; then + echo "No fuse3-libs package found from $FUSEURL" >&2 + exit 1 + fi + URLS="$URLS $URL" +else + if [ "$DISTRO" = suse ]; then + FUSEURL="https://download.opensuse.org/distribution/openSUSE-stable/repo/oss/x86_64" + FUSELIB=libfuse2 + else + if [ "$EL" -lt 8 ]; then + FUSEURL="https://vault.centos.org/centos/$EL/os/$ARCH/Packages/" + elif [ "$EL" -eq 8 ]; then + FUSEURL="https://repo.almalinux.org/almalinux/$EL/BaseOS/$ARCH/os/Packages" + else + FUSEURL="https://repo.almalinux.org/almalinux/$EL/AppStream/$ARCH/os/Packages" + fi + if [ "$EL" -lt 9 ]; then + FUSELIB=fuse-libs + else + FUSELIB=fuse3-libs + URLS="$URLS $CVMFSFUSE3URL" + fi + fi + URL="`latesturl $FUSEURL $FUSELIB $FUSESUBDIR`" + if [ -z "$URL" ]; then + echo "No $FUSELIB package found from $FUSEURL" >&2 + exit 1 + fi + URLS="$URLS $URL" +fi + +if [ "$EL" -eq 7 ]; then + # add fuse2fs only on EL7, it is standard elsewhere + URLS="$URLS $(getcoprurl fuse2fs)" +fi + +mkdir -p $DIST/etc +cd $DIST + +# make an os-release subset for repository configs that need that, +# in particular for osgstorage-auth.conf +cat >etc/os-release <&2 + exit 1 + fi +done +find * -type l|while read LINK; do + LINKDEST="`readlink $LINK`" + if [ "${LINKDEST:0:1}" = "/" ]; then + # turn full path symlink target into relative path + NEWDEST="$(echo $(dirname $LINK)|sed 's,[^/]*,..,g')$LINKDEST" + echo "$LINK -> $NEWDEST" + rm -f $LINK + ln -s $NEWDEST $LINK + fi +done + +echo "./etc/cvmfs/default.local" +(echo 'CVMFS_HTTP_PROXY="auto;DIRECT"' +if [ "$DISTTYPE" = osg ]; then + WLCGPACS="http://cernvm-wpad.fnal.gov/wpad.dat;http://cernvm-wpad.cern.ch/wpad.dat" +else + WLCGPACS="http://cernvm-wpad.cern.ch/wpad.dat;http://cernvm-wpad.fnal.gov/wpad.dat" +fi +echo "CVMFS_PAC_URLS=\"http://grid-wpad/wpad.dat;http://wpad/wpad.dat;$WLCGPACS\"" +) >etc/cvmfs/default.local + +if $INCLUDEHELPER; then + echo "Wrapping authz helper commands" + HERE=$PWD + cd usr/libexec/cvmfs/authz + cat >.wrapper <<'!EOF!' +#!/bin/bash +BASEME=${0##*/} +HERE="${0%/*}" +if [[ "$HERE" != /* ]]; then + HERE="$PWD/$HERE" +fi +PARENT="${HERE%/*}" +GGPARENT="${PARENT%/*/*}" +if [ -n "$LD_LIBRARY_PATH" ]; then + LD_LIBRARY_PATH=":$LD_LIBRARY_PATH" +fi +LD_LIBRARY_PATH=$GGPARENT/lib64:$GGPARENT/lib$LD_LIBRARY_PATH exec -a $0 $HERE/.$BASEME "$@" +!EOF! + chmod +x .wrapper + for CMD in *; do + mv $CMD .$CMD + ln -s .wrapper $CMD + done + cd $HERE +fi + +echo "./.cvmfsexecdist" +( +if $SING; then + echo "disttype: sing" +else + echo "disttype: standard" +fi +echo "machtype: $MACHTYPE" +) >.cvmfsexecdist diff --git a/apptainer-images/extra/cvmfsexec/mountrepo b/apptainer-images/extra/cvmfsexec/mountrepo new file mode 100755 index 00000000..350de695 --- /dev/null +++ b/apptainer-images/extra/cvmfsexec/mountrepo @@ -0,0 +1,158 @@ +#!/bin/bash +# +# Mount a cvmfs repository as an unprivileged user. +# Works with fusermount or with a kernel (>= 4.18) that supports mounting +# fuse repositories in unprivileged "fakeroot" namespaces. +# Mounts in cvmfs subdirectory of the directory where the command is found. +# Written by Dave Dykstra 17 April 2019 +# + +usage() +{ + echo "Usage: mountrepo repo.name" >&2 + exit 1 +} + +if [ $# != 1 ]; then + usage +fi + +REPO="$1" + +if [ -n "$CVMFSEXEC_CMDFD" ] && [ -n "$CVMFSEXEC_WAITFIFO" ]; then + # this is within cvmfsexec, requesting to mount another repo + if [ -z "$CVMFSMOUNT" ]; then + echo "$0: mount within cvmfsexec only works through \$CVMFSMOUNT interface" >&2 + exit 1 + fi + + # Send command to the "parent" process still outside the namespace. + # "Parent" is in quotes because the linux process tree gets reversed and + # it is actually a linux child. It is a parent environment-wise though. + set -e + echo MOUNTREPO $REPO >&$CVMFSEXEC_CMDFD + exec {CVMFSEXEC_CMDFD}>&- # close it, no longer needed + # wait until that process is finished mounting + read RET <$CVMFSEXEC_WAITFIFO + exit $RET +fi + +HERE="$(cd `dirname $0` && pwd)" +DIST="$HERE/dist" + +if [ ! -f $DIST/usr/bin/cvmfs2 ] || [ ! -f $DIST/etc/cvmfs/default.conf ]; then + echo "$DIST should be rpm2cpio of cvmfs rpm" >&2 + exit 1 +fi + +CONFFILE="`mktemp`" +trap "rm -f $CONFFILE" 0 + +# recursively substitute a ". " or "source " with the contents of the +# sourced file. +procsource() +{ + for FILE; do + while read LINE; do + case "$LINE" in + ". "*) eval procsource "${LINE/. /}";; + "source "*) eval procsource "${LINE/source /}";; + *) echo "$LINE";; + esac + done <$FILE + done +} + +# relocate the given files and process them for sourced files the same way +# that cvmfs does +relocate() +{ + cd ${1%/*} + # do /var first in case $DIST begins with /var + procsource $* 2>/dev/null | \ + sed -e "s,^\([^/]*\)/var,\1$DIST/var," \ + -e "s,^\([^/]*\)/etc,\1$DIST/etc," \ + -e "s,^\([^/]*\)/cvmfs$,\1$DIST/cvmfs," \ + -e "s,^\([^/]*\)/cvmfs/,\1$DIST/cvmfs/," + cd - >/dev/null +} + +DOMAIN="`echo "$REPO"|cut -d. -f2-`" +( +DEFD_FILES="`find $DIST/etc/cvmfs/default.d -name "*.conf" 2>/dev/null`" +if [ -n "$DEFD_FILES" ]; then + CONFIG_REPO="`sed -n 's/^CVMFS_CONFIG_REPOSITORY=//p' $DIST/etc/cvmfs/default.d/*.conf`" +fi +# these are used in some configuration repository includes, set them +# for procsource +CVMFS_CONFIG_REPOSITORY=$CONFIG_REPO +CVMFS_MOUNT_DIR=$DIST/cvmfs + +relocate $DIST/etc/cvmfs/default.conf +if [ -n "$DEFD_FILES" ]; then + relocate $DIST/etc/cvmfs/default.d/*.conf +fi +if [ -n "$CONFIG_REPO" -a "$REPO" != "$CONFIG_REPO" ]; then + relocate $DIST/cvmfs/$CONFIG_REPO/etc/cvmfs/default.conf +fi + +DEFLOCAL=$DIST/etc/cvmfs/default.local +relocate $DEFLOCAL +# set some defaults if they weren't already in the dist's default.local +if ! grep -q "^CVMFS_NFILES=" $DEFLOCAL 2>/dev/null; then + echo "CVMFS_NFILES=`ulimit -Hn`" +fi +if ! grep -q "^CVMFS_USYSLOG=" $DEFLOCAL 2>/dev/null; then + mkdir -p $HERE/log + echo "CVMFS_USYSLOG=$HERE/log/$REPO.log" +fi +if ! grep -q "^CVMFS_CACHE_BASE=" $DEFLOCAL 2>/dev/null && [ -d /e2fs ]; then + mkdir -p /e2fs/cvmfscache + echo "CVMFS_CACHE_BASE=/e2fs/cvmfscache" +fi + +if [ -n "$CONFIG_REPO" -a "$REPO" != "$CONFIG_REPO" ]; then + relocate $DIST/cvmfs/$CONFIG_REPO/etc/cvmfs/domain.d/$DOMAIN.conf +fi +relocate $DIST/etc/cvmfs/domain.d/$DOMAIN.conf +relocate $DIST/etc/cvmfs/domain.d/$DOMAIN.local +if [ -n "$CONFIG_REPO" -a "$REPO" != "$CONFIG_REPO" ]; then + REPOCONF=$DIST/cvmfs/$CONFIG_REPO/etc/cvmfs/config.d/$REPO.conf + if [ "$REPO" = oasis.opensciencegrid.org ]; then + # OASIS_CERTIFICATES is one special variable we do not want relocated + relocate $REPOCONF | sed 's,^\(OASIS_CERTIFICATES\)=.[^/]*,\1=/cvmfs,' + else + relocate $REPOCONF + fi +fi +relocate $DIST/etc/cvmfs/config.d/$REPO.conf +relocate $DIST/etc/cvmfs/config.d/$REPO.local + +# this is for finding an authz helper +echo CVMFS_AUTHZ_CVMFS_DIST=$DIST + +) > $CONFFILE + +OPTS="" +if grep -q ^CVMFS_DEBUGLOG=. $CONFFILE; then + OPTS="$OPTS,debug" +fi + +mkdir -p $DIST/var/run/cvmfs +mkdir -p $DIST/cvmfs/$REPO +if [ -f $DIST/usr/lib/libcvmfs_fuse.so ]; then + LPATH=$DIST/usr/lib + LDPATH=$LPATH + if [ ! -f $LPATH/libfuse.so.2 ]; then + # for el6 + LDPATH=$LPATH:$DIST/lib + fi +else + LPATH=$DIST/usr/lib64 + LDPATH=$LPATH + if [ ! -f $LPATH/libfuse.so.2 ]; then + # for el6 + LDPATH=$LPATH:$DIST/lib64 + fi +fi +LD_LIBRARY_PATH=$LDPATH CVMFS_LIBRARY_PATH=$LPATH $DIST/usr/bin/cvmfs2 -o config=$CONFFILE$OPTS $REPO $DIST/cvmfs/$REPO diff --git a/apptainer-images/extra/cvmfsexec/umountrepo b/apptainer-images/extra/cvmfsexec/umountrepo new file mode 100755 index 00000000..a8472bd6 --- /dev/null +++ b/apptainer-images/extra/cvmfsexec/umountrepo @@ -0,0 +1,84 @@ +#!/bin/bash +# +# unmount cvmfs repositories that were mounted with mountrepo +# Written by Dave Dykstra 17 April 2019 +# + + +usage() +{ + echo "Usage: umountrepo [-z] {repo.name|-a}" >&2 + exit 1 +} + +LAZY=false +if [ "$1" = "-z" ]; then + LAZY=true + shift +fi + +if [ $# != 1 ]; then + usage +fi + +if [ -n "$CVMFSEXEC_CMDFD" ] && [ -n "$CVMFSEXEC_WAITFIFO" ]; then + # this is within cvmfsexec, requesting to umount a repo + if [ -z "$CVMFSUMOUNT" ]; then + echo "$0: umount within cvmfsexec only works through \$CVMFSUMOUNT interface" >&2 + exit 1 + fi + if $LAZY; then + echo "$0: lazy umount not supported within cvmfsexec" >&2 + exit 1 + fi + if [ "$1" == "-a" ]; then + echo "$0: umount within cvmfsexec only works with one repo" >&2 + exit 1 + fi + REPO="$1" + + # Send command to the "parent" process still outside the namespace. + # "Parent" is in quotes because the linux process tree gets reversed and + # it is actually a linux child. It is a parent environment-wise though. + echo UMOUNTREPO $REPO >&$CVMFSEXEC_CMDFD + exec {CVMFSEXEC_CMDFD}>&- # close it, no longer needed + # wait until that process is finished mounting + read RET <$CVMFSEXEC_WAITFIFO + exit $RET +fi + + +HERE="$(cd `dirname $0` && pwd)" +DIST="$HERE/dist" + +if [ "$1" = "-a" ]; then + set -- `mount|grep " $DIST"|awk '{print $3}'` +else + set -- $DIST/cvmfs/$1 +fi + +if [ "`id -u`" == 0 ]; then + # most likely this is actually a "fake" root + ISROOT=true +else + ISROOT=false +fi + +for REPO; do + if $LAZY; then + echo "Lazily unmounting $REPO" + if $ISROOT; then + umount -l $REPO + else + fusermount -uz $REPO + fi + else + echo "Unmounting $REPO" + if $ISROOT; then + umount $REPO + else + fusermount -u $REPO + fi + rmdir $REPO + fi +done