aboutsummaryrefslogtreecommitdiff
path: root/sh/tmpfiles.sh.in
diff options
context:
space:
mode:
Diffstat (limited to 'sh/tmpfiles.sh.in')
-rwxr-xr-xsh/tmpfiles.sh.in286
1 files changed, 286 insertions, 0 deletions
diff --git a/sh/tmpfiles.sh.in b/sh/tmpfiles.sh.in
new file mode 100755
index 00000000..9c738975
--- /dev/null
+++ b/sh/tmpfiles.sh.in
@@ -0,0 +1,286 @@
+#!/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
+#
+
+warninvalid() {
+ printf "tmpfiles: ignoring invalid entry on line %d of \`%s'\n" "$LINENUM" "$FILE"
+ error=$(( error+1 ))
+} >&2
+
+relabel() {
+ local paths=$1 mode=$2 uid=$3 gid=$4
+
+ for path in ${paths}; do
+ if [ -e $path ]; then
+ [ $uid != '-' ] && chown $CHOPTS "$uid" "$path"
+ [ $gid != '-' ] && chgrp $CHOPTS "$gid" "$path"
+ [ $mode != '-' ] && chmod $CHOPTS "$mode" "$path"
+ # TODO: SELinux relabel
+ 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" ] && 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" ] && 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
+ 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
+
+ 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
+ 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
+ find "$path" -mindepth 1 -maxdepth 1 -xdev -exec rm -rf {} +
+ fi
+
+ if [ $CREATE -gt 0 ]; then
+ 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" ] && 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
+ 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 paths=$1
+
+ [ $REMOVE -gt 0 ] || return 0
+
+ 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 paths=$1
+
+ [ $REMOVE -gt 0 ] || return 0
+
+ for path in "${paths}"; do
+ [ -d $path ] && 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
+ [ -f "$path" ] && echo "$arg" >>"$path"
+}
+
+_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=
+# TODO: The systemd spec explicitly says /usr/lib/, but it should probably be
+# OUTSIDE of lib entirely, or at the very least handle multilib systems better.
+tmpfiles_dirs='/usr/lib64/tmpfiles.d/ /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 [ $(( CREATE + REMOVE )) -ne 1 ] ; then
+ printf 'usage: %s [--create] [--remove]\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!
+
+ # TODO: Sorry, we don't handle whitespace in paths.
+ while read line; do
+ LINENUM=$(( LINENUM+1 ))
+
+ # This will fix up whitespace and comment lines
+ # skip over comments and empty lines
+ set -- $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) ;;
+ *) 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} = '-' ] && uid=0
+ [ ${5} = '-' ] && gid=0
+ [ ${6} = '-' ] && age=0
+ [ ${7} = '-' ] && arg=''
+ set -- "$path" "$mode" "$uid" "$gid" "$age" "$arg"
+
+ [ "$VERBOSE" -eq "1" ] && echo _$cmd "$@"
+ if [ "${DRYRUN}" -eq "0" ]; then
+ _$cmd "$@"
+ rc=$?
+ [ $rc -ne 0 ] && error=$((error + 1))
+ fi
+ done <$FILE
+done
+
+exit $error
+
+# vim: set ts=2 sw=2 sts=2 noet ft=sh: