#!/bin/bash # ------- # File: llxcfg-config # Description: tool to manage llxcfg conffiles # Author: Luis Garcia Gisbert # (written for LliureX in June 2007) # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA # -------- set -e PATH="/usr/sbin:/usr/bin:/sbin:/bin" # library LLX_COMMONLIB="/usr/share/lliurex/llxcfg-common/llxcfg-common.sh" [ -f "${LLX_COMMONLIB}" ] || exit 1 . ${LLX_COMMONLIB} # variables # --------- declare -a CONF_DIRS declare -a CONF_LIST # more priority first CONF_DIRS=("$LCFG_ADMIN_DIR" "$LCFG_PROGS_DIR" "$LCFG_STATIC_DIR") BINARY="Y" UNIQUE="" SORT="" NO_NEWLINES="" # to store non-opt arguments declare -a ARGV # Bash arrays are 0-based, but we like ARGC to be equal to the number of non-option arguments. # To solve this issue, we use a "C-like" ARGV vector by setting ARGV[0] with the name of script # ('basename' of script for arbitrary cosmetic reasons), so real arguments begin with index 1 # the array will be ARGC+1 elements ARGC=0 ARGV[0]="$(basename "$0")" cfg_test0(){ CFILE="$1" if [ -r "$CFILE" -a -s "$CFILE" ] ; then return 0 # TODO Add more tests to guess the nature of the file ... # file "$CFILE" |grep -q " text$\| empty$\| (no magic)$" && return 0 fi return 1 } cfg_test(){ [ -r "$1" ] || return 1 return 0 } get_relnames(){ # find files, test for type "text" and remove pathname (with no loops) CFG_DIR="$1" if [ -d "${CFG_DIR}" ] ; then if [ -z "$BINARY" ] ; then file $(find "${CFG_DIR}" -xtype f) 2>/dev/null |sed -ne "/ text$\| empty$\| (no magic)$/{s%:.*$%%;s%^${CFG_DIR}%%p}" || true else find "${CFG_DIR}" -xtype f -printf "/%P\n" fi fi return 0 } list_cfg_dir(){ REL_DIR="$1" CFG_ACTION="$2" if [ "$REL_DIR" ] ; then REL_DIR="/$(echo "${REL_DIR}" |sed -e "s%/$%%;s%^/%%")" fi # create a tmp file with the list of files in every CONF_DIR level # in each list, the preceding lists (in the CONF_DIR hierarchy) are removed # # more utils power: # 'sponge' allows pipes that read from and write to the same file, avoiding the use of 2 tempfiles # 'combine' merges files with a more readable syntax than an equivalent diff/grep/sed pipeline i=0 while [ $i -le ${#CONF_DIRS[@]} ] ; do j=0 TMPFILE="$(tempfile)" get_relnames "${CONF_DIRS[$i]}${REL_DIR}" > "$TMPFILE" j=0 while [ $j -lt $i ] ; do [ -s "${CONF_LIST[$j]}" ] && combine "$TMPFILE" not "${CONF_LIST[$j]}" |sponge "$TMPFILE" j=$(( $j + 1 )) done CONF_LIST[$i]="$TMPFILE" i=$(( $i + 1 )) done # add full path to file lists i=0 while [ $i -le ${#CONF_DIRS[@]} ] ; do PRE_PATH="${CONF_DIRS[$i]}${REL_DIR}" TMPFILE="${CONF_LIST[$i]}" if [ -s "$TMPFILE" ] ; then case "$CFG_ACTION" in cat) cat $(sed -e "s%^%${PRE_PATH}%" "${TMPFILE}") ;; relative) cat "${TMPFILE}" ;; *) sed -e "s%^%${PRE_PATH}%" "${TMPFILE}" ;; esac fi rm -f "$TMPFILE" i=$(( $i + 1 )) done return 0 } list_cfg_filter(){ FILTER="" FILTER2="" if [ "$SORT" ] ; then FILTER="sort" fi if [ "$UNIQUE" ] ; then FILTER="sort -u" fi if [ "$NO_NEWLINES" ] ; then FILTER2="tr '\n' ' '" fi if [ "$FILTER" -a "$NO_NEWLINES" ] ; then list_cfg_dir "$@" | $FILTER | tr '\n' ' ' elif [ "$FILTER" ] ; then list_cfg_dir "$@" | $FILTER elif [ "$NO_NEWLINES" ] ; then list_cfg_dir "$@" | tr '\n' ' ' else list_cfg_dir "$@" fi } exit_message(){ echo -e "$1" >&2 exit 1 } usage() { CMD_NAME="$(basename "$0")" exit_message \ "Usage: $CMD_NAME [--binary|--text] {read|write|delete|filename} (RELATIVE)CONFFILE\n\ $CMD_NAME [--binary|--text] [--unique|--sort] [ --no-newlines] dump (RELATIVE)CONFDIR\n\ $CMD_NAME [--binary|--text] [--unique|--sort] [ --no-newlines] {list|listfiles} [(RELATIVE)CONFDIR]" } # ---- # main # ---- while [ $# -ge 1 ] ; do case "$1" in --text|-t) BINARY="" ;; --binary|-b) BINARY="Y" ;; --sort|-s) SORT="Y" ;; --unique|-u) UNIQUE="Y" ;; --no-newlines|-n) NO_NEWLINES="Y" ;; read|write|delete|filename|dump|list|listfiles) ACTION="$1" ;; -*) # an invalid option -* or --* or a single - usage ;; *) ARGC=$(( $ARGC + 1 )) ARGV[$ARGC]="$1" ;; esac shift done [ -z "$ACTION" ] && usage [ $ARGC -gt 1 ] && usage case "$ACTION" in read|filename) [ $ARGC -lt 1 ] && usage CONFFILE="${ARGV[1]}" i=0 rc=1 while [ $i -le ${#CONF_DIRS[@]} ] ; do CFG_FILE="${CONF_DIRS[$i]}/${CONFFILE}" if cfg_test "${CFG_FILE}" ; then if [ "$ACTION" = "read" ] ; then cat "${CFG_FILE}" && rc=0 else rc=0 echo "${CFG_FILE}" fi break fi i=$(( $i + 1 )) done exit $rc ;; write) [ $ARGC -lt 1 ] && usage CONFFILE="${ARGV[1]}" # we only write files at "LCFG_PROGS_DIR" TEXT_OPTION="--binary" [ -z "$BINARY" ] && TEXT_OPTION="--text" llxcfg-install --mode=${LLXCFG_MODE} --group=${LLXCFG_GROUP} ${TEXT_OPTION} - "${LCFG_PROGS_DIR}/${CONFFILE}" || exit_message "Error writing config file \"${LCFG_PROGS_DIR}/${CONFFILE}\"" ;; delete) [ $ARGC -lt 1 ] && usage # we only delete files at "LCFG_PROGS_DIR" CONFFILE="${LCFG_PROGS_DIR}/${ARGV[1]}" rm -f "$CONFFILE" ;; dump) [ $ARGC -lt 1 ] && usage CONFDIR="${ARGV[1]}" list_cfg_filter "$CONFDIR" "cat" ;; list) CONFDIR="${ARGV[1]}" list_cfg_filter "$CONFDIR" "relative" ;; listfiles) CONFDIR="${ARGV[1]}" list_cfg_filter "$CONFDIR" ;; *) usage ;; esac exit 0