diff options
author | davidovski <david@davidovski.xyz> | 2022-03-01 23:14:41 +0000 |
---|---|---|
committer | davidovski <david@davidovski.xyz> | 2022-03-01 23:14:41 +0000 |
commit | ea4267fac55c7d8a6093a6b03ff0e9c795786d3e (patch) | |
tree | 04905981b9d5ad7a4710c472faf920f3c5e28375 /xi/bin/tmpfiles | |
parent | efee4ebf43e376a7cd8b8abcef0c70aa90427bb4 (diff) |
reorganised
Diffstat (limited to 'xi/bin/tmpfiles')
-rwxr-xr-x | xi/bin/tmpfiles | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/xi/bin/tmpfiles b/xi/bin/tmpfiles new file mode 100755 index 0000000..5e4ee0a --- /dev/null +++ b/xi/bin/tmpfiles @@ -0,0 +1,592 @@ +#!/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 +# Released under the 2-clause BSD license. +# +# This instance is a pure-POSIX sh version, written by Robin H Johnson +# <robbat2@gentoo.org>, based on the Arch Linux version as of 2012/01/01: +# http://projects.archlinux.org/initscripts.git/tree/arch-tmpfiles +# +# See the tmpfiles.d manpage as well: +# https://www.freedesktop.org/software/systemd/man/tmpfiles.d.html +# This script should match the old manpage +# http://0pointer.de/public/systemd-man/tmpfiles.d.html +# as of 2012/03/12 and also implements some more recent features +# + +DRYRUN=0 + +checkprefix() { + n=$1 + shift + for x in "$@"; do + case ${n} in + ${x}*) return 0 ;; + esac + done + return 1 +} + +warninvalid() { + printf "tmpfiles: ignoring invalid entry on line %d of \`%s'\n" "${LINENUM}" "${FILE}" + error=$(( error+1 )) +} >&2 + +invalid_option() { + printf "tmpfiles: invalid option '%s'\n" "$1" >&2 + exit 1 +} + +dryrun_or_real() { + local dryrun= + if [ ${DRYRUN} -eq 1 ]; then + dryrun="echo" + fi + ${dryrun} "$@" +} + +_chattr() { + local attr="$2" + case ${attr} in + [+-=]*) : ;; + '') return ;; + *) attr="+${attr}" ;; + esac + local IFS= + dryrun_or_real chattr "$1" "${attr}" -- "$3" +} + +_setfacl() { + dryrun_or_real setfacl -P "$1" "$2" "$3" -- "$4" +} + +relabel() { + local path + local paths=$1 mode=$2 uid=$3 gid=$4 + local status + + status=0 + for path in ${paths}; do + if [ -e "${path}" ]; then + if [ -x /sbin/restorecon ]; then + dryrun_or_real restorecon ${CHOPTS} "${path}" || status="$?" + fi + if [ "${uid}" != '-' ]; then + dryrun_or_real chown ${CHOPTS} "${uid}" "${path}" || status="$?" + fi + if [ "${gid}" != '-' ]; then + dryrun_or_real chgrp ${CHOPTS} "${gid}" "${path}" || status="$?" + fi + if [ "${mode}" != '-' ]; then + dryrun_or_real chmod ${CHOPTS} "${mode}" "${path}" || status="$?" + fi + fi + done + return ${status} +} + +splitpath() { + local path=$1 + while [ -n "${path}" ]; do + printf '%s\n' "${path}" + path=${path%/*} + done +} + +_restorecon() { + local path=$1 + if [ -x /sbin/restorecon ]; then + dryrun_or_real restorecon -F "$(splitpath "${path}")" + fi +} + +createdirectory() { + local mode="$1" uid="$2" gid="$3" path="$4" + [ -d "${path}" ] || dryrun_or_real mkdir -p "${path}" + if [ "${uid}" = - ]; then + uid=root + fi + if [ "${gid}" = - ]; then + gid=root + fi + if [ "${mode}" = - ]; then + mode=0755 + fi + dryrun_or_real chown ${uid} "${path}" + dryrun_or_real chgrp ${gid} "${path}" + dryrun_or_real chmod ${mode} "${path}" +} + +createfile() { + local mode="$1" uid="$2" gid="$3" path="$4" + dryrun_or_real touch "${path}" + if [ "${uid}" = - ]; then + uid=root + fi + if [ "${gid}" = - ]; then + gid=root + fi + if [ "${mode}" = - ]; then + mode=0644 + fi + dryrun_or_real chown ${uid} "${path}" + dryrun_or_real chgrp ${gid} "${path}" + dryrun_or_real chmod ${mode} "${path}" +} + +createpipe() { + local mode="$1" uid="$2" gid="$3" path="$4" + dryrun_or_real mkfifo "${path}" + if [ "${uid}" = - ]; then + uid=root + fi + if [ "${gid}" = - ]; then + gid=root + fi + if [ "${mode}" = - ]; then + mode=0644 + fi + dryrun_or_real chown ${uid} "${path}" + dryrun_or_real chgrp ${gid} "${path}" + dryrun_or_real chmod ${mode} "${path}" +} + +_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 + if [ "${uid}" = - ]; then + uid=root + fi + if [ "${gid}" = - ]; then + gid=root + fi + if [ "${mode}" = - ]; then + mode=0644 + fi + if [ ! -e "${path}" ]; then + dryrun_or_real mknod -m ${mode} "${path}" b "${arg%:*}" "${arg#*:}" + _restorecon "${path}" + dryrun_or_real chown ${uid} "${path}" + dryrun_or_real chgrp ${gid} "${path}" + fi +} + +_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 + if [ "${uid}" = - ]; then + uid=root + fi + if [ "${gid}" = - ]; then + gid=root + fi + if [ "${mode}" = - ]; then + mode=0644 + fi + if [ ! -e "${path}" ]; then + dryrun_or_real mknod -m ${mode} "${path}" c "${arg%:*}" "${arg#*:}" + _restorecon "${path}" + dryrun_or_real chown ${uid} "${path}" + dryrun_or_real chgrp ${gid} "${path}" + fi +} + +_C() { + # recursively copy a file or directory + local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 + if [ ! -e "${path}" ]; then + dryrun_or_real cp -r "${arg}" "${path}" + _restorecon "${path}" + if [ "${uid}" != '-' ]; then + dryrun_or_real chown "${uid}" "${path}" + fi + if [ "${gid}" != '-' ]; then + dryrun_or_real chgrp "${gid}" "${path}" + fi + if [ "${mode}" != '-' ]; then + dryrun_or_real chmod "${mode}" "${path}" + fi + fi +} + +_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 + createfile "${mode}" "${uid}" "${gid}" "${path}" + if [ -n "${arg}" ]; then + _w "$@" + fi + 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 rm -f "${path}" + createfile "${mode}" "${uid}" "${gid}" "${path}" + if [ -n "${arg}" ]; then + _w "$@" + fi +} + +_d() { + # Create a directory if it doesn't exist yet + local path=$1 mode=$2 uid=$3 gid=$4 + + if [ "${CREATE}" -gt 0 ]; then + createdirectory "${mode}" "${uid}" "${gid}" "${path}" + _restorecon "${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 {} + + _restorecon "${path}" + fi + + if [ "${CREATE}" -gt 0 ]; then + createdirectory "${mode}" "${uid}" "${gid}" "${path}" + _restorecon "${path}" + fi +} + +_v() { + # Create a subvolume if the path does not exist yet and the file system + # supports this (btrfs). Otherwise create a normal directory. + # TODO: Implement btrfs subvol creation. + _d "$@" +} + +_q() { + # Similar to _v. However, make sure that the subvolume will be assigned + # to the same higher-level quota groups as the subvolume it has + # been created in. + # TODO: Implement btrfs subvol creation. + _d "$@" +} + +_Q() { + # Similar to q. However, instead of copying the higher-level quota + # group assignments from the parent as-is, the lowest quota group + # of the parent subvolume is determined that is not the + # leaf quota group. + # TODO: Implement btrfs subvol creation. + _d "$@" +} + +_a() { + # Set/add file/directory ACL. Lines of this type accept + # shell-style globs in place of normal path names. + # The format of the argument field matches setfacl + local ACTION='--remove-all --set' + [ "${FORCE}" -gt 0 ] && ACTION='--modify' + _setfacl '' "${ACTION}" "$6" "$1" +} + +_A() { + # Recursively set/add file/directory ACL. Lines of this type accept + # shell-syle globs in place of normal path names. + # Does not follow symlinks + local ACTION='--remove-all --set' + [ "${FORCE}" -gt 0 ] && ACTION='--modify' + _setfacl -R "${ACTION}" "$6" "$1" +} + +_h() { + # Set file/directory attributes. Lines of this type accept + # shell-style globs in place of normal path names. + # The format of the argument field matches chattr + _chattr '' "$6" "$1" +} + +_H() { + # Recursively set file/directory attributes. Lines of this type accept + # shell-syle globs in place of normal path names. + # Does not follow symlinks + _chattr -R "$6" "$1" +} + +_L() { + # Create a symlink if it doesn't exist yet + local path=$1 mode=$2 uid=$3 gid=$4 age=$5 arg=$6 + if [ ! -e "${path}" ]; then + dryrun_or_real ln -s "${arg}" "${path}" + fi + _restorecon "${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 + createpipe "${mode}" "${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 +} + +_X() { + # Ignore a path during cleanup. Use this type to prevent path + # removal as controled with the age parameter. Note that if path is + # a directory, the content of the directory is not excluded from + # clean-up, only the directory itself. + # Lines of this type accept shell-style globs in place 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 + local status + + [ "${REMOVE}" -gt 0 ] || return 0 + + status=0 + for path in ${paths}; do + if [ -f "${path}" ]; then + dryrun_or_real rm -f "${path}" || status="$?" + elif [ -d "${path}" ]; then + dryrun_or_real rmdir "${path}" || status="$?" + fi + done + return ${status} +} + +_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 + local status + + [ "${REMOVE}" -gt 0 ] || return 0 + + status=0 + for path in ${paths}; do + if [ -d "${path}" ]; then + dryrun_or_real rm -rf --one-file-system "${path}" || status="$?" + fi + done + return ${status} +} + +_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 "$@" +} + +usage() { + printf 'usage: %s [--exclude-prefix=path] [--prefix=path] [--boot] [--create] [--remove] [--clean] [--verbose] [--dry-run]\n' "${0##*/}" + exit "${1:-0}" +} + +version() { + # We don't record the version info anywhere currently. + echo "opentmpfiles" + exit 0 +} + +BOOT=0 CREATE=0 REMOVE=0 CLEAN=0 VERBOSE=0 DRYRUN=0 error=0 LINENO=0 +EXCLUDE= +PREFIX= +FILES= + +while [ $# -gt 0 ]; do + case $1 in + --boot) BOOT=1 ;; + --create) CREATE=1 ;; + --remove) REMOVE=1 ;; + --clean) CLEAN=1 ;; # TODO: Not implemented + --verbose) VERBOSE=1 ;; + --dryrun|--dry-run) DRYRUN=1 ;; + --exclude-prefix=*) EXCLUDE="${EXCLUDE}${1##--exclude-prefix=} " ;; + --prefix=*) PREFIX="${PREFIX}${1##--prefix=} " ;; + -h|--help) usage ;; + --version) version ;; + -*) invalid_option "$1" ;; + *) FILES="${FILES} $1" + esac + shift +done + +if [ $(( CLEAN )) -eq 1 ] ; then + printf '%s clean mode is not implemented\n' "${0##*/}" + exit 1 +fi + +if [ "${CREATE}${REMOVE}" = '00' ]; then + usage 1 >&2 +fi + +# XXX: The harcoding of /usr/lib/ is an explicit choice by upstream +tmpfiles_dirs='/usr/lib/tmpfiles.d /run/tmpfiles.d /etc/tmpfiles.d' +tmpfiles_basenames='' + +if [ -z "${FILES}" ]; then + # 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 + case "${f##*/}" in + systemd.conf|systemd-*.conf) continue;; + esac + [ -f "${f}" ] && tmpfiles_basenames="${tmpfiles_basenames}\n${f##*/}" + done # for f in ${d} + done # for d in ${tmpfiles_dirs} + # shellcheck disable=SC2059 + FILES="$(printf "${tmpfiles_basenames}" | sort -u )" +fi + +tmpfiles_d='' + +for b in ${FILES} ; do + if [ "${b##*/}" != "${b}" ]; then + # The user specified a path on the command line + # Just pass it through unaltered + tmpfiles_d="${tmpfiles_d} ${b}" + else + 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}" + fi +done + +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' +FILE= +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! + # If Cmd ends with !, the line is only processed if --boot is passed + + # 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 -r cmd path mode uid gid age arg rest; do + LINENUM=$(( LINENUM+1 )) + FORCE=0 + + # Unless we have both command and path, skip this line. + if [ -z "${cmd}" ] || [ -z "${path}" ]; then + continue + fi + + case ${cmd} in + \#*) continue ;; + esac + + while [ ${#cmd} -gt 1 ]; do + case ${cmd} in + *!) cmd=${cmd%!}; [ "${BOOT}" -eq "1" ] || continue 2 ;; + *+) cmd=${cmd%+}; FORCE=1; ;; + *) warninvalid ; continue 2 ;; + esac + done + + # whine about invalid entries + case ${cmd} in + f|F|w|d|D|v|p|L|c|C|b|x|X|r|R|z|Z|q|Q|h|H|a|A) ;; + *) warninvalid ; continue ;; + esac + + # fall back on defaults when parameters are passed as '-' + if [ "${mode}" = '-' ] || [ "${mode}" = '' ]; then + case "${cmd}" in + p|f|F) mode=0644 ;; + d|D|v) mode=0755 ;; + C|z|Z|x|r|R|L) ;; + esac + fi + + [ "${uid}" = '-' ] || [ "${uid}" = '' ] && uid=0 + [ "${gid}" = '-' ] || [ "${gid}" = '' ] && gid=0 + [ "${age}" = '-' ] || [ "${age}" = '' ] && age=0 + [ "${arg}" = '-' ] || [ "${arg}" = '' ] && arg='' + set -- "${path}" "${mode}" "${uid}" "${gid}" "${age}" "${arg}" + + [ -n "${EXCLUDE}" ] && checkprefix "${path}" "${EXCLUDE}" && continue + [ -n "${PREFIX}" ] && ! checkprefix "${path}" "${PREFIX}" && continue + + if [ "${FORCE}" -gt 0 ]; then + case ${cmd} in + p|L|c|b) [ -f "${path}" ] && dryrun_or_real rm -f "${path}" + esac + fi + + [ "${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: |