#!/bin/bash # Purpose: set config value ################################################################################ set -e set -u # support testsuite FUNCTIONS="${FUNCTIONS:-/usr/share/ngcp-ngcpcfg/functions/}" HELPER="${HELPER:-/usr/share/ngcp-ngcpcfg/helper/}" SCRIPTS="${SCRIPTS:-/usr/share/ngcp-ngcpcfg/scripts/}" if ! [ -r "${FUNCTIONS}"/main ] ; then printf "Error: %s/main could not be read. Exiting.\n" "${FUNCTIONS}">&2 exit 1 fi # shellcheck disable=SC1090 . "${FUNCTIONS}"/main ## functions {{{ help() { echo "Usage: ngcpcfg set [] " echo "Example: ngcpcfg set /etc/ngcp-config/config.yml general.maintenance=yes" echo "Options:" echo " --diff: show difference(s) for the applied changes" } ## }}} RC=0 b_show_diff=false while [ -n "${1:-}" ]; do case "$1" in --diff) b_show_diff=true shift ;; --*) log_error "unsupported option '$1'. Exiting." help >&2 exit 1 ;; *) break ;; esac done if [ "${#:-}" != "2" ]; then help >&2 exit 1 fi file="$1" data="$2" [ -f "${file}" ] || (log_error "missing ${file}. Exiting." ; exit 1) [ -z "${data}" ] && (log_error "missing data to set. Exiting." ; exit 1) # Check whether we should use different permissions when creating the file. CREATEFILE=no if [ ! -f "${file}" ]; then CREATEFILE=yes if [ "${file}" = "${NETWORK_CONFIG:-}" ]; then MODE="${NETWORK_CONFIG_CHMOD}" USER="${NETWORK_CONFIG_USER}" GROUP="${NETWORK_CONFIG_GROUP}" else MODE="${CONFIG_CHMOD}" USER="${CONFIG_USER}" GROUP="${CONFIG_GROUP}" fi fi log_debug "Received data: ${data}" option="${data%%=*}" value="${data#*=}" log_debug "Parsed option: ${option}" log_debug "Parsed value: ${value}" [ -z "${option}" ] && ( log_error "missing option to set. Exiting." ; exit 1) [ -z "${value}" ] && ( log_error "missing value to set. Exiting." ; exit 1) log_debug "Saving option '${option}' value '${value}' into '${file}'" if [[ ${value} =~ ^\'.*\'$ ]] ; then log_debug "\$value is already quoted." elif [[ ${value} =~ ^[0-9]*$ ]] ; then log_debug "Not quoting \$value for integers." elif [[ ${value} =~ ^\[.*\]$ ]] ; then log_debug "Not quoting \$value for arrays." elif [[ ${value} =~ ^\{.*\}$ ]] ; then log_debug "Not quoting \$value for hashes." else log_debug "Quoting \$value to prevent further Perl errors." value="\"${value}\"" fi tmp=$(mktemp -t ngcpcfg-set.XXXXXXXXXX) log_debug "Temporary perl file: ${tmp}" cat > "${tmp}" << EOF use strict; use warnings; use YAML::XS; my \$file="${file}"; my \$yaml = YAML::XS::LoadFile("\$file"); my \$valref = \\\$yaml; for my \$component (split(/\\./, "${option}")) { if (ref(\$valref) eq 'SCALAR' && defined(\$\$valref)) { print STDERR ("Key resolved to a SCALAR at '\$component'; cannot continue.\n"); exit(1); } elsif (\$component =~ /^\\d+\$/ && (!defined(\$\$valref) || ref(\$\$valref) eq 'ARRAY')) { \$valref = \\\$\$valref->[\$component]; } elsif (\$component eq 'APPEND' && ref(\$\$valref) eq 'ARRAY') { \$valref = \\\$\$valref->[\$#{\$\$valref}+1]; } elsif (!defined(\$\$valref) || ref(\$\$valref) eq 'HASH') { \$valref = \\\$\$valref->{\$component}; } else { print STDERR ("Key resolved to a " . ref(\$\$valref) . " reference; refusing to overwrite.\n"); exit(1); } } if (!defined(\$\$valref) || ref(\$valref) eq 'SCALAR') { \$\$valref = ${value}; } elsif (ref(\$\$valref) eq 'ARRAY' && ref(${value}) eq 'ARRAY') { \$\$valref = ${value}; } elsif (ref(\$\$valref) eq 'HASH' && ref(${value}) eq 'HASH') { \$\$valref = ${value}; } else { print STDERR ("Key resolved to a " . ref(\$\$valref) . " reference; refusing to overwrite.\n"); exit(1); } YAML::XS::DumpFile(\$file, \$yaml); EOF log_debug "perl -wCSD \"${tmp}\" || RC=$?" perl -wCSD "${tmp}" || RC=$? if [ "${CREATEFILE}" = "yes" ]; then chmod "${MODE}" "${file}" chown "${USER}" "${file}" chgrp "${GROUP}" "${file}" fi if [ "${RC}" = "0" ] && "${b_show_diff:-false}"; then log_debug "${SCRIPTS}/diff || true" "${SCRIPTS}"/diff || true fi if [ -n "${DEBUG:-}" ] ; then log_debug "Not removing temporary file ${tmp}" else rm -f "${tmp}" fi exit ${RC} ## END OF FILE #################################################################