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; }
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
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.
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
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[@]}" \
63 readConfigFileIfExists
() {
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
74 done < <(sandboxedConfigFileEval
"$1")
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.
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[@]}")
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
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."
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
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"
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"
175 btrfs subvolume create
"$bees_home" ||
exit
176 sync
# workaround for Zygo/bees#93
178 db
=$bees_home/beeshash.dat
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
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.
211 if [[ $1 = *=* ]]; then
213 elif [[ $1 = -- ]]; then
223 "do_$mode" "${args[@]}"