summaryrefslogtreecommitdiffstats
path: root/src/bin/keactrl/keactrl.in
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/keactrl/keactrl.in')
-rw-r--r--src/bin/keactrl/keactrl.in599
1 files changed, 599 insertions, 0 deletions
diff --git a/src/bin/keactrl/keactrl.in b/src/bin/keactrl/keactrl.in
new file mode 100644
index 0000000..15fe4da
--- /dev/null
+++ b/src/bin/keactrl/keactrl.in
@@ -0,0 +1,599 @@
+#!/bin/sh
+
+# Copyright (C) 2014-2022 Internet Systems Consortium, Inc. ("ISC")
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This is keactrl script responsible for starting up Kea processes.
+# This script is used to run Kea from installation directory,
+# as well as for running tests.
+
+# shellcheck disable=SC2034
+# SC2034: ... appears unused. Verify use (or export if used externally).
+
+# shellcheck disable=SC2039
+# SC2039: In POSIX sh, 'local' is undefined.
+
+# shellcheck disable=SC2154
+# SC2154: ... is referenced but not assigned.
+# Reason: some variables are taken from keactrl.conf
+
+# Exit with error if commands exit with non-zero and if undefined variables are
+# used.
+set -eu
+
+VERSION="@PACKAGE_VERSION@"
+
+# Set the have_netconf flag to know if netconf is available.
+if test -z '@HAVE_SYSREPO_TRUE@'; then
+ have_netconf=true
+else
+ have_netconf=false
+fi
+
+### Logging functions ###
+
+# Logs message at the error level.
+log_error() {
+ printf "ERROR/keactrl: %s\n" "${1}"
+}
+
+# Logs message at the warning level.
+log_warning() {
+ printf "WARNING/keactrl: %s\n" "${1}"
+}
+
+# Logs message at the info level.
+log_info() {
+ printf "INFO/keactrl: %s\n" "${1}"
+}
+
+### Convenience functions ###
+
+# Checks if the value is in the list. An example usage of this function
+# is to determine whether the keactrl command belongs to the list of
+# supported commands.
+is_in_list() {
+ local member="${1-}" # Value to be checked
+ local list="${2-}" # Comma separated list of items
+ _inlist=0 # Return value: 0 if not in list, 1 otherwise.
+ if [ -z "${member}" ]; then
+ log_error "missing ${member}"
+ fi
+ # Iterate over all items on the list and compare with the member.
+ # If they match, return, otherwise log error and exit.
+ for item in ${list}
+ do
+ if [ "${item}" = "${member}" ]; then
+ _inlist=1
+ return
+ fi
+ done
+}
+
+# Prints keactrl usage.
+usage() {
+ printf "usage is %s command [-c keactrl-config-file] [-s server[,server,..]]\n" \
+ "$(basename -- "${0}")"
+ printf "commands: start stop reload status version\n"
+}
+
+### Functions managing Kea processes ###
+# Constructs a server's PID file based on its binary name, the config file,
+# and the --localstatedir and returns the contents as $_pid. If the file
+# does not exist, the value of $_pid is 0. If the file exists but cannot
+# be read the function exists with a error message. Note the PID file name
+# is always returned in $_pid_file.
+
+# There are some variables set in /etc/kea/keactrl.conf that's included here.
+# Since we run shellcheck against keactrl.in rather than an installed file,
+# we get false warnings about the variable being referenced but not assigned.
+get_pid_from_file() {
+ local proc_name="${1}" # Process name.
+
+ local kea_config_file=
+ case ${proc_name} in
+ kea-dhcp4)
+ kea_config_file=${kea_dhcp4_config_file}
+ ;;
+ kea-dhcp6)
+ kea_config_file=${kea_dhcp6_config_file}
+ ;;
+ kea-dhcp-ddns)
+ kea_config_file=${kea_dhcp_ddns_config_file}
+ ;;
+ kea-ctrl-agent)
+ kea_config_file=${kea_ctrl_agent_config_file}
+ ;;
+ kea-netconf)
+ kea_config_file=${kea_netconf_config_file}
+ ;;
+ esac
+
+ # Extract the name portion (from last slash to last dot) of the config file name
+ # File name and extension are documented in src/lib/util/filename.h
+ local conf_name
+ conf_name=$(basename -- "${kea_config_file}" | rev | cut -f2- -d'.' | rev)
+
+ # Default the directory to --localstatedir / run
+ local pid_file_dir
+ pid_file_dir="@runstatedir@/@PACKAGE@"
+
+ # Use directory override if set (primarily for testing only)
+ if test -n "${KEA_PIDFILE_DIR+x}"; then
+ pid_file_dir=${KEA_PIDFILE_DIR}
+ fi
+
+ # construct the PID file name
+ _pid_file="${pid_file_dir}/${conf_name}.${proc_name}.pid"
+
+ # Grab the PID if the file exists
+ _pid=$(cat "${_pid_file}" 2> /dev/null || true)
+ if test -z "${_pid}"; then
+ # No file, means no pid
+ _pid=0;
+ fi
+}
+
+
+# Checks if the specified process is running by reading its
+# PID file and checking the PID it contains. If the file does
+# not exist, the process is assumed to not be running.
+check_running() {
+ local proc_name="${1}" # Process name.
+ # Initially mark the process as not running.
+ _running=0
+
+ # Get the PID from the PID file (if it exists)
+ get_pid_from_file "${proc_name}"
+ if [ ${_pid} -gt 0 ]; then
+ # Use ps to check if PID is alive
+ if ps -p ${_pid} 1>/dev/null; then
+ # No error, so PID IS ALIVE
+ _running=1
+ fi
+ fi
+}
+
+# Sends a signal to a process based on its PID file
+send_signal() {
+ local sig="${1}" # Signal number
+ local proc_name="${2}" # Process name.
+
+ get_pid_from_file "${proc_name}"
+ if [ "${_pid}" -eq 0 ]; then
+ log_info "Skip sending signal ${sig} to process ${proc_name}: \
+process is not running"
+ else
+ if ! kill "-${sig}" "${_pid}"; then
+ log_error "Failed to send signal ${sig} to process ${proc_name}, PID {$_pid}."
+ fi
+ fi
+}
+
+# Start the Kea process. Do not start the process if there is an instance
+# already running.
+start_server() {
+ binary_path=${1} # Full path to the binary.
+ # Extract the name of the binary from the path.
+ local binary_name
+ binary_name=$(basename -- "${binary_path}")
+ # Use the binary name to check if the process is already running.
+ check_running "${binary_name}"
+ # If process is running, don't start another one. Just log a message.
+ if [ "${_running}" -ne 0 ]; then
+ log_info "${binary_name} appears to be running, see: \
+PID ${_pid}, PID file: ${_pid_file}."
+ else
+ log_info "Starting ${*}"
+ # Start the process.
+ "${@}" &
+ fi
+}
+
+# Instruct Kea process to shutdown by sending it signal 15
+stop_server() {
+ binary_path=${1} # Full path to the binary.
+ local sig=15
+ # Extract the name of the binary from the path.
+ local binary_name
+ binary_name=$(basename -- "${binary_path}")
+
+ # Use the binary name to check if the process is already running.
+ check_running "${binary_name}"
+ # If process isn't running, don't start another one. Just log a message.
+ if [ "${_running}" -eq 0 ]; then
+ log_info "${binary_name} isn't running."
+ else
+ log_info "Stopping ${binary_name}..."
+ if ! kill "-${sig}" "${_pid}"; then
+ log_error "Stop failed, could not send signal ${sig} \
+to process ${proc_name}, PID ${_pid}."
+ fi
+ fi
+}
+
+# Instruct Kea process to reload config by sending it signal 1
+reload_server() {
+ binary_path=${1} # Full path to the binary.
+ local sig=1
+ # Extract the name of the binary from the path.
+ local binary_name
+ binary_name=$(basename -- "${binary_path}")
+
+ # Use the binary name to check if the process is already running.
+ check_running "${binary_name}"
+ # If process isn't running, don't start another one. Just log a message.
+ if [ "${_running}" -eq 0 ]; then
+ log_info "${binary_name} isn't running."
+ else
+ log_info "Reloading ${binary_name}..."
+ if ! kill "-${sig}" "${_pid}"; then
+ log_error "Reload failed, could not send signal ${sig} \
+to process ${proc_name}, PID ${_pid}."
+ fi
+ fi
+}
+
+# Print Kea daemon version
+print_version() {
+ name=${1}
+ binary_path=${2}
+
+ if [ -e "${binary_path}" ]; then
+ if ! ver=$(${binary_path} -v); then
+ log_error "Error checking version of binary file: ${binary_path}"
+ fi
+ else
+ # No file, means no pid
+ ver="unknown, ${binary_path} missing";
+ fi
+
+ echo "${name}: ${ver}"
+}
+
+### Functions testing the existence of the Kea config file
+
+# Check if the Kea configuration file location has been specified in the
+# keactrl configuration file. If not, it is a warning or a fatal error.
+check_kea_conf() {
+ local conf_file="${1-}" # Kea config file name.
+ if [ -z "${conf_file}" ]; then
+ log_error "Configuration file for Kea not specified."
+ exit 1
+ elif [ ! -f "${conf_file}" ]; then
+ log_error "Configuration file for Kea does not exist: ${conf_file}."
+ exit 1
+ fi
+}
+
+# Run the specified command if the server has been enabled.
+# In order for the command to run, the following conditions have to be met:
+# - server must be on the list of servers (e.g. specified from command line)
+# or servers must contain all
+# - if check_file_cfg is non zero, the server must be enabled in the
+# configuration file, so the variable named after server name should exist
+# and be set to yes, e.g. ${dhcp4} should be equal to yes if server name
+# is dhcp4
+run_conditional() {
+ local server="${1}" # Server name: dhcp4, dhcp6, dhcp_ddns, ctrl_agent, netconf
+ local commands="${2}" # Commands to execute
+ local check_file_cfg="${3}" # Check if server enabled in the configuration file
+ local is_all=0 # is all servers or a specific one
+
+ # If keyword "all" is not on the list of servers we will have to check
+ # if our specific server is on the list. If, not return.
+ is_in_list "all" "${servers}"
+ if [ "${_inlist}" -eq 0 ]; then
+ is_in_list "${server}" "${servers}"
+ if [ "${_inlist}" -eq 0 ]; then
+ return
+ fi
+ else
+ is_all=1
+ fi
+ # Return for for netconf when not available.
+ if [ "${server}" = "netconf" ]; then
+ if ! ${have_netconf}; then
+ return
+ fi
+ # reload is not supported for netconf.
+ if [ "${command}" = "reload" ]; then
+ if [ "${is_all}" -eq 1 ]; then
+ return
+ fi
+ log_warning "netconf does not support reload"
+ return
+ fi
+ fi
+
+ # Get the configuration value of the keactrl which indicates whether
+ # the server should be enabled or not. Variables that hold these values
+ # are: ${dhcp4}, ${dhcp6}, ${dhcp_ddns}.
+ local file_config
+ file_config=$( eval printf "%s" "\${$server}" )
+ # Run the commands if we ignore the configuration setting or if the
+ # setting is "yes".
+ if [ "${check_file_cfg}" -eq 0 ] || [ "${file_config}" = "yes" ]; then
+ ${commands}
+ fi
+}
+
+### Script starts here ###
+
+# Configure logger to log messages into the file.
+# Do not set destination if the KEA_LOGGER_DESTINATION is set,
+# because a unit test could have set this to some other location.
+# Note that when the configuration is applied this location may be
+# altered and only the handful of initial messages will be logged
+# to the default file.
+if [ -z "${KEA_LOGGER_DESTINATION+x}" ]; then
+ prefix="@prefix@"
+ export KEA_LOGGER_DESTINATION="@localstatedir@/log/kea.log"
+fi
+
+command=${1-}
+if [ -z "${command}" ]; then
+ log_error "missing command"
+ usage
+ exit 1
+fi
+
+# Check if this is a simple question about version.
+if test "${command}" = "-v" || test "${command}" = "--version" ; then
+ echo "${VERSION}"
+ exit 0
+fi
+
+is_in_list "${command}" "start stop reload status version"
+if [ "${_inlist}" -eq 0 ]; then
+ log_error "invalid command: ${command}"
+ exit 1
+fi
+
+# Get the location of the keactrl configuration file.
+prefix="@prefix@"
+localstatedir="@localstatedir@"
+keactrl_conf="@sysconfdir@/@PACKAGE@/keactrl.conf"
+
+servers="all"
+
+shift
+while test ${#} -gt 0
+do
+ option=${1}
+ case ${option} in
+ # Override keactrl configuration file.
+ -c|--ctrl-config)
+ shift
+ keactrl_conf=${1-}
+ if [ -z "${keactrl_conf}" ]; then
+ log_error "keactrl-config-file not specified"
+ usage
+ exit 1
+ fi
+ ;;
+ # Get the specific servers for which the command will be
+ # executed.
+ -s|--server)
+ shift
+ servers=$(printf '%s' "${1-}" | tr ',' '\n')
+ if [ -z "${servers}" ]; then
+ log_error "servers not specified"
+ usage
+ exit 1
+ fi
+ # Validate that the specified server names are correct.
+ for s in ${servers}
+ do
+ server_list="all dhcp4 dhcp6 dhcp_ddns ctrl_agent"
+ if ${have_netconf}; then
+ server_list="${server_list} netconf"
+ fi
+ is_in_list "${s}" "${server_list}"
+ if [ "${_inlist}" -eq 0 ]; then
+ log_error "invalid server name: ${s}"
+ exit 1
+ fi
+ done
+ ;;
+ *)
+ log_error "invalid option: ${option}"
+ usage
+ exit 1
+ esac
+ shift
+done
+
+# Check if the file exists. If it doesn't, it is a fatal error.
+if [ ! -f "${keactrl_conf}" ]; then
+ log_error "keactrl configuration file doesn't exist in ${keactrl_conf}."
+ exit 1
+fi
+
+# Include the configuration file.
+# Shellcheck complaints about not being to follow the source. Let's ingore it.
+# shellcheck disable=SC1090
+. "${keactrl_conf}"
+
+# Get location of the DHCPv4 server binary.
+if [ -z "${dhcp4_srv+x}" ]; then
+ log_error "dhcp4_srv parameter not specified"
+ exit 1
+fi
+
+# Get location of the DHCPv6 server binary.
+if [ -z "${dhcp6_srv+x}" ]; then
+ log_error "dhcp6_srv parameter not specified"
+ exit 1
+fi
+
+# Get location of the DHCP DDNS server binary.
+if [ -z "${dhcp_ddns+x}" ]; then
+ log_error "dhcp_ddns parameter not specified"
+ exit 1
+fi
+
+# Get location of the Control Agent binary.
+if [ -z "${ctrl_agent_srv+x}" ]; then
+ log_error "ctrl_agent_srv parameter not specified"
+ exit 1
+fi
+
+# Get location of the Netconf binary.
+if ${have_netconf}; then
+ if [ -z "${netconf_srv+x}" ]; then
+ log_error "netconf_srv parameter not specified"
+ exit 1
+ fi
+fi
+
+# dhcp4 and dhcp6 (=yes) indicate if we should start DHCPv4 and DHCPv6 server
+# respectively. The same is true for ddns, ctrl-agent and netconf.
+dhcp4=$( printf "%s" "${dhcp4}" | tr '[:upper:]' '[:lower:]' )
+dhcp6=$( printf "%s" "${dhcp6}" | tr '[:upper:]' '[:lower:]' )
+dhcp_ddns=$( printf "%s" "${dhcp_ddns}" | tr '[:upper:]' '[:lower:]' )
+ctrl_agent=$( printf "%s" "${ctrl_agent}" | tr '[:upper:]' '[:lower:]' )
+if ${have_netconf}; then
+ netconf=$( printf "%s" "${netconf}" | tr '[:upper:]' '[:lower:]' )
+fi
+
+case ${command} in
+ # Start the servers.
+ start)
+ args=""
+ # kea_verbose is set in keactrl.conf that shellcheck is unable to load.
+ if [ "${kea_verbose}" = "yes" ]; then
+ args="-d"
+ fi
+
+ # Run servers if they are on the list of servers from the command line
+ # and if they are enabled in the keactrl configuration file.
+ # The variables (dhcp4_srv, dhcp6_serv, dhcp_ddns_srv etc) are set in the
+ # keactrl.conf file that shellcheck is unable to read.
+ run_conditional "dhcp4" "start_server ${dhcp4_srv} -c ${kea_dhcp4_config_file} ${args}" 1
+ run_conditional "dhcp6" "start_server ${dhcp6_srv} -c ${kea_dhcp6_config_file} ${args}" 1
+ run_conditional "dhcp_ddns" "start_server ${dhcp_ddns_srv} -c ${kea_dhcp_ddns_config_file} \
+${args}" 1
+ run_conditional "ctrl_agent" "start_server ${ctrl_agent_srv} -c ${kea_ctrl_agent_config_file} \
+${args}" 1
+ if ${have_netconf}; then
+ run_conditional "netconf" "start_server ${netconf_srv} -c ${kea_netconf_config_file} \
+${args}" 1
+ fi
+
+ exit 0 ;;
+
+ # Stop running servers.
+ stop)
+ # Stop all servers or servers specified from the command line.
+ run_conditional "dhcp4" "stop_server ${dhcp4_srv}" 0
+ run_conditional "dhcp6" "stop_server ${dhcp6_srv}" 0
+ run_conditional "dhcp_ddns" "stop_server ${dhcp_ddns_srv}" 0
+ run_conditional "ctrl_agent" "stop_server ${ctrl_agent_srv}" 0
+ if ${have_netconf}; then
+ run_conditional "netconf" "stop_server ${netconf_srv}" 0
+ fi
+
+ exit 0 ;;
+
+ # Reconfigure the servers.
+ reload)
+ # Reconfigure all servers or servers specified from the command line.
+ run_conditional "dhcp4" "reload_server ${dhcp4_srv}" 0
+ run_conditional "dhcp6" "reload_server ${dhcp6_srv}" 0
+ run_conditional "dhcp_ddns" "reload_server ${dhcp_ddns_srv}" 0
+ run_conditional "ctrl_agent" "reload_server ${ctrl_agent_srv}" 0
+ if ${have_netconf}; then
+ run_conditional "netconf" "reload_server ${netconf_srv}" 0
+ fi
+
+ exit 0 ;;
+
+ status)
+ if [ -t 1 ]; then
+ inactive="\033[91minactive\033[0m"
+ active="\033[92mactive\033[0m"
+ else
+ inactive="inactive"
+ active="active"
+ fi
+
+ kea4_status=$inactive
+ # This case of nested double quotes looks confusing, but it is actually
+ # correct. For details, see this fine explanation:
+ # https://unix.stackexchange.com/questions/443989/whats-the-right-way-to-quote-command-arg
+ check_running "$(basename -- "${dhcp4_srv}")"
+ if [ "${_running}" -eq 1 ]; then
+ kea4_status=$active
+ fi
+ printf "DHCPv4 server: %b\n" "${kea4_status}"
+
+ kea6_status=$inactive
+ check_running "$(basename -- "${dhcp6_srv}")"
+ if [ "${_running}" -eq 1 ]; then
+ kea6_status=$active
+ fi
+ printf "DHCPv6 server: %b\n" "${kea6_status}"
+
+ d2_status=$inactive
+ check_running "$(basename -- "${dhcp_ddns_srv}")"
+ if [ "${_running}" -eq 1 ]; then
+ d2_status=$active
+ fi
+ printf "DHCP DDNS: %b\n" "${d2_status}"
+
+ agent_status=$inactive
+ check_running "$(basename -- "${ctrl_agent_srv}")"
+ if [ "${_running}" -eq 1 ]; then
+ agent_status=$active
+ fi
+ printf "Control Agent: %b\n" "${agent_status}"
+
+ if ${have_netconf}; then
+ netconf_status=$inactive
+ check_running "$(basename -- "${netconf_srv}")"
+ if [ "${_running}" -eq 1 ]; then
+ netconf_status=$active
+ fi
+ printf "Netconf agent: %b\n" "${netconf_status}"
+ fi
+
+ printf "Kea DHCPv4 configuration file: %s\n" "${kea_dhcp4_config_file}"
+ printf "Kea DHCPv6 configuration file: %s\n" "${kea_dhcp6_config_file}"
+ printf "Kea DHCP DDNS configuration file: %s\n" "${kea_dhcp_ddns_config_file}"
+ printf "Kea Control Agent configuration file: %s\n" "${kea_ctrl_agent_config_file}"
+ if ${have_netconf}; then
+ printf "Kea Netconf configuration file: %s\n" "${kea_netconf_config_file}"
+ fi
+ printf "keactrl configuration file: %s\n" "${keactrl_conf}"
+
+ check_kea_conf "${kea_dhcp4_config_file}"
+ check_kea_conf "${kea_dhcp6_config_file}"
+ check_kea_conf "${kea_dhcp_ddns_config_file}"
+ check_kea_conf "${kea_ctrl_agent_config_file}"
+ if ${have_netconf}; then
+ check_kea_conf "${kea_netconf_config_file}"
+ fi
+
+ exit 0 ;;
+
+ version)
+ echo "keactrl: ${VERSION}"
+ run_conditional "dhcp4" "print_version kea-dhcp4 ${dhcp4_srv}" 0
+ run_conditional "dhcp6" "print_version kea-dhcp6 ${dhcp6_srv}" 0
+ run_conditional "dhcp_ddns" "print_version kea-dhcp-ddns ${dhcp_ddns_srv}" 0
+ run_conditional "ctrl_agent" "print_version kea-ctrl-agent ${ctrl_agent_srv}" 0
+ if ${have_netconf}; then
+ run_conditional "netconf" "print_version kea-netconf ${netconf_srv}" 0
+ fi
+
+ exit 0 ;;
+
+ # No other commands are supported.
+ *)
+ log_error "Invalid command: ${command}."
+ exit 1 ;;
+esac