lib.sh 7.6 KB

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