/lib/tmpfiles
#! | 296 lines | 235 code | 61 blank | 0 comment | 0 complexity | b1f78f419c11ba2cce360d0603fa09a5 MD5 | raw file
Possible License(s): GPL-2.0
- #!/bin/bash
- #
- # /lib/initscripts/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
- # don't try to resolve numeric IDs in case they don't exist
- if [[ $uid ]]; then
- if [[ $uid != +([0-9]) ]] && ! getent passwd "$uid" >/dev/null; then
- return 1
- fi
- fi
- # gid must be numeric or a valid group name
- # don't try to resolve numeric IDs in case they don't exist
- if [[ $gid ]]; then
- if [[ $gid != +([0-9]) ]] && ! getent group "$gid" >/dev/null; then
- return 1
- fi
- fi
- return 0
- }
- relabel() {
- local -a paths=($1)
- local mode=$2 uid=$3 gid=$4
- if ! checkparams 4 "$@"; then
- warninvalid
- return
- fi
- for path in "${paths[@]}"; do
- if [[ -e $path ]]; then
- [[ $uid != '-' ]] && chown $CHOPTS "$uid" "$path"
- [[ $gid != '-' ]] && chgrp $CHOPTS "$gid" "$path"
- [[ $mode != '-' ]] && chmod $CHOPTS "$mode" "$path"
- fi
- done
- }
- _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" /dev/null "$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" /dev/null "$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
- if ! checkparams 4 "$@"; then
- warninvalid
- return
- fi
- if [[ -d $path ]] && (( REMOVE )); then
- find "$path" -mindepth 1 -maxdepth 1 -xdev -exec rm -rf {} +
- fi
- if (( CREATE )); then
- install -d -m"$mode" -o"$uid" -g"$gid" "$path"
- fi
- }
- _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
- }
- _z() {
- # Set ownership, access mode and relabel security context of a file or
- # directory if it exists. Lines of this type accept shell-style globs in
- # place of normal path names.
- local -a paths=($1)
- local mode=$2 uid=$3 gid=$4
- (( CREATE )) || return 0
- relabel "$@"
- }
- _Z() {
- # Recursively set ownership, access mode and relabel security context of 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.
- (( CREATE )) || return 0
- CHOPTS=-R relabel "$@"
- }
- shopt -s nullglob
- declare -i CREATE=0 REMOVE=0 CLEAN=0 error=0 LINENO=0
- declare FILE=
- declare -A fragments
- declare -a tmpfiles_d=(
- /etc/tmpfiles.conf
- /etc/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 `/etc/tmpfiles.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]='root'
- [[ ${line[4]} = '-' ]] && line[4]='root'
- "_${line[@]}"
- done <"$FILE"
- done < <(printf '%s\0' "${!fragments[@]}" | sort -z)
- exit $error
- # vim: set ts=2 sw=2 noet: