PKGBUILDs/core/initscripts/arch-tmpfiles

251 lines
5.2 KiB
Bash
Executable file

#!/bin/bash
#
# /usr/lib/initscripts/arch-tmpfiles
#
# Control creation, deletion, and cleaning of volatile and temporary files
#
warninvalid() {
printf "arch-tmpfiles: ignoring invalid entry on line %d of \`%s'\n" "$LINENUM" "$FILE"
(( ++error ))
} >&2
checkparams() {
local parmreq=$1; shift
local path=$1 mode=$2 uid=$3 gid=$4
# parmcount must be >= parmreq
if (( $# < parmreq )); then
return 1
fi
# mode must be valid octal and 3 or 4 digits
if [[ $mode && ! $mode =~ ^[0-7]{3,4}$ ]]; then
return 1
fi
# uid must be numeric or a valid user name
if [[ $uid && $uid != +([[:digit:]]) ]] && ! getent passwd "$uid" >/dev/null; then
return 1
fi
# gid must be numeric or a valid group name
if [[ $gid && $gid != +([[:digit:]]) ]] && ! getent group "$gid" >/dev/null; then
return 1
fi
return 0
}
_f() {
# Create a file if it doesn't exist yet
local path=$1 mode=$2 uid=$3 gid=$4
(( CREATE )) || return 0
if ! checkparams 4 "$@"; then
warninvalid
return
fi
if [[ ! -e $path ]]; then
install -m"$mode" -o"$uid" -g"$gid" <(:) "$path"
fi
}
_F() {
# Create or truncate a file
local path=$1 mode=$2 uid=$3 gid=$4
(( CREATE )) || return 0
if ! checkparams 4 "$@"; then
warninvalid
return
fi
install -m"$mode" -o"$uid" -g"$gid" <(:) "$path"
}
_d() {
# Create a directory if it doesn't exist yet
local path=$1 mode=$2 uid=$3 gid=$4
(( CREATE )) || return 0
if ! checkparams 4 "$@"; then
warninvalid
return
fi
if [[ ! -d "$path" ]]; then
install -d -m"$mode" -o"$uid" -g"$gid" "$path"
fi
}
_D() {
# Create or empty a directory
local path=$1 mode=$2 uid=$3 gid=$4
(( CREATE )) || return 0
if ! checkparams 4 "$@"; then
warninvalid
return
fi
if [[ -d $path ]]; then
find "$path" -mindepth 1 -maxdepth 1 -xdev -print0 | xargs -r0 rm -rf
fi
install -d -m"$mode" -o"$uid" -g"$gid" "$path"
}
_p() {
# Create a named pipe (FIFO) if it doesn't exist yet
local path=$1 mode=$2 uid=$3 gid=$4
(( CREATE )) || return 0
if ! checkparams 4 "$@"; then
warninvalid
return
fi
if [[ ! -p "$path" ]]; then
mkfifo -m$mode "$path"
chown "$uid:$gid" "$path"
fi
}
_x() {
# Ignore a path during cleaning. Use this type to exclude paths from clean-up as
# controlled with the Age parameter. Note that lines of this type do not
# influence the effect of r or R lines. Lines of this type accept shell-style
# globs in place of of normal path names.
:
# XXX: we don't implement this
}
_r() {
# Remove a file or directory if it exists. This may not be used to remove
# non-empty directories, use R for that. Lines of this type accept shell-style
# globs in place of normal path names.
local path
local -a paths=($1)
(( REMOVE )) || return 0
if ! checkparams 1 "$@"; then
warninvalid
return
fi
for path in "${paths[@]}"; do
if [[ -f $path ]]; then
rm -f "$path"
elif [[ -d $path ]]; then
rmdir "$path"
fi
done
}
_R() {
# Recursively remove a path and all its subdirectories (if it is a directory).
# Lines of this type accept shell-style globs in place of normal path names.
local path
local -a paths=($1)
(( REMOVE )) || return 0
if ! checkparams 1 "$@"; then
warninvalid
return
fi
for path in "${paths[@]}"; do
[[ -d $path ]] && rm -rf --one-file-system "$path"
done
}
shopt -s nullglob
declare -i CREATE=0 REMOVE=0 CLEAN=0 error=0 LINENO=0
declare FILE=
declare -A fragments
declare -a tmpfiles_d=(
/usr/lib/tmpfiles.d/*.conf
/etc/tmpfiles.d/*.conf
/run/tmpfiles.d/*.conf
)
while (( $# )); do
case $1 in
--create) CREATE=1 ;;
--remove) REMOVE=1 ;;
esac
shift
done
if (( !(CREATE + REMOVE) )); then
printf 'usage: %s [--create] [--remove]\n' "${0##*/}"
exit 1
fi
# directories declared later in the tmpfiles_d array will override earlier
# directories, on a per file basis.
# Example: `/etc/tmpfiles.d/foo.conf' supersedes `/usr/lib/tmpfiles.d/foo.conf'.
for path in "${tmpfiles_d[@]}"; do
[[ -f $path ]] && fragments[${path##*/}]=${path%/*}
done
# catch errors in functions so we can exit with something meaningful
set -E
trap '(( ++error ))' ERR
# loop through the gathered fragments, sorted globally by filename.
# `/run/tmpfiles/foo.conf' will always be read after `/etc/tmpfiles.d/bar.conf'
while read -d '' fragment; do
LINENUM=0
printf -v FILE '%s/%s' "${fragments[$fragment]}" "$fragment"
### FILE FORMAT ###
# XXX: We ignore the final 'Age' parameter
# 0 1 2 3 4 5
# Type Path Mode UID GID Age
# d /run/user 0755 root root 10d
# omit read's -r flag to honor escapes here, so that whitespace can be
# escaped for paths. We will _not_ honor quoted paths.
while read -a line; do
(( ++LINENUM ))
# skip over comments and empty lines
if (( ! ${#line[*]} )) || [[ ${line[0]:0:1} = '#' ]]; then
continue
fi
# whine about invalid entries
if ! type -t _${line[0]} >/dev/null; then
warninvalid
continue
fi
# fall back on defaults when parameters are passed as '-'
if [[ ${line[2]} = '-' ]]; then
case ${line[0]} in
p|f|F) line[2]=0644 ;;
d|D) line[2]=0755 ;;
esac
fi
[[ ${line[3]} = '-' ]] && line[3]=0
[[ ${line[4]} = '-' ]] && line[4]=0
_${line[0]} "${line[@]:1}"
done <"$FILE"
done < <(printf '%s\0' "${!fragments[@]}" | sort -z)
exit $error
# vim: set ts=2 sw=2 noet: