Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at devShellTools-shell 223 lines 8.6 kB view raw
1#!/usr/bin/env bash 2 3shopt -s extglob 4 5# Upstream wrapper requires UUID to be used for configuration. 6 7# However, when declaratively describing a host, we may not know its UUID, and 8# shouldn't need to persist something that will differ between hosts built from 9# the same configuration template. 10 11# Thus, for using bees from NixOS, we have our own wrapper, which supports not 12# just UUID but any specification permitted by findmnt 13 14[[ $bees_debug ]] && { PS4=':${BASH_SOURCE##*/}:$LINENO+'; set -x; } 15 16usage() { 17 cat >&2 <<EOF 18Usage: ${BASH_SOURCE##*/} run|cleanup config-name|fsSpec [idxSizeMB=...] [verbosity=...] [workDir=...] [-- daemon-options...] 19 20 fsSpec should be in a format recognized by findmnt. Alternately, 21 "config-name" may refer to a file that exists in ${bees_config_dir:-/etc/bees} 22 with a .conf extension; if that file does not specify UUID, findmnt will be 23 used in addition. 24 25 Note that while config files may presently use shell arithmetic, use of this 26 functionality is not encouraged going forward: Setting ''idxSizeMB=4096'' is 27 preferred over ''DB_SIZE=$((1024*1024*1024*4))'' or ''DB_SIZE=$(( AL16M * 256 ))'', 28 although both of these are presently supported. 29 30 If fsSpec contains a /, it assumed to be a mount point to be looked up by 31 findmnt, not a config file name. 32 33 daemon-options are passed directly through to the daemon on startup, as 34 documented at https://github.com/Zygo/bees/blob/master/docs/options.md. 35EOF 36 exit 1 37} 38 39die() { echo "$*" >&2; exit 1; } 40 41allConfigNames=( blockdev fsSpec home idxSize idxSizeMB mntDir runDir status verbosity workDir ) 42 43# Alternate names for configuration values; "bees_" will always be prepended 44declare -A altConfigNames=( 45 # from original bees wrapper 46 [BEESHOME]=home 47 [BEESSTATUS]=status 48 [MNT_DIR]=mntDir 49 [UUID]=uuid 50 [WORK_DIR]=runDir 51 [DB_SIZE]=idxSize 52) 53 54# legacy bees config files can be arbitrary shell scripts, so we need to actually evaluate them 55sandboxedConfigFileEval() { 56 bash_exe=$(type -P bash) || exit 57 PATH=/var/empty ENV='' BASH_ENV='' AL128K="$((128*1024))" AL16M="$((16*1024*1024))" "$bash_exe" -r ${bees_debug+-x} \ 58 -c 'eval "$(</dev/stdin)" >&2; for var; do [[ ${!var} ]] && printf "%q=%s\\0" "$var" "${!var}"; done' \ 59 "${!altConfigNames[@]}" "${allConfigNames[@]}" \ 60 <"$1" 61} 62 63readConfigFileIfExists() { 64 local line 65 [[ -s $1 ]] || return 1 66 while IFS= read -r -d '' line; do 67 line=${line%%+([[:space:]])"#"*} 68 [[ $line ]] || continue 69 [[ $line = *=* ]] || { 70 printf 'WARNING: Config file line not recognized: %q\n' "$line" >&2 71 continue 72 } 73 set_option "$line" 74 done < <(sandboxedConfigFileEval "$1") 75} 76 77set_option() { 78 local k v 79 k="${1%%=*}" v="${1#*=}" 80 [[ ${altConfigNames[$k]} ]] && k=${altConfigNames[$k]} 81 printf -v "bees_$k" %s "$v" 82} 83 84uuid_re='^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$' 85 86# Shared code for setting configuration used by other operations. 87# 88# Reads from global associative array "opts" containing options passed in as 89# key=value pairs on the command line, looks for config-file overrides, and 90# sets individual global variables. 91_setup() { 92 declare fstype 93 bees_fsSpec=$1; shift 94 95 # Look for file-based configuration, additional to honoring configuration on the command line 96 bees_config_dir="${bees_config_dir:-/etc/bees}" 97 if [[ $bees_fsSpec =~ $uuid_re ]]; then 98 bees_uuid=$bees_fsSpec 99 # If our spec looks like a bare UUID, and no config file exists in the new 100 # format, fall back to legacy config file search mechanism (grep; ewww). 101 if ! readConfigFileIfExists "$bees_config_dir/UUID=$bees_fsSpec.conf"; then 102 # Legacy approach to finding a config file: Grep for a *.conf file 103 # containing the UUID within its text. Permitting spaces around the "=" 104 # appears to be a bug, but is retained for compatibility with the 105 # original upstream script. 106 allConfFiles=( "$bees_config_dir"/*.conf ) 107 if (( ${#allConfFiles[@]} )); then 108 # in read or readarray with -d '', the NUL terminating the empty string is used as delimiter character. 109 readarray -d '' -t matchingConfFiles < <(grep -E -l -Z "^[^#]*UUID[[:space:]]*=[[:space:]]*" "${allConfFiles[@]}") 110 else 111 matchingConfFiles=( ) 112 fi 113 if (( ${#matchingConfFiles[@]} == 1 )); then 114 # Exactly one configuration file exists in our target directory with a reference to the UUID given. 115 bees_config_file=${matchingConfFiles[0]} 116 readConfigFileIfExists "$bees_config_file" 117 echo "NOTE: Please consider renaming $bees_config_file to $bees_config_dir/UUID=$bees_fsSpec" >&2 118 echo " ...and passing UUID=$bees_fsSpec on startup." >&2 119 elif (( ${#matchingConfFiles[@]} > 1 )); then 120 # The legacy wrapper would silently use the first file and ignore 121 # others, but... no. 122 echo "ERROR: Passed a bare UUID, but multiple configuration files match it:" >&2 123 printf ' - %q\n' "${matchingConfFiles[@]}" >&2 124 die "Unable to continue." 125 fi 126 fi 127 else 128 # For a non-UUID fsSpec that is not a path, look only for a config file 129 # exactly matching its text. 130 # 131 # (Passing a mount point as a fsSpec is only supported with the new 132 # wrapper; all key=value pairs can be passed on the command line in this 133 # mode, so config file support is not needed). 134 [[ $bees_fsSpec = */* ]] || readConfigFileIfExists "$bees_config_dir/$bees_fsSpec.conf" 135 fi 136 137 [[ $bees_uuid ]] || { 138 # if bees_uuid is not in our .conf file, look it up with findmnt 139 for findmnt_mode in --kernel --mtab --fstab; do 140 read -r bees_uuid fstype < <(findmnt "$findmnt_mode" -n -o uuid,fstype "$bees_fsSpec") ||: 141 [[ $bees_uuid && $fstype ]] && break 142 done 143 [[ $bees_uuid ]] || die "Unable to identify a device matching $bees_fsSpec with findmnt" 144 [[ $fstype = btrfs ]] || die "Device type is $fstype, not btrfs" 145 } 146 147 [[ $bees_uuid = */* ]] || readConfigFileIfExists "$bees_config_dir/UUID=$bees_uuid.conf" 148 149 # Honor any values read from config files above; otherwise, set defaults. 150 bees_workDir="${bees_workDir:-.beeshome}" 151 bees_runDir="${bees_runDir:-/run/bees}" 152 bees_mntDir="${bees_mntDir:-$bees_runDir/mnt/$bees_uuid}" 153 bees_home="${bees_home:-$bees_mntDir/$bees_workDir}" 154 bees_status="${bees_status:-${bees_runDir}/$bees_uuid.status}" 155 bees_verbosity="${bees_verbosity:-6}" 156 bees_idxSizeMB="${bees_idxSizeMB:-1024}" 157 bees_idxSize=${bees_idxSize:-"$(( bees_idxSizeMB * 1024 * 1024 ))"} 158 bees_blockdev=${bees_blockdev:-"/dev/disk/by-uuid/$bees_uuid"} 159 160 [[ -b $bees_blockdev ]] || die "Block device $bees_blockdev missing" 161 (( bees_idxSize % (16 * 1024 * 1024) == 0 )) || die "DB size must be divisible by 16MB" 162} 163 164do_run() { 165 local db old_db_size 166 167 _setup "$1"; shift 168 mkdir -p -- "$bees_mntDir" || exit 169 170 # subvol id 5 is reserved for the root subvolume of a btrfs filesystem. 171 mountpoint -q "$bees_mntDir" || mount -osubvolid=5 -- "$bees_blockdev" "$bees_mntDir" || exit 172 if [[ -d $bees_home ]]; then 173 btrfs subvolume show "$bees_home" >/dev/null 2>&1 || die "$bees_home exists but is not a subvolume" 174 else 175 btrfs subvolume create "$bees_home" || exit 176 sync # workaround for Zygo/bees#93 177 fi 178 db=$bees_home/beeshash.dat 179 touch -- "$db" 180 181 old_db_size=$(stat -c %s -- "$db") 182 new_db_size=$bees_idxSize 183 184 if (( old_db_size != new_db_size )); then 185 rm -f -- "$bees_home"/beescrawl."$bees_uuid".dat 186 truncate -s "$new_db_size" -- "$db" || exit 187 fi 188 chmod 700 -- "$bees_home" 189 190 # BEESSTATUS and BEESHOME are the only variables handled by the legacy 191 # wrapper for which getenv() is called in C code. 192 BEESSTATUS=$bees_status BEESHOME=$bees_home exec "${beesd_bin:-/lib/bees/bees}" \ 193 --verbose "$bees_verbosity" \ 194 "$@" "$bees_mntDir" || exit 195} 196 197do_cleanup() { 198 _setup "$1"; shift 199 mountpoint -q "$bees_mntDir" && umount -l -- "$bees_mntDir" || exit 200} 201 202(( $# >= 2 )) || usage 203declare -f "do_$1" >/dev/null 2>&1 || usage 204mode=$1; shift # must be a do_* function; currently "run" or "cleanup" 205 206declare -a args=( "$1" ); shift # pass first argument (config-name|fsSpec) through literally 207 208# parse other arguments as key=value pairs, or pass them through literally if they do not match that form. 209# similarly, any option after "--" will be passed through literally. 210while (( $# )); do 211 if [[ $1 = *=* ]]; then 212 set_option "$1" 213 elif [[ $1 = -- ]]; then 214 shift 215 args+=( "$@" ) 216 break 217 else 218 args+=( "$1" ) 219 fi 220 shift 221done 222 223"do_$mode" "${args[@]}"