Files
init.sh/lib/filefct.sh
2023-08-02 11:36:01 +02:00

351 lines
9.9 KiB
Bash

#!/bin/bash
# ------------------------------------------------------------------------------
# File manipulation function
# This file is part of the init.sh project
# Copyright (c) 2019-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
# ------------------------------------------------------------------------------
# This file is distributed under 3-clause BSD license.
# The complete license agreement can be obtained at:
# https://opensource.org/licenses/BSD-3-Clause
# ------------------------------------------------------------------------------
# Some useful variables:
export HOST_REPO_PATH=${HOST_REPO_PATH:-"$MYPATH/repo/hosts/$HOSTNAME"}
export REALM_REPO_PATH=${REALM_REPO_PATH:-"$MYPATH/repo/realms/$REALM"}
export GROUP_REPO_PATH=${GROUP_REPO_PATH:-"$MYPATH/repo/groups/$GROUPNAME"}
export RLMGRP_REPO_PATH=${RLMGRP_REPO_PATH:-"$MYPATH/repo/realms/$REALM/groups/$GROUPNAME"}
export RLMHST_REPO_PATH=${RLMHST_REPO_PATH:-"$MYPATH/repo/realms/$REALM/hosts/$HOSTNAME"}
export COMM_REPO_PATH=${COMM_REPO_PATH:-"$MYPATH/repo/common"}
# ------------------------------------------------------------------------------
# Backup original installation files
# (or any old files if runned several time on same file)
backup_dist()
{
if [[ $# -lt 1 ]]; then
prnt E "backup_dist(): At least one argument is required."
exit 11
fi
local file=
for file in $@; do
local tmstmp=$(stdtime)
if [[ -L ${file} ]]; then
# With symbolik links we call again backup_dist to treat target
prnt I "Following the symbolic link $file to do a proper backup..."
backup_dist $(readlink -f "${file}")
elif [[ -f ${file} ]]; then
prnt I "Creating a backup of ${file} on $tmstmp..."
cp -av $file ${file}.dist.${tmstmp}
if [[ $? -ne 0 ]]; then
prnt E "backup_dist(): Failed copying file."
die 12
fi
elif [[ -d ${file} ]]; then
prnt I "Creating a backup of the directory ${file} on $tmstmp..."
cp -av $file ${file}.dist.${tmstmp}
if [[ $? -ne 0 ]]; then
prnt E "backup_dist(): Failed copying directory recursively."
die 12
fi
else
prnt W "backup_dist(): $file don't exists, nothing to do."
fi
unset tmstmp
done
unset file
}
export -f backup_dist
# ------------------------------------------------------------------------------
# Select source file according to our priority mechanism
# 1) repo/<realm>/hosts/<hostname>/ => specific hostname in specific realm
# 2) repo/<realm>/groups/<groupname>/ => specific group in specific realm
# 3) repo/hosts/<hostname>/ => specific hostname in any realm
# 4) repo/groups/<groupname>/ => specific group in any realm
# 5) repo/<realm>/ => any hostname in specific realm
# 6) repo/common => any hostname any realm
# Note: if a specific hostname is provided it makes unecessary to find a group.
select_file()
{
local infile=$1
if [[ -f $RLMHST_REPO_PATH/$infile ]]; then
local source="$RLMHST_REPO_PATH/$infile"
elif [[ -f $RLMGRP_REPO_PATH/$infile ]]; then
local source="$RLMGRP_REPO_PATH/$infile"
elif [[ -f $HOST_REPO_PATH/$infile ]]; then
local source="$HOST_REPO_PATH/$infile"
elif [[ -f $GROUP_REPO_PATH/$infile ]]; then
local source="$GROUP_REPO_PATH/$infile"
elif [[ -f $REALM_REPO_PATH/$infile ]]; then
local source="$REALM_REPO_PATH/$infile"
elif [[ -f $COMM_REPO_PATH/$infile ]]; then
local source="$COMM_REPO_PATH/$infile"
else
# Not found in repository, we expect full name
local source="$infile"
fi
unset infile
echo $source
unset source
}
export -f select_file
# ------------------------------------------------------------------------------
# Select source directory according to our priority mechanism (same as above)
select_directory()
{
local indir=$1
if [[ -d $RLMHST_REPO_PATH/$indir ]]; then
local source="$RLMHST_REPO_PATH/$indir"
elif [[ -d $RLMGRP_REPO_PATH/$indir ]]; then
local source="$RLMGRP_REPO_PATH/$indir"
elif [[ -d $HOST_REPO_PATH/$indir ]]; then
local source="$HOST_REPO_PATH/$indir"
elif [[ -d $GROUP_REPO_PATH/$indir ]]; then
local source="$GROUP_REPO_PATH/$indir"
elif [[ -d $REALM_REPO_PATH/$indir ]]; then
local source="$REALM_REPO_PATH/$indir"
elif [[ -d $COMM_REPO_PATH/$indir ]]; then
local source="$COMM_REPO_PATH/$indir"
else
# Not found in repository, we expect full name
local source="$indir"
fi
unset indir
echo $source
unset source
}
export -f select_directory
# ------------------------------------------------------------------------------
# Install file to the host (specific first then general)
# Todo: implement wildcard support
install_file()
{
local filelist=""
local i=0
if [[ $# -lt 2 ]]; then
prnt E "install_file(): At least two arguments are required."
die 11
fi
if [[ -n $(echo $@ | grep "\*\|\?") ]]; then
prnt E "install_file(): Wildcards are not authorized."
die 7
fi
local arg=
for arg in $@; do
filelist="$filelist $(select_file $arg)"
# We always replace until the last argument being the target
target="$arg"
done
unset arg
if [[ ! $target == /* ]]; then
prnt E "install_file(): Target must be on the root filesystem and full path must be provided."
die 13
fi
unset target
if [[ -d $(dirname $i) ]]; then
prnt I "Creating required target directory $(dirname $i)..."
mkdir -pv $(dirname $i)
if [[ $? -ne 0 ]]; then
prnt E "install_file(): Can't create target directory!"
die 12
fi
fi
prnt I "Copying files ${filelist} to target directory $(dirname $i)..."
cp -av $filelist
if [[ $? -ne 0 ]]; then
prnt E "install_file(): Couldn't copy some required files!"
die 12
fi
}
export -f install_file
# ------------------------------------------------------------------------------
# Add the content of a file at the end of an other
append_file()
{
if [[ $# -ne 2 ]]; then
prnt E "append_file(): Two arguments are required, source and destination."
die 11
fi
local srcfile=$(select_file $1)
local dstfile=$2
if [[ ! $dstfile == /* ]]; then
prnt E "append_file(): Target must be on the root filesystem and full path must be provided."
die 13
fi
if [[ -e $dstfile ]]; then
prnt E "append_file(): Target file must exist (use touch first to create it if required)."
die 13
fi
prnt I "Adding content to file $dstfile..."
cat $srcfile >> $dstfile
if [[ $? -ne 0 ]]; then
prnt E "append_file(): Couldn't append a file!"
die 12
fi
}
export -f append_file
# ------------------------------------------------------------------------------
# determine if a directory is empty
is_dir_empty()
{
dir=$1
if [[ -f $dir ]]; then
prnt E "is_dir_empty(): The given parameter is not a directory."
die 15
fi
if [[ ! -d $dir ]]; then
return 0
fi
nbfiles=$(ls -a1 $dir | grep -Evc '^.$|^..$')
if [[ $nbfiles -eq 0 ]]; then
return 0
fi
return 1
}
export -f is_dir_empty
# ------------------------------------------------------------------------------
# copy and patch a file replacing all @var@ by the corresponding value in
# the environment or the variable list given in parameter
patch_file()
{
local srcfile=$(select_file $1) && shift
local dstfile=$1 && shift
local workfile=${dstfile}.work
if [[ ! -s $srcfile ]]; then
prnt E "patch_file(): Source file is empty, is not a file or don't exists!"
die 10
fi
# Create a sub-process, to avoid bash environment pollution
(
local varlist='' pattern=''
if [[ $# -eq 0 ]] ; then
pattern="-e s/<\(.*\)>/\$\1\$\1/g"
else
local var=
for var in $* ; do
if ! declare -p $var >/dev/null 2>&1 ; then
local $var=$(eval echo \$$var)
fi
pattern="$pattern -e s/@$var@/\$$var/g"
varlist=$varlist\$$var
done
fi
# sed replace <VAR> with \$$VAR and envsubst do the replace by value
sed $pattern $srcfile | envsubst ${varlist:+"$varlist"} > "$workfile"
)
local -a rights=( $(stat --printf="%a %u %g" "$srcfile") )
unset srcfile
mv "$workfile" "$dstfile"
chmod ${rights[0]} "$dstfile"
chown ${rights[1]}:${rights[2]} "$dstfile"
unset rights dstfile
}
export -f patch_file
# ------------------------------------------------------------------------------
# Put a small header in a file showing it have been automatically modified
tag_file()
{
for f in $@; do
local text="# File automatically modified by init.sh on $(stdtime)."
if [[ -e $f ]]; then
sed -i "1s/^/$text\n/" $f
else
echo $text > $f
sed -i -e "s/modified/generated/" $f
fi
done
}
export -f tag_file
# ------------------------------------------------------------------------------
# check files exists and return 1 if one do not
file_exists()
{
for f in $@; do
if [[ ! -f $(select_file $f) ]]; then
echo $f
return 1
fi
done
return 0
}
export -f file_exists
# ------------------------------------------------------------------------------
# check if file exists and return error if not
file_must_exists()
{
prnt I "Checking $@ files existance..."
local mf=$(file_exists $@)
if [[ $? -ne 0 ]]; then
prnt E "file_must_exists(): The $mf file is missing, can't continue."
die 10
fi
unset mf
}
export -f file_must_exists
# ------------------------------------------------------------------------------
# check files exists and return 1 if one do not
directory_exists()
{
for d in $@; do
if [[ ! -d $(select_directory $d) ]]; then
echo $d
return 1
fi
done
return 0
}
export -f directory_exists
# ------------------------------------------------------------------------------
# check if file exists and return error if not
directory_must_exists()
{
prnt I "Checking $@ directories existance..."
local md=$(directory_exists $@)
if [[ $? -ne 0 ]]; then
prnt E "directory_must_exists(): The $md directory is missing, can't continue."
die 10
fi
unset md
}
export -f directory_must_exists
# EOF