#!/bin/bash # Copyright Broadcom, Inc. All Rights Reserved. # SPDX-License-Identifier: APACHE-2.0 # # Library for managing services # shellcheck disable=SC1091 # Load Generic Libraries . /opt/bitnami/scripts/libvalidations.sh . /opt/bitnami/scripts/liblog.sh # Functions ######################## # Read the provided pid file and returns a PID # Arguments: # $1 - Pid file # Returns: # PID ######################### get_pid_from_file() { local pid_file="${1:?pid file is missing}" if [[ -f "$pid_file" ]]; then if [[ -n "$(< "$pid_file")" ]] && [[ "$(< "$pid_file")" -gt 0 ]]; then echo "$(< "$pid_file")" fi fi } ######################## # Check if a provided PID corresponds to a running service # Arguments: # $1 - PID # Returns: # Boolean ######################### is_service_running() { local pid="${1:?pid is missing}" kill -0 "$pid" 2>/dev/null } ######################## # Stop a service by sending a termination signal to its pid # Arguments: # $1 - Pid file # $2 - Signal number (optional) # Returns: # None ######################### stop_service_using_pid() { local pid_file="${1:?pid file is missing}" local signal="${2:-}" local pid pid="$(get_pid_from_file "$pid_file")" [[ -z "$pid" ]] || ! is_service_running "$pid" && return if [[ -n "$signal" ]]; then kill "-${signal}" "$pid" else kill "$pid" fi local counter=10 while [[ "$counter" -ne 0 ]] && is_service_running "$pid"; do sleep 1 counter=$((counter - 1)) done } ######################## # Start cron daemon # Arguments: # None # Returns: # true if started correctly, false otherwise ######################### cron_start() { if [[ -x "/usr/sbin/cron" ]]; then /usr/sbin/cron elif [[ -x "/usr/sbin/crond" ]]; then /usr/sbin/crond else false fi } ######################## # Generate a cron configuration file for a given service # Arguments: # $1 - Service name # $2 - Command # Flags: # --run-as - User to run as (default: root) # --schedule - Cron schedule configuration (default: * * * * *) # Returns: # None ######################### generate_cron_conf() { local service_name="${1:?service name is missing}" local cmd="${2:?command is missing}" local run_as="root" local schedule="* * * * *" local clean="true" # Parse optional CLI flags shift 2 while [[ "$#" -gt 0 ]]; do case "$1" in --run-as) shift run_as="$1" ;; --schedule) shift schedule="$1" ;; --no-clean) clean="false" ;; *) echo "Invalid command line flag ${1}" >&2 return 1 ;; esac shift done mkdir -p /etc/cron.d if "$clean"; then cat > "/etc/cron.d/${service_name}" <> /etc/cron.d/"$service_name" fi } ######################## # Remove a cron configuration file for a given service # Arguments: # $1 - Service name # Returns: # None ######################### remove_cron_conf() { local service_name="${1:?service name is missing}" local cron_conf_dir="/etc/monit/conf.d" rm -f "${cron_conf_dir}/${service_name}" } ######################## # Generate a monit configuration file for a given service # Arguments: # $1 - Service name # $2 - Pid file # $3 - Start command # $4 - Stop command # Flags: # --disable - Whether to disable the monit configuration # Returns: # None ######################### generate_monit_conf() { local service_name="${1:?service name is missing}" local pid_file="${2:?pid file is missing}" local start_command="${3:?start command is missing}" local stop_command="${4:?stop command is missing}" local monit_conf_dir="/etc/monit/conf.d" local disabled="no" # Parse optional CLI flags shift 4 while [[ "$#" -gt 0 ]]; do case "$1" in --disable) disabled="yes" ;; *) echo "Invalid command line flag ${1}" >&2 return 1 ;; esac shift done is_boolean_yes "$disabled" && conf_suffix=".disabled" mkdir -p "$monit_conf_dir" cat > "${monit_conf_dir}/${service_name}.conf${conf_suffix:-}" <&2 return 1 ;; esac shift done mkdir -p "$logrotate_conf_dir" cat < "${logrotate_conf_dir}/${service_name}" # Copyright Broadcom, Inc. All Rights Reserved. # SPDX-License-Identifier: APACHE-2.0 ${log_path} { ${period} rotate ${rotations} dateext compress copytruncate missingok $(indent "$extra" 2) } EOF } ######################## # Remove a logrotate configuration file # Arguments: # $1 - Service name # Returns: # None ######################### remove_logrotate_conf() { local service_name="${1:?service name is missing}" local logrotate_conf_dir="/etc/logrotate.d" rm -f "${logrotate_conf_dir}/${service_name}" } ######################## # Generate a Systemd configuration file # Arguments: # $1 - Service name # Flags: # --custom-service-content - Custom content to add to the [service] block # --environment - Environment variable to define (multiple --environment options may be passed) # --environment-file - Text file with environment variables (multiple --environment-file options may be passed) # --exec-start - Start command (required) # --exec-start-pre - Pre-start command (optional) # --exec-start-post - Post-start command (optional) # --exec-stop - Stop command (optional) # --exec-reload - Reload command (optional) # --group - System group to start the service with # --name - Service full name (e.g. Apache HTTP Server, defaults to $1) # --restart - When to restart the Systemd service after being stopped (defaults to always) # --pid-file - Service PID file # --standard-output - File where to print stdout output # --standard-error - File where to print stderr output # --success-exit-status - Exit code that indicates a successful shutdown # --type - Systemd unit type (defaults to forking) # --user - System user to start the service with # --working-directory - Working directory at which to start the service # Returns: # None ######################### generate_systemd_conf() { local -r service_name="${1:?service name is missing}" local -r systemd_units_dir="/etc/systemd/system" local -r service_file="${systemd_units_dir}/bitnami.${service_name}.service" # Default values local name="$service_name" local type="forking" local user="" local group="" local environment="" local environment_file="" local exec_start="" local exec_start_pre="" local exec_start_post="" local exec_stop="" local exec_reload="" local restart="always" local pid_file="" local standard_output="journal" local standard_error="" local limits_content="" local success_exit_status="" local custom_service_content="" local working_directory="" # Parse CLI flags shift while [[ "$#" -gt 0 ]]; do case "$1" in --name \ | --type \ | --user \ | --group \ | --exec-start \ | --exec-stop \ | --exec-reload \ | --restart \ | --pid-file \ | --standard-output \ | --standard-error \ | --success-exit-status \ | --custom-service-content \ | --working-directory \ ) var_name="$(echo "$1" | sed -e "s/^--//" -e "s/-/_/g")" shift declare "$var_name"="${1:?"${var_name} value is missing"}" ;; --limit-*) [[ -n "$limits_content" ]] && limits_content+=$'\n' var_name="${1//--limit-}" shift limits_content+="Limit${var_name^^}=${1:?"--limit-${var_name} value is missing"}" ;; --exec-start-pre) shift [[ -n "$exec_start_pre" ]] && exec_start_pre+=$'\n' exec_start_pre+="ExecStartPre=${1:?"--exec-start-pre value is missing"}" ;; --exec-start-post) shift [[ -n "$exec_start_post" ]] && exec_start_post+=$'\n' exec_start_post+="ExecStartPost=${1:?"--exec-start-post value is missing"}" ;; --environment) shift # It is possible to add multiple environment lines [[ -n "$environment" ]] && environment+=$'\n' environment+="Environment=${1:?"--environment value is missing"}" ;; --environment-file) shift # It is possible to add multiple environment-file lines [[ -n "$environment_file" ]] && environment_file+=$'\n' environment_file+="EnvironmentFile=${1:?"--environment-file value is missing"}" ;; *) echo "Invalid command line flag ${1}" >&2 return 1 ;; esac shift done # Validate inputs local error="no" if [[ -z "$exec_start" ]]; then error "The --exec-start option is required" error="yes" fi if [[ "$error" != "no" ]]; then return 1 fi # Generate the Systemd unit cat > "$service_file" <> "$service_file" <<< "WorkingDirectory=${working_directory}" fi if [[ -n "$exec_start_pre" ]]; then # This variable may contain multiple ExecStartPre= directives cat >> "$service_file" <<< "$exec_start_pre" fi if [[ -n "$exec_start" ]]; then cat >> "$service_file" <<< "ExecStart=${exec_start}" fi if [[ -n "$exec_start_post" ]]; then # This variable may contain multiple ExecStartPost= directives cat >> "$service_file" <<< "$exec_start_post" fi # Optional stop and reload commands if [[ -n "$exec_stop" ]]; then cat >> "$service_file" <<< "ExecStop=${exec_stop}" fi if [[ -n "$exec_reload" ]]; then cat >> "$service_file" <<< "ExecReload=${exec_reload}" fi # User and group if [[ -n "$user" ]]; then cat >> "$service_file" <<< "User=${user}" fi if [[ -n "$group" ]]; then cat >> "$service_file" <<< "Group=${group}" fi # PID file allows to determine if the main process is running properly (for Restart=always) if [[ -n "$pid_file" ]]; then cat >> "$service_file" <<< "PIDFile=${pid_file}" fi if [[ -n "$restart" ]]; then cat >> "$service_file" <<< "Restart=${restart}" fi # Environment flags if [[ -n "$environment" ]]; then # This variable may contain multiple Environment= directives cat >> "$service_file" <<< "$environment" fi if [[ -n "$environment_file" ]]; then # This variable may contain multiple EnvironmentFile= directives cat >> "$service_file" <<< "$environment_file" fi # Logging if [[ -n "$standard_output" ]]; then cat >> "$service_file" <<< "StandardOutput=${standard_output}" fi if [[ -n "$standard_error" ]]; then cat >> "$service_file" <<< "StandardError=${standard_error}" fi if [[ -n "$custom_service_content" ]]; then # This variable may contain multiple miscellaneous directives cat >> "$service_file" <<< "$custom_service_content" fi if [[ -n "$success_exit_status" ]]; then cat >> "$service_file" <> "$service_file" <> "$service_file" <> "$service_file" <