on master help sign in
utils: add lib.sh containing common shell script

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>

commit28dacee28819ea2d24fd8ff6547fd3a175677084 authorMarkus Heiser <markus.heiser@darmarit.de> dateWed 8 Jan 2020, 18:13 (7 years ago) parentbdf3920
3 files changed+318
.gitignore+1 Makefile+1 utils/lib.sh+316
.gitignore+1
@@ -1,6 +1,7 @@
11# to sync with .dockerignore
22.coverage
33coverage/
4+cache/
45.installed.cfg
56engines.cfg
67env
Makefile+1
@@ -77,6 +77,7 @@ test: test.pep8 test.unit test.sh test.robot
7777 - make pylint
7878
7979test.sh:
80+ shellcheck -x utils/lib.sh
8081
8182test.pep8: pyenvinstall
8283 $(PY_ENV_ACT); ./manage.sh pep8_check
utils/lib.shnew file+316
@@ -0,0 +1,316 @@
1+#!/usr/bin/env bash
2+# -*- coding: utf-8; mode: sh -*-
3+# shellcheck disable=SC2059,SC1117,SC2162,SC2004
4+
5+if [[ -z "${REPO_ROOT}" ]]; then
6+ REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")
7+ while [ -h "${REPO_ROOT}" ] ; do
8+ REPO_ROOT=$(readlink "${REPO_ROOT}")
9+ done
10+ REPO_ROOT=$(cd "${REPO_ROOT}/.." && pwd -P )
11+fi
12+
13+if [[ -z ${TEMPLATES} ]]; then
14+ TEMPLATES="${REPO_ROOT}/utils/templates"
15+fi
16+
17+if [[ -z "$CACHE" ]]; then
18+ CACHE="${REPO_ROOT}/cache"
19+fi
20+
21+if [[ -z "$SYSTEMD_UNITS" ]]; then
22+ SYSTEMD_UNITS="/lib/systemd/system/"
23+fi
24+
25+sudo_or_exit() {
26+ # usage: sudo_or_exit
27+
28+ if [ ! "$(id -u)" -eq 0 ]; then
29+ err_msg "this command requires root (sudo) privilege!" >&2
30+ exit 42
31+ fi
32+}
33+
34+rst_title() {
35+ # usage: rst_title <header-text> [part|chapter|section]
36+
37+ case ${2-chapter} in
38+ part) printf "\n${1//?/=}\n$1\n${1//?/=}\n";;
39+ chapter) printf "\n${1}\n${1//?/=}\n";;
40+ section) printf "\n${1}\n${1//?/-}\n";;
41+ *)
42+ err_msg "invalid argument '${2}' in line $(caller)"
43+ return 42
44+ ;;
45+ esac
46+}
47+
48+if command -v fmt >/dev/null; then
49+ export FMT="fmt -u"
50+else
51+ export FMT="cat"
52+fi
53+
54+rst_para() {
55+ # usage: RST_INDENT=1 rst_para "lorem ipsum ..."
56+ local prefix=''
57+ if ! [[ -z $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then
58+ prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf " "; done)"
59+ echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix"
60+ else
61+ echo -en "\n$*\n" | $FMT
62+ fi
63+}
64+
65+err_msg() { echo -e "ERROR: $*" >&2; }
66+warn_msg() { echo -e "WARN: $*" >&2; }
67+info_msg() { echo -e "INFO: $*"; }
68+
69+wait_key(){
70+ # usage: waitKEY [<timeout in sec>]
71+
72+ local _t=$1
73+ [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
74+ [[ ! -z $_t ]] && _t="-t $_t"
75+ # shellcheck disable=SC2086
76+ read -n1 $_t -p "** press any [KEY] to continue **"
77+ echo
78+}
79+
80+ask_yn() {
81+ # usage: ask_yn <prompt-text> [Ny|Yn] [<timeout in sec>]
82+
83+ local EXIT_YES=0 # exit status 0 --> successful
84+ local EXIT_NO=1 # exit status 1 --> error code
85+
86+ local _t=$3
87+ [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
88+ [[ ! -z $_t ]] && _t="-t $_t"
89+ case "${2}" in
90+ Yn)
91+ local exit_val=${EXIT_YES}
92+ local choice="[YES/no]"
93+ local default="Yes"
94+ ;;
95+ *)
96+ local exit_val=${EXIT_NO}
97+ local choice="[NO/yes]"
98+ local default="No"
99+ ;;
100+ esac
101+ echo
102+ while true; do
103+ printf "$1 ${choice} "
104+ # shellcheck disable=SC2086
105+ read -n1 $_t
106+ if [[ -z $REPLY ]]; then
107+ printf "$default\n"; break
108+ elif [[ $REPLY =~ ^[Yy]$ ]]; then
109+ exit_val=${EXIT_YES}
110+ printf "\n"
111+ break
112+ elif [[ $REPLY =~ ^[Nn]$ ]]; then
113+ exit_val=${EXIT_NO}
114+ printf "\n"
115+ break
116+ fi
117+ _t=""
118+ err_msg "invalid choice"
119+ done
120+ return $exit_val
121+}
122+
123+tee_stderr () {
124+
125+ # usage::
126+ # tee_stderr 1 <<EOF | python -i
127+ # print("hello")
128+ # EOF
129+ # ...
130+ # >>> print("hello")
131+ # hello
132+
133+ local _t="0";
134+ if [[ ! -z $1 ]] ; then _t="$1"; fi
135+
136+ (while read line; do
137+ # shellcheck disable=SC2086
138+ sleep $_t
139+ echo -e "$line" >&2
140+ echo "$line"
141+ done)
142+}
143+
144+prefix_stdout () {
145+ # usage: <cmd> | prefix_stdout [prefix]
146+
147+ local prefix="-->| "
148+
149+ if [[ ! -z $1 ]] ; then prefix="$1"; fi
150+
151+ (while IFS= read line; do
152+ echo -e "${prefix}$line"
153+ done)
154+}
155+
156+append_line() {
157+
158+ # usage: append_line <line> <file>
159+ #
160+ # Append line if not exists, create file if not exists. E.g::
161+ #
162+ # append_line 'source ~/.foo' ~/bashrc
163+
164+ local LINE=$1
165+ local FILE=$2
166+ grep -qFs -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
167+}
168+
169+cache_download() {
170+
171+ # usage: cache_download <url> <local-filename>
172+
173+ local exit_value=0
174+
175+ if [[ ! -z ${SUDO_USER} ]]; then
176+ sudo -u "${SUDO_USER}" mkdir -p "${CACHE}"
177+ else
178+ mkdir -p "${CACHE}"
179+ fi
180+
181+ if [[ -f "${CACHE}/$2" ]] ; then
182+ info_msg "already cached: $1"
183+ info_msg " --> ${CACHE}/$2"
184+ fi
185+
186+ if [[ ! -f "${CACHE}/$2" ]]; then
187+ info_msg "caching: $1"
188+ info_msg " --> ${CACHE}/$2"
189+ if [[ ! -z ${SUDO_USER} ]]; then
190+ sudo -u "${SUDO_USER}" wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
191+ else
192+ wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
193+ fi
194+ if $exit_value; then
195+ err_msg "failed to download: $1"
196+ fi
197+ fi
198+}
199+
200+choose_one() {
201+
202+ # usage:
203+ #
204+ # DEFAULT_SELECT= 2 \
205+ # choose_one <name> "your selection?" "Coffee" "Coffee with milk"
206+
207+ local default=${DEFAULT_SELECT-1}
208+ local REPLY
209+ local env_name=$1 && shift
210+ local choice=$1;
211+ local max="${#@}"
212+ local _t
213+ [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
214+ [[ ! -z $_t ]] && _t="-t $_t"
215+
216+ list=("$@")
217+ echo -e "Menu::"
218+ for ((i=1; i<= $(($max -1)); i++)); do
219+ if [[ "$i" == "$default" ]]; then
220+ echo -e " $i.) ${list[$i]} [default]"
221+ else
222+ echo -e " $i.) ${list[$i]}"
223+ fi
224+ done
225+ while true; do
226+ printf "$1 [$default] "
227+
228+ if (( 10 > $max )); then
229+ # shellcheck disable=SC2086
230+ read -n1 $_t
231+ else
232+ # shellcheck disable=SC2086,SC2229
233+ read $_t
234+ fi
235+ # selection fits
236+ [[ $REPLY =~ ^-?[0-9]+$ ]] && (( $REPLY > 0 )) && (( $REPLY < $max )) && break
237+
238+ # take default
239+ [[ -z $REPLY ]] && REPLY=$default && break
240+
241+ _t=""
242+ err_msg "invalid choice"
243+ done
244+ echo
245+ eval "$env_name"='${list[${REPLY}]}'
246+}
247+
248+install_template() {
249+
250+ # usage:
251+ #
252+ # install_template [--no-eval] {file} [{owner} [{group} [{chmod}]]]
253+ #
254+ # install_template --no-eval /etc/updatedb.conf root root 644
255+
256+ local do_eval=0
257+ if [[ "$1" == "--no-eval" ]]; then
258+ do_eval=1; shift
259+ fi
260+ local dst="${1}"
261+ local owner=${2-$(id -un)}
262+ local group=${3-$(id -gn)}
263+ local chmod=${4-644}
264+ local _reply=""
265+
266+ info_msg "install: ${dst}"
267+
268+ if [[ ! -f "${TEMPLATES}${dst}" ]] ; then
269+ err_msg "${TEMPLATES}${dst} does not exists"
270+ err_msg "... can't install $dst / exit installation with error 42"
271+ wait_key 30
272+ return 42
273+ fi
274+
275+ local template_file="${TEMPLATES}${dst}"
276+ if [[ "$do_eval" == "1" ]]; then
277+ info_msg "BUILD template ${template_file}"
278+ if [[ -f "${TEMPLATES}${dst}" ]] ; then
279+ template_file="${CACHE}${dst}"
280+ mkdir -p "$(dirname "${template_file}")"
281+ # shellcheck disable=SC2086
282+ eval "echo \"$(cat ${TEMPLATES}${dst})\"" > "${template_file}"
283+ else
284+ err_msg "failed ${template_file}"
285+ return 42
286+ fi
287+ fi
288+
289+ if [[ -f "${dst}" ]] ; then
290+ info_msg "file ${dst} allready exists on this host"
291+ choose_one _reply "choose next step with file $dst" \
292+ "replace file" \
293+ "leave file unchanged"
294+
295+ case $_reply in
296+ "replace file")
297+ info_msg "install: ${template_file}"
298+ sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
299+ "${template_file}" "${dst}"
300+ ;;
301+ "leave file unchanged")
302+ ;;
303+ "interactiv shell")
304+ echo "// exit with STRG-D"
305+ sudo -H -u "${owner}" -i
306+ ;;
307+ esac
308+
309+ else
310+ info_msg "install: ${template_file}"
311+ sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
312+ "${template_file}" "${dst}"
313+ fi
314+
315+}
316+