5c736ad63e
Dry-run with more detail is more useful this way. Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
309 lines
7.7 KiB
Bash
Executable File
309 lines
7.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# This is a reimplementation of the systemd tmpfiles.d code
|
|
# Control creation, deletion, and cleaning of volatile and temporary files
|
|
#
|
|
# Copyright (c) 2012 Gentoo Foundation
|
|
#
|
|
# This instance based on the Arch Linux version:
|
|
# http://projects.archlinux.org/initscripts.git/tree/arch-tmpfiles
|
|
# As of 2012/01/01
|
|
#
|
|
# See the tmpfiles.d manpage as well:
|
|
# http://0pointer.de/public/systemd-man/tmpfiles.d.html
|
|
# This script should match the manpage as of 2012/03/12
|
|
#
|
|
|
|
DRYRUN=0
|
|
|
|
warninvalid() {
|
|
printf "tmpfiles: ignoring invalid entry on line %d of \`%s'\n" "$LINENUM" "$FILE"
|
|
error=$(( error+1 ))
|
|
} >&2
|
|
|
|
dryrun_or_real() {
|
|
local dryrun=
|
|
[ $DRYRUN -eq 1 ] && dryrun=echo
|
|
$dryrun "$@"
|
|
}
|
|
|
|
relabel() {
|
|
local path
|
|
local paths=$1 mode=$2 uid=$3 gid=$4
|
|
|
|
for path in ${paths}; do
|
|
if [ -e "$path" ]; then
|
|
[ $uid != '-' ] && dryrun_or_real chown $CHOPTS "$uid" "$path"
|
|
[ $gid != '-' ] && dryrun_or_real chgrp $CHOPTS "$gid" "$path"
|
|
[ $mode != '-' ] && dryrun_or_real chmod $CHOPTS "$mode" "$path"
|
|
[ -x /sbin/restorecon ] && dryrun_or_real restorecon $CHOPTS "$path"
|
|
fi
|
|
done
|
|
}
|
|
|
|
_b() {
|
|
# Create a block device node if it doesn't exist yet
|
|
local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6
|
|
[ ! -e "$path" ] && dryrun_or_real mknod $path b ${arg%:*} ${arg#*:}
|
|
}
|
|
|
|
_c() {
|
|
# Create a character device node if it doesn't exist yet
|
|
local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6
|
|
[ ! -e "$path" ] && dryrun_or_real mknod $path c ${arg%:*} ${arg#*:}
|
|
}
|
|
|
|
|
|
_f() {
|
|
# Create a file if it doesn't exist yet
|
|
local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6
|
|
|
|
[ $CREATE -gt 0 ] || return 0
|
|
|
|
if [ ! -e "$path" ]; then
|
|
dryrun_or_real install -m"$mode" -o"$uid" -g"$gid" /dev/null "$path"
|
|
[ -n "$arg" ] && _w "$@"
|
|
fi
|
|
}
|
|
|
|
_F() {
|
|
# Create or truncate a file
|
|
local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6
|
|
|
|
[ $CREATE -gt 0 ] || return 0
|
|
|
|
dryrun_or_real install -m"$mode" -o"$uid" -g"$gid" /dev/null "$path"
|
|
[ -n "$arg" ] && _w "$@"
|
|
}
|
|
|
|
_d() {
|
|
# Create a directory if it doesn't exist yet
|
|
local path=$1 mode=$2 uid=$3 gid=$4
|
|
|
|
[ $CREATE -gt 0 ] || return 0
|
|
|
|
if [ ! -d "$path" ]; then
|
|
dryrun_or_real 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 [ -d "$path" ] && [ $REMOVE -gt 0 ]; then
|
|
dryrun_or_real find "$path" -mindepth 1 -maxdepth 1 -xdev -exec rm -rf {} +
|
|
fi
|
|
|
|
if [ $CREATE -gt 0 ]; then
|
|
dryrun_or_real install -d -m"$mode" -o"$uid" -g"$gid" "$path"
|
|
fi
|
|
}
|
|
|
|
_L() {
|
|
# Create a symlink if it doesn't exist yet
|
|
local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6
|
|
[ ! -e "$path" ] && dryrun_or_real ln -s "$args" "$path"
|
|
}
|
|
|
|
_p() {
|
|
# Create a named pipe (FIFO) if it doesn't exist yet
|
|
local path=$1 mode=$2 uid=$3 gid=$4
|
|
|
|
[ $CREATE -gt 0 ] || return 0
|
|
|
|
if [ ! -p "$path" ]; then
|
|
dryrun_or_real mkfifo -m$mode "$path"
|
|
dryrun_or_real 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 paths=$1
|
|
|
|
[ $REMOVE -gt 0 ] || return 0
|
|
|
|
for path in ${paths}; do
|
|
if [ -f "$path" ]; then
|
|
dryrun_or_real rm -f "$path"
|
|
elif [ -d "$path" ]; then
|
|
dryrun_or_real 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 paths=$1
|
|
|
|
[ $REMOVE -gt 0 ] || return 0
|
|
|
|
for path in ${paths}; do
|
|
[ -d "$path" ] && dryrun_or_real rm -rf --one-file-system "$path"
|
|
done
|
|
}
|
|
|
|
_w() {
|
|
# Write the argument parameter to a file, if it exists.
|
|
local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6
|
|
if [ -f "$path" ]; then
|
|
if [ $DRYRUN -eq 1 ]; then
|
|
echo "echo \"$arg\" >>\"$path\""
|
|
else
|
|
echo "$arg" >>"$path"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
_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.
|
|
[ $CREATE -gt 0 ] || 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 -gt 0 ] || return 0
|
|
|
|
CHOPTS=-R relabel "$@"
|
|
}
|
|
|
|
CREATE=0 REMOVE=0 CLEAN=0 VERBOSE=0 DRYRUN=0 error=0 LINENO=0
|
|
FILE=
|
|
fragments=
|
|
# XXX: The harcoding of /usr/lib/ is an explicit choice by upstream
|
|
tmpfiles_dirs='/usr/lib/tmpfiles.d/ /etc/tmpfiles.d/ /run/tmpfiles.d/'
|
|
tmpfiles_basenames=''
|
|
tmpfiles_d=''
|
|
# Build a list of sorted unique basenames
|
|
# directories declared later in the tmpfiles_d array will override earlier
|
|
# directories, on a per file basename basis.
|
|
# `/etc/tmpfiles.d/foo.conf' supersedes `/usr/lib/tmpfiles.d/foo.conf'.
|
|
# `/run/tmpfiles/foo.conf' will always be read after `/etc/tmpfiles.d/bar.conf'
|
|
for d in ${tmpfiles_dirs} ; do
|
|
[ -d $d ] && for f in ${d}/*.conf ; do
|
|
[ -f $f ] && tmpfiles_basenames="${tmpfiles_basenames}\n${f##*/}"
|
|
done # for f in ${d}
|
|
done # for d in ${tmpfiles_dirs}
|
|
tmpfiles_basenames="`printf "${tmpfiles_basenames}\n" | sort | uniq`"
|
|
|
|
for b in $tmpfiles_basenames ; do
|
|
real_f=''
|
|
for d in $tmpfiles_dirs ; do
|
|
f=${d}/${b}
|
|
[ -f "${f}" ] && real_f=$f
|
|
done
|
|
[ -f "${real_f}" ] && tmpfiles_d="${tmpfiles_d} ${real_f}"
|
|
done
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case $1 in
|
|
--create) CREATE=1 ;;
|
|
--remove) REMOVE=1 ;;
|
|
--clean) CLEAN=1 ;; # TODO: Not implemented
|
|
--verbose) VERBOSE=1 ;;
|
|
--dryrun|--dry-run) DRYRUN=1 ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ $(( CLEAN )) -eq 1 ] ; then
|
|
printf '%s clean mode is not implemented\n' "${0##*/}"
|
|
exit 1
|
|
fi
|
|
|
|
if [ $(( CREATE + REMOVE )) -ne 1 ] ; then
|
|
printf 'usage: %s [--create] [--remove] [--clean] [--verbose] [--dry-run]\n' "${0##*/}"
|
|
exit 1
|
|
fi
|
|
|
|
error=0
|
|
|
|
# loop through the gathered fragments, sorted globally by filename.
|
|
# `/run/tmpfiles/foo.conf' will always be read after `/etc/tmpfiles.d/bar.conf'
|
|
for FILE in $tmpfiles_d ; do
|
|
LINENUM=0
|
|
|
|
### FILE FORMAT ###
|
|
# XXX: We ignore the 'Age' parameter
|
|
# 1 2 3 4 5 6 7
|
|
# Cmd Path Mode UID GID Age Argument
|
|
# d /run/user 0755 root root 10d -
|
|
# Mode, UID, GID, Age, Argument may be omitted!
|
|
|
|
# XXX: Upstream says whitespace is NOT permitted in the Path argument.
|
|
# But IS allowed when globs are expanded for the x/r/R/z/Z types.
|
|
while read line; do
|
|
LINENUM=$(( LINENUM+1 ))
|
|
|
|
# This will skip over comments and empty lines
|
|
set -- $line
|
|
|
|
# Unless we have both command and path, skip this line.
|
|
if [ -z "$1" -o -z "$2" ]; then
|
|
continue
|
|
fi
|
|
|
|
# whine about invalid entries
|
|
case $1 in
|
|
f|F|w|d|D|p|L|c|b|x|r|R|z|Z) ;;
|
|
\#) continue ;;
|
|
*) warninvalid ; continue ;;
|
|
esac
|
|
|
|
cmd=$1
|
|
path=$2
|
|
|
|
# fall back on defaults when parameters are passed as '-'
|
|
if [ "$3" = '-' -o "$3" = '' ]; then
|
|
case ${1} in
|
|
p|f|F) mode=0644 ;;
|
|
d|D) mode=0755 ;;
|
|
z|Z|x|r|R|L) ;;
|
|
esac
|
|
else
|
|
mode=$3
|
|
fi
|
|
uid=$4
|
|
gid=$5
|
|
age=$6
|
|
arg=$7
|
|
|
|
[ "${4}" = '-' -o "${4}" = '' ] && uid=0
|
|
[ "${5}" = '-' -o "${5}" = '' ] && gid=0
|
|
[ "${6}" = '-' -o "${6}" = '' ] && age=0
|
|
[ "${7}" = '-' -o "${7}" = '' ] && arg=''
|
|
set -- "$path" "$mode" "$uid" "$gid" "$age" "$arg"
|
|
|
|
[ "$VERBOSE" -eq "1" ] && echo _$cmd "$@"
|
|
_$cmd "$@"
|
|
rc=$?
|
|
if [ "${DRYRUN}" -eq "0" ]; then
|
|
[ $rc -ne 0 ] && error=$((error + 1))
|
|
fi
|
|
done <$FILE
|
|
done
|
|
|
|
exit $error
|
|
|
|
# vim: set ts=2 sw=2 sts=2 noet ft=sh:
|