Advertisement
urain39

common-functions.sh

Nov 8th, 2019
139
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 12.72 KB | None | 0 0
  1. # Copyright (c) 2018-2019 The Alcove Project Authors.
  2. #
  3. # Maintainer: urain39 <urain39[AT]qq[DOT]com>
  4. #
  5. # Requirements: awk chmod chown grep mkdir mkfifo
  6. #       resize sed stat start-stop-daemon touch
  7. #
  8. # For debug:
  9. #   alias exit='not echo "exited with"'
  10. #   export readonly USER="root"
  11. #   alias readonly=''
  12.  
  13. ##################################################
  14. # Constants
  15. ##################################################
  16.  
  17. # shellcheck disable=SC2034
  18. readonly __VERSION__="1"
  19.  
  20. readonly COLOR_RESET="\033[0m"
  21. readonly COLOR_BOLD_RED="\033[1;31m"
  22. readonly COLOR_BOLD_GREEN="\033[1;32m"
  23. readonly COLOR_BOLD_YELLOW="\033[1;33m"
  24. readonly COLOR_BOLD_BLUE="\033[1;34m"
  25.  
  26. #readonly CURSOR_BEGIN="\r"
  27. #readonly CURSOR_BEGIN_ERASE="\r\033[K"
  28. readonly CURSOR_GOTO="\033[%d;%dH"
  29.  
  30. ##################################################
  31. # User's Variables
  32. ##################################################
  33.  
  34. : "${command:=""}"
  35. : "${command_args:=""}"
  36. : "${command_user:="root:root"}"
  37. : "${pidfile:=""}"
  38. : "${start_stop_daemon_args:=""}"
  39.  
  40. ##################################################
  41. # Hack for Shell
  42. ##################################################
  43.  
  44. __hack_environ__() {
  45.     careless eval "$(resize)"
  46. }
  47.  
  48. __hack_stdout__() {
  49.     __hack_environ__
  50.     printf "\033[%d;1H" "${LINES}"
  51. }
  52.  
  53. # __hack_xxxxxx__() {
  54. #   your compat hack
  55. # }
  56.  
  57. ##################################################
  58. # Common Functions
  59. ##################################################
  60.  
  61. ____checkstatus____() {
  62.     # return codes
  63.     #   0:  ok
  64.     #   1:  fail
  65.  
  66.     local _path="${1}"
  67.     local _mode="${2}"
  68.     local _owner="${3}"
  69.     local _status=""
  70.  
  71.     if not isempty "${_mode}"; then
  72.         _status="$(stat -c "%04a" "${_path}")"
  73.         #_status="${_status// /}"
  74.  
  75.         if [ "${_status}" != "${_mode}" ]; then
  76.             einfo "${_path}: correcting mode"
  77.             quietly chmod "${_mode}" "${_path}"
  78.  
  79.             if not issuccess; then
  80.                 eend 1
  81.                 return 1
  82.             fi
  83.         fi
  84.     fi
  85.  
  86.     if not isempty "${_owner}"; then
  87.         _status="$(stat -c "%U:%G" "${_path}")"
  88.         #_status="${_status// /}"
  89.  
  90.         if [ "${_status}" != "${_owner}" ]; then
  91.             einfo "${_path}: correcting owner"
  92.             quietly chown "${_owner}" "${_path}"
  93.  
  94.             if not issuccess; then
  95.                 eend 1
  96.                 return 1
  97.             fi
  98.         fi
  99.     fi
  100. }
  101.  
  102. action() {
  103.     # return codes
  104.     #   0:  ok
  105.     #   1:  fail
  106.  
  107.     local _action="${1}"
  108.  
  109.     case "${_action}" in
  110.         "start")
  111.             start_pre && \
  112.                 start && start_post
  113.             ;;
  114.         "stop")
  115.             stop_pre && \
  116.                 stop && start_post
  117.             ;;
  118.         "reload")
  119.             reload
  120.             ;;
  121.         "restart")
  122.             restart
  123.             ;;
  124.         "status")
  125.             status
  126.             ;;
  127.         *)
  128.             einfo "Usage: <start|stop|reload|restart|status>"
  129.             ;;
  130.     esac
  131. }
  132.  
  133. checkpath() {
  134.     # return codes
  135.     #   0:  ok
  136.     #   1:  fail
  137.  
  138.     local _option=""
  139.     local _option_mode=""
  140.     local _truncate="no"
  141.     local _umask_old=""
  142.     local _mode=""
  143.     local _owner=""
  144.     local _path=""
  145.     local _retval="0"
  146.  
  147.     local OPTIND="1"
  148.     local OPTARG=""
  149.  
  150.     _umask_old="$(umask)"
  151.  
  152.     umask 002
  153.  
  154.     while getopts 'dDfFpm:o:W' _option; do
  155.         case "${_option}" in
  156.             "d")
  157.                 _option_mode="d"
  158.                 ;;
  159.             "D")
  160.                 _option_mode="d"
  161.                 _truncate="yes"
  162.                 ;;
  163.             "f")
  164.                 _option_mode="f"
  165.                 ;;
  166.             "F")
  167.                 _option_mode="f"
  168.                 _truncate="yes"
  169.                 ;;
  170.             "p")
  171.                 _option_mode="p"
  172.                 ;;
  173.             "m")
  174.                 _mode="$(
  175.                     echo "${OPTARG}" | awk -F'[ \t]+' '{
  176.                         if (NF == 1) {
  177.                             if ($1 ~ /^[0-7]{3,4}$/) {
  178.                                 printf("%04d", $1)
  179.                                 exit(0)
  180.                             }
  181.                         }
  182.                         exit(1)
  183.                     }'
  184.                 )"
  185.  
  186.                 if not issuccess; then
  187.                     eerror "checkpath: invalid mode '${OPTARG}'!"
  188.                     _retval="1"
  189.                     break
  190.                 fi
  191.                 ;;
  192.             "o")
  193.                 _owner="$(
  194.                     echo "${OPTARG}" | awk -F':' '{
  195.                         if (NF == 2) {
  196.                             printf("%s:%s", $1, $2)
  197.                         } else if (NF == 1) {
  198.                             printf("%s:%s", $1, $1)
  199.                         } else {
  200.                             exit(1)
  201.                         }
  202.                         exit(0)
  203.                     }'
  204.                 )"
  205.  
  206.                 if not issuccess; then
  207.                     eerror "checkpath: invalid owner '${OPTARG}'!"
  208.                     _retval="1"
  209.                     break
  210.                 fi
  211.                 ;;
  212.             "W")
  213.                 _option_mode="W"
  214.                 ;;
  215.         esac
  216.     done
  217.  
  218.     if [ "${_retval}" != 0 ]; then
  219.         eend 1 "checkpath: parse arguments failed"
  220.         umask "${_umask_old}"
  221.         return "${_retval}"
  222.     fi
  223.  
  224.     shift "$((OPTIND - 1))"
  225.  
  226.     for _path in "${@}"; do
  227.         case "${_option_mode}" in
  228.             "d")
  229.                 if yesno "${_truncate}"; then
  230.                     einfo "${_path}: truncating directory"
  231.                     quietly eval "find \"${_path}\" | sed '1d' | xargs rm -rf"
  232.  
  233.                     if not issuccess; then
  234.                         eend 1
  235.                         _retval="1"
  236.                         break
  237.                     fi
  238.                 fi
  239.  
  240.                 if not isdirectory "${_path}"; then
  241.                     einfo "${_path}: creating directory"
  242.                     quietly mkdir "${_path}"
  243.  
  244.                     if not issuccess; then
  245.                         eend 1
  246.                         _retval="1"
  247.                         break
  248.                     fi
  249.                 fi
  250.  
  251.                 ____checkstatus____ "${_path}" "${_mode}" "${_owner}"
  252.                 ;;
  253.             "f")
  254.                 if yesno "${_truncate}"; then
  255.                     einfo "${_path}: truncating file"
  256.                     quietly eval ": > \"${_path}\""
  257.  
  258.                     if not issuccess; then
  259.                         eend 1
  260.                         _retval="1"
  261.                         break
  262.                     fi
  263.                 fi
  264.  
  265.                 if not isfile "${_path}"; then
  266.                     einfo "${_path}: creating file"
  267.                     quietly touch "${_path}"
  268.  
  269.                     if not issuccess; then
  270.                         eend 1
  271.                         _retval="1"
  272.                         break
  273.                     fi
  274.                 fi
  275.  
  276.                 ____checkstatus____ "${_path}" "${_mode}" "${_owner}"
  277.                 ;;
  278.             "p")
  279.                 if not ispipe "${_path}"; then
  280.                     einfo "${_path}: creating pipe"
  281.                     quietly mkfifo "${_path}"
  282.  
  283.                     if not issuccess; then
  284.                         eend 1
  285.                         _retval="1"
  286.                         break
  287.                     fi
  288.                 fi
  289.  
  290.                 ____checkstatus____ "${_path}" "${_mode}" "${_owner}"
  291.                 ;;
  292.             "W")
  293.                 if isexists "${_path}"; then
  294.                     einfo "${_path}: writable check"
  295.  
  296.                     if isdirectory "${_path}"; then
  297.                         quietly touch "${_path}/.writable${$}" && \
  298.                             quietly rm "${_path}/.writable${$}"
  299.                     else
  300.                         quietly mv "${_path}" "${_path}.writable${$}" && \
  301.                             quietly mv "${_path}.writable${$}" "${_path}"
  302.                     fi
  303.  
  304.                     if not issuccess; then
  305.                         eend 1
  306.                         _retval="1"
  307.                         break
  308.                     fi
  309.                 fi
  310.                 ;;
  311.         esac
  312.     done
  313.  
  314.     umask "${_umask_old}"
  315.     return "${_retval}"
  316. }
  317.  
  318. ebegin() {
  319.     printf " ${COLOR_BOLD_GREEN}*${COLOR_RESET} %s ...\n" "${*}"
  320. }
  321.  
  322. einfo() {
  323.     printf " ${COLOR_BOLD_GREEN}*${COLOR_RESET} %s\n" "${*}"
  324. }
  325.  
  326. ewarn() {
  327.     printf " ${COLOR_BOLD_YELLOW}*${COLOR_RESET} %s\n" "${*}"
  328. }
  329.  
  330. eerror() {
  331.     printf " ${COLOR_BOLD_RED}*${COLOR_RESET} %s\n" "${*}"
  332. }
  333.  
  334. eend() {
  335.     # return codes
  336.     #   0:  ok
  337.     #   1:  fail
  338.  
  339.     [ "${#}" -lt 1 ] && return 0
  340.  
  341.     local _status="${1}"
  342.  
  343.     shift # skip _status
  344.  
  345.     # for ash & dash
  346.     __hack_environ__
  347.  
  348.     # NOTE: `stat="[ ok ]"; echo "${#stat}"` -> 6
  349.  
  350.     if [ "${_status}" = "0" ]; then
  351.         printf "${CURSOR_GOTO}${COLOR_BOLD_BLUE}[ ${COLOR_BOLD_GREEN}ok ${COLOR_BOLD_BLUE}]${COLOR_RESET}\n" \
  352.             "$((LINES - 1))" "$((COLUMNS - 5))"
  353.     else
  354.         if not isempty "${*}"; then
  355.             eerror "${*}"
  356.             printf "${CURSOR_GOTO}${COLOR_BOLD_BLUE}[ ${COLOR_BOLD_RED}!! ${COLOR_BOLD_BLUE}]${COLOR_RESET}\n" \
  357.                 "$((LINES - 1))" "$((COLUMNS - 5))"
  358.  
  359.             return 1
  360.         else
  361.             printf "${CURSOR_GOTO} ${COLOR_BOLD_RED}*${COLOR_RESET}" \
  362.                 "$((LINES - 1))" "1"
  363.             printf "${CURSOR_GOTO}${COLOR_BOLD_BLUE}[ ${COLOR_BOLD_RED}!! ${COLOR_BOLD_BLUE}]${COLOR_RESET}\n" \
  364.                 "$((LINES - 1))" "$((COLUMNS - 5))"
  365.  
  366.             return 1
  367.         fi
  368.     fi
  369.  
  370.     return 0
  371. }
  372.  
  373. quietly() {
  374.     "${@}" > /dev/null 2>&1
  375. }
  376.  
  377. careless() {
  378.     "${@}" > /dev/null 2>&1
  379.     return 0
  380. }
  381.  
  382. shouldbe() {
  383.     # return codes
  384.     #   0:  matched
  385.     #   1:  not match
  386.  
  387.     [ "${#}" -lt 2 ] && return 1
  388.  
  389.     local _shouldbe="${1}"
  390.     local _result=""
  391.  
  392.     shift # skip _shouldbe
  393.     _result="$("${@}" 2> /dev/null)"
  394.  
  395.     if [ "${_result}" != "${_shouldbe}" ]; then
  396.         einfo "shouldbe: ${*}"
  397.         eend 1 # "^^ We'd like to add a failed flag above"
  398.         eerror "shouldbe: NOTE: expected '${_shouldbe}', but found '${_result}'"
  399.         return 1
  400.     fi
  401.  
  402.     return 0
  403. }
  404.  
  405. contains() {
  406.     # return codes
  407.     #   0:  contains
  408.     #   1:  not contains
  409.  
  410.     [ "${#}" -lt 2 ] && return 1
  411.  
  412.     local _string="${1}"
  413.     local _search="${2}"
  414.  
  415.     [ "${_string#*${_search}}" = "${_string}" ] && return 1 \
  416.         || return 0
  417. }
  418.  
  419. is() {
  420.     "${@}" && return 0 \
  421.         || return 1
  422. }
  423.  
  424. not() {
  425.     "${@}" && return 1 \
  426.         || return 0
  427. }
  428.  
  429. isempty() {
  430.     # return codes
  431.     #   0:  empty
  432.     #   1:  not empty
  433.  
  434.     #[ "${#}" -lt 1 ] && return 1
  435.  
  436.     if [ -z "${*}" ]; then
  437.         return 0
  438.     fi
  439.  
  440.     return 1
  441. }
  442.  
  443. isexists() {
  444.     # return codes
  445.     #   0:  exists
  446.     #   1:  not exists
  447.  
  448.     #[ "${#}" -lt 1 ] && return 1
  449.  
  450.     if [ -e "${*}" ]; then
  451.         return 0
  452.     fi
  453.  
  454.     return 1
  455. }
  456.  
  457. isfile() {
  458.     # return codes
  459.     #   0:  file
  460.     #   1:  not file
  461.  
  462.     #[ "${#}" -lt 1 ] && return 1
  463.  
  464.     if [ -f "${*}" ]; then
  465.         return 0
  466.     fi
  467.  
  468.     return 1
  469. }
  470.  
  471. ispipe() {
  472.     # return codes
  473.     #   0:  pipe
  474.     #   1:  not pipe
  475.  
  476.     #[ "${#}" -lt 1 ] && return 1
  477.  
  478.     if [ -p "${*}" ]; then
  479.         return 0
  480.     fi
  481.  
  482.     return 1
  483. }
  484.  
  485. isdirectory() {
  486.     # return codes
  487.     #   0:  directory
  488.     #   1:  not directory
  489.  
  490.     #[ "${#}" -lt 1 ] && return 1
  491.  
  492.     if [ -d "${*}" ]; then
  493.         return 0
  494.     fi
  495.  
  496.     return 1
  497. }
  498.  
  499. issuccess() {
  500.     # return codes
  501.     #   0:  success
  502.     #   1:  failed
  503.  
  504.     if [ "${?}" = 0 ]; then
  505.         return 0
  506.     fi
  507.  
  508.     return 1
  509. }
  510.  
  511. ismounted() {
  512.     # return codes
  513.     #   0:  mounted
  514.     #   1:  unmounted
  515.  
  516.     local _path="${1}"
  517.  
  518.     # shellcheck disable=SC2016
  519.     quietly awk -F'[ \t]+' \
  520.     -v "path=${_path}" '\
  521.     BEGIN {
  522.         found = 0
  523.     } {
  524.         gsub(/\\040/, " ", $2)
  525.         if ($2 == path) {
  526.             found = 1
  527.             exit(0)
  528.         }
  529.     } END {
  530.         if (found) {
  531.             exit(0)
  532.         }
  533.         exit(1)
  534.     }
  535.     ' "/proc/${$}/mounts"
  536.  
  537.     if issuccess; then
  538.         return 0
  539.     else
  540.         return 1
  541.     fi
  542. }
  543.  
  544. yesno() {
  545.     # return codes
  546.     #   0:  yes
  547.     #   1:  no
  548.  
  549.     [ "${#}" -lt 1 ] && return 1
  550.  
  551.     local _value="${1}"
  552.  
  553.     # Check the _value directly so people can do:
  554.     # yesno ${VAR}
  555.  
  556.     case "${_value}" in
  557.         [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
  558.             return 0
  559.             ;;
  560.         #[Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
  561.         #   return 1
  562.         #   ;;
  563.     esac
  564.  
  565.     return 1
  566. }
  567.  
  568. noyes() { :; }
  569.  
  570. ##################################################
  571. # Default Actions
  572. ##################################################
  573.  
  574. start_pre() { :; }
  575.  
  576. start() {
  577.     local _background=""
  578.  
  579.     ebegin "Starting ${name:-"${0}"}"
  580.  
  581.     if isempty "${command}"; then
  582.         ewarn "WARNING: \${command} is empty or not set!"
  583.     fi
  584.  
  585.     # shellcheck disable=SC2154
  586.     if yesno "${command_background}"; then
  587.         if isempty "${pidfile}"; then
  588.             eend 1 "command_background option requires a pidfile"
  589.             return 1
  590.         fi
  591.  
  592.         # shellcheck disable=SC2154
  593.         if not isempty "${command_args_background}"; then
  594.             eend 1 "command_background option used with command_args_background"
  595.             return 1
  596.         fi
  597.  
  598.         # shellcheck disable=SC2034
  599.         _background="--background --make-pidfile"
  600.     fi
  601.  
  602.     # shellcheck disable=SC2034,SC2154
  603.     not isempty "${output_logger}" && \
  604.         output_logger_arg="--stdout-logger \"${output_logger}\""
  605.     # shellcheck disable=SC2034,SC2154
  606.     not isempty "${error_logger}" && \
  607.         error_logger_arg="--stderr-logger \"${error_logger}\""
  608.  
  609.     # shellcheck disable=SC2154
  610.     # the eval call is necessary for cases like:
  611.     # command_args="this \"is a\" test"
  612.     # to work properly.
  613.     eval "start-stop-daemon --start \
  614.         --exec ${command} \
  615.         ${chroot:+--chroot} ${chroot} \
  616.         ${directory:+--chdir} ${directory} \
  617.         ${output_log+--stdout} ${output_log} \
  618.         ${error_log+--stderr} ${error_log} \
  619.         ${output_logger_arg} \
  620.         ${error_logger_arg} \
  621.         ${procname:+--name} ${procname} \
  622.         ${pidfile:+--pidfile} ${pidfile} \
  623.         ${command_user+--user} ${command_user} \
  624.         ${umask+--umask} ${umask} \
  625.         ${_background} ${start_stop_daemon_args} \
  626.         -- ${command_args} ${command_args_background} \
  627.         "
  628.  
  629.     eend "${?}" "Failed to start ${name:-"${0}"}"
  630. }
  631.  
  632. start_post() { :; }
  633.  
  634. stop_pre() { :; }
  635.  
  636. stop() {
  637.     local _progress=""
  638.  
  639.     ebegin "Stopping ${name:-"${0}"}"
  640.  
  641.     # shellcheck disable=SC2154
  642.     yesno "${command_progress}" && _progress="--progress"
  643.  
  644.     # shellcheck disable=SC2154
  645.     eval "start-stop-daemon --stop \
  646.         ${retry:+--retry} ${retry} \
  647.         ${command:+--exec} ${command} \
  648.         ${procname:+--name} ${procname} \
  649.         ${pidfile:+--pidfile} ${chroot}${pidfile} \
  650.         ${stopsig:+--signal} ${stopsig} \
  651.         ${_progress} \
  652.         "
  653.  
  654.     eend "${?}" "Failed to stop ${name:-"${0}"}"
  655. }
  656.  
  657. stop_post() { :; }
  658.  
  659. status() {
  660.     # return codes
  661.     #   0:  running
  662.     #   1:  stopped
  663.     #   2:  crashed
  664.  
  665.     local _pid=""
  666.  
  667.     if isfile "${pidfile}"; then
  668.         while read -r _pid; do
  669.             if isfile "/proc/${_pid}/cmdline"; then
  670.                 if quietly grep "${command}" "/proc/${_pid}/cmdline"; then
  671.                     einfo "status: running"
  672.                     return 0
  673.                 fi
  674.             fi
  675.  
  676.             eerror "status: crashed"
  677.             return 2
  678.         done < "${pidfile}"
  679.     fi
  680.  
  681.     einfo "status: stopped"
  682.     return 1
  683. }
  684.  
  685. restart() {
  686.     stop && start
  687. }
  688.  
  689. reload() { :; }
  690.  
  691. ##################################################
  692. # Apply Pre-hacks
  693. ##################################################
  694.  
  695. __hack_environ__
  696. __hack_stdout__
  697.  
  698. ##################################################
  699. # Apply Pre-checks
  700. ##################################################
  701.  
  702. if [ "${USER}" != "root" ]; then
  703.     eend 1 "ERROR: requires root to manage daemons!"
  704.     exit 1
  705. fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement