biome: 1.9.2 -> 1.9.3
[NixPkgs.git] / pkgs / tools / filesystems / bees / bees-service-wrapper
blobad72c1d95e9d5c40b9d7ac9eaedfd7f1c61d1bcb
1 #!/usr/bin/env bash
3 shopt -s extglob
5 # Upstream wrapper requires UUID to be used for configuration.
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.
11 # Thus, for using bees from NixOS, we have our own wrapper, which supports not
12 # just UUID but any specification permitted by findmnt
14 [[ $bees_debug ]] && { PS4=':${BASH_SOURCE##*/}:$LINENO+'; set -x; }
16 usage() {
17 cat >&2 <<EOF
18 Usage: ${BASH_SOURCE##*/} run|cleanup config-name|fsSpec [idxSizeMB=...] [verbosity=...] [workDir=...] [-- daemon-options...]
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.
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.
30 If fsSpec contains a /, it assumed to be a mount point to be looked up by
31 findmnt, not a config file name.
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.
35 EOF
36 exit 1
39 die() { echo "$*" >&2; exit 1; }
41 allConfigNames=( blockdev fsSpec home idxSize idxSizeMB mntDir runDir status verbosity workDir )
43 # Alternate names for configuration values; "bees_" will always be prepended
44 declare -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
54 # legacy bees config files can be arbitrary shell scripts, so we need to actually evaluate them
55 sandboxedConfigFileEval() {
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"
63 readConfigFileIfExists() {
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
73 set_option "$line"
74 done < <(sandboxedConfigFileEval "$1")
77 set_option() {
78 local k v
79 k="${1%%=*}" v="${1#*=}"
80 [[ ${altConfigNames[$k]} ]] && k=${altConfigNames[$k]}
81 printf -v "bees_$k" %s "$v"
84 uuid_re='^[[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{12}$'
86 # Shared code for setting configuration used by other operations.
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
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=( )
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."
127 else
128 # For a non-UUID fsSpec that is not a path, look only for a config file
129 # exactly matching its text.
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"
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"
147 [[ $bees_uuid = */* ]] || readConfigFileIfExists "$bees_config_dir/UUID=$bees_uuid.conf"
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"}
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"
164 do_run() {
165 local db old_db_size
167 _setup "$1"; shift
168 mkdir -p -- "$bees_mntDir" || exit
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
178 db=$bees_home/beeshash.dat
179 touch -- "$db"
181 old_db_size=$(stat -c %s -- "$db")
182 new_db_size=$bees_idxSize
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
188 chmod 700 -- "$bees_home"
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
197 do_cleanup() {
198 _setup "$1"; shift
199 mountpoint -q "$bees_mntDir" && umount -l -- "$bees_mntDir" || exit
202 (( $# >= 2 )) || usage
203 declare -f "do_$1" >/dev/null 2>&1 || usage
204 mode=$1; shift # must be a do_* function; currently "run" or "cleanup"
206 declare -a args=( "$1" ); shift # pass first argument (config-name|fsSpec) through literally
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.
210 while (( $# )); do
211 if [[ $1 = *=* ]]; then
212 set_option "$1"
213 elif [[ $1 = -- ]]; then
214 shift
215 args+=( "$@" )
216 break
217 else
218 args+=( "$1" )
220 shift
221 done
223 "do_$mode" "${args[@]}"