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-nocarrier.sh | ||||
| udhcpc-hook.sh | ||||
| tmpfiles.sh | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| DIR=	${LIBEXECDIR}/sh | ||||
| 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 | ||||
| BIN=	gendepends.sh init.sh runscript.sh ${BIN-${OS}} | ||||
| BIN=	gendepends.sh init.sh runscript.sh tmpfiles.sh ${BIN-${OS}} | ||||
|  | ||||
| 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