sh/tmpfiles: tmpfiles.d support.
This is the baseline support for tmpfiles.d. Still missing: - SELinux relabel, pending upstream clarification - LIBDIR vs multilib systems, pending upstream clarification - Whitespace in paths? - Clean support not implemented - "x" exclude type not implemented X-Gentoo-Bug: 396003 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=396003 Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
This commit is contained in:
		
							
								
								
									
										1
									
								
								sh/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								sh/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -9,3 +9,4 @@ init-early.sh | |||||||
| ifwatchd-carrier.sh | ifwatchd-carrier.sh | ||||||
| ifwatchd-nocarrier.sh | ifwatchd-nocarrier.sh | ||||||
| udhcpc-hook.sh | udhcpc-hook.sh | ||||||
|  | tmpfiles.sh | ||||||
|   | |||||||
| @@ -1,8 +1,8 @@ | |||||||
| DIR=	${LIBEXECDIR}/sh | DIR=	${LIBEXECDIR}/sh | ||||||
| SRCS=	init.sh.in functions.sh.in gendepends.sh.in init-common-post.sh.in \ | SRCS=	init.sh.in functions.sh.in gendepends.sh.in init-common-post.sh.in \ | ||||||
| 	rc-functions.sh.in runscript.sh.in ${SRCS-${OS}} | 	rc-functions.sh.in runscript.sh.in tmpfiles.sh.in ${SRCS-${OS}} | ||||||
| INC=	init-common-post.sh rc-mount.sh functions.sh rc-functions.sh | INC=	init-common-post.sh rc-mount.sh functions.sh rc-functions.sh | ||||||
| BIN=	gendepends.sh init.sh runscript.sh ${BIN-${OS}} | BIN=	gendepends.sh init.sh runscript.sh tmpfiles.sh ${BIN-${OS}} | ||||||
|  |  | ||||||
| INSTALLAFTER=	_installafter | INSTALLAFTER=	_installafter | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										286
									
								
								sh/tmpfiles.sh.in
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										286
									
								
								sh/tmpfiles.sh.in
									
									
									
									
									
										Executable file
									
								
							| @@ -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: | ||||||
		Reference in New Issue
	
	Block a user