diff options
Diffstat (limited to 'sys-apps')
-rw-r--r-- | sys-apps/tmpfs/Manifest | 3 | ||||
-rw-r--r-- | sys-apps/tmpfs/files/tmpfs.conf | 56 | ||||
-rw-r--r-- | sys-apps/tmpfs/files/tmpfs.init | 552 | ||||
-rw-r--r-- | sys-apps/tmpfs/tmpfs-1.0.ebuild | 62 |
4 files changed, 673 insertions, 0 deletions
diff --git a/sys-apps/tmpfs/Manifest b/sys-apps/tmpfs/Manifest new file mode 100644 index 00000000..6a4dd30c --- /dev/null +++ b/sys-apps/tmpfs/Manifest @@ -0,0 +1,3 @@ +AUX tmpfs.conf 2102 SHA256 4c973eb015791a1d2b61037cb072e173a4122104152ca8ffa7110fad4488bbf0 SHA512 4f9696e418ba0e90074714ab64a976163d49ba8b9b48c3cecb177630d25c144e226637bef8e2bad261952995fadb415092c96e84a60dd1cc6684af2186ecaf4f WHIRLPOOL 40b2cbce83e051f2fab32d55135cd106b8ade1035d4cf3c0c3d41f3914ede82e7467d8075f536008c0779e685eaf97481b449d91170325ed5aff18048d60bfae +AUX tmpfs.init 12412 SHA256 7c1e319621432e175c9a8abab3f421c9dc142a1917cff2faacf7d035a21006d6 SHA512 e65504ee339d80d9f8dd7ee473181e17393e32e37b9df2e7f4da398299c1023865d649638c63995c547b3a9f41c81c2874e08f81b7677fb00478c83949cb7fd9 WHIRLPOOL f25619edba9396c30cbc89a5d7dd475c2a3b90fc9100f1ed80e75ee1a0779f1f1656ce54b8524190f0c3c1ede30fcb449b5c57e2c54912438ba4e4dd533edb7a +EBUILD tmpfs-1.0.ebuild 1848 SHA256 1092a951f27a9d097451ff52e7f9c235e971f981e8a7e2f7b8fe628407e45ef2 SHA512 156d986545e1e211799aca3d7b0a2cbbb9ab0cfd6ddf0d614760a3c0011528d4185c90cd41c19cc842413404af8f36f4c0e3dbb4fb8bb40c0a1fd13413aa88c8 WHIRLPOOL fda02de650fe2b87e525c2e83cbd80adbd046896675916fd288dc6c0f09089af7d915763130503f245b39fd97b8bdfc7ee5047f930c9516c55969cb4b066bb61 diff --git a/sys-apps/tmpfs/files/tmpfs.conf b/sys-apps/tmpfs/files/tmpfs.conf new file mode 100644 index 00000000..48af6ce0 --- /dev/null +++ b/sys-apps/tmpfs/files/tmpfs.conf @@ -0,0 +1,56 @@ +# tmpfs - Mirror segments of a mounted filesystem to a network-mounted +# or memory-based backing store... + +# +# OVERLAY_ROOT_{location} allows the destination for 'location' to be +# customised; +# {location}_PATH allows the original location itself to be modified. +# +# Note that the former requires an inital '/', but the latter should +# not have one. +# +# Either OVERLAY_ROOT or OVERLAY_ROOT and PATH may be specified. +# +# OVERLAY_ROOT/PATH will replace /PATH. +# + + +# Specify default location to which to mirror data: +# +#OVERLAY_ROOT="<local nfs mount-point>" + +# Relocate /var to an NFS-mounted portage sub-directory: +# +#OVERLAY_ROOT_var="/usr/portage/local/hostname/var" +#var_PATH="var" + +# Mount a ram-backed filesystem on /mnt/ram and then relocate /var/.mem to this +# location. Items in /var can then be symlinked into .mem (for example +# /var/tmp -> ./.mem/tmp) to limit writes to flash-backed filesystems. Data in +# /var/lock and /var/run is mirrored back to disk on shutdown, but is not +# copied into RAM on startup, to prevent issues with state locks and PID files. +# Finally, we ensure that we start *after* swap is enabled, to ensure that we +# don't trigger an OOM situation on boot. The 'size' parameter in the FSTAB +# entry should be sized appropriately to the system - 96m is suitable for a +# router with 256MB of RAM: +# +OVERLAY_ROOT_ram="/mnt/ram" +ram_PATH="var/.mem" +ram_FSTAB="ram /mnt/ram tmpfs nodev,nosuid,noexec,size=96m" +ram_EXCLUDE="/lock/* /run/*" +RC_NEED_ram="localmount" +RC_AFTER_ram="swap" + +# Provide a mount-point on /var/.nfs which is mirrored to an NFS mount. This +# is useful to store the dynamic data for applications which generate more data +# than can be held in memory. In this case, ntop is made to save its data via +# NFS by ensuring that there is a symlink /var/lib/ntop -> ../.nfs/lib/ntop: +# +#OVERLAY_ROOT_nfs="/usr/portage/local/hostname" +#nfs_PATH="var/.nfs" +#RC_NEED_nfs="localmount nfsmount net.ef0" +#RC_USE_nfs="sshd" + +# NB: If DEBUG is set to a non-zero value, then commits will be *simulated* +# only, and no changes will be committed to disk. +DEBUG=0 diff --git a/sys-apps/tmpfs/files/tmpfs.init b/sys-apps/tmpfs/files/tmpfs.init new file mode 100644 index 00000000..d393ccb8 --- /dev/null +++ b/sys-apps/tmpfs/files/tmpfs.init @@ -0,0 +1,552 @@ +#!/sbin/runscript +# Copyright 2010-2013 Stuart Shelton +# Distributed under the terms of the GNU General Public License v2 + +source /etc/conf.d/tmpfs + +extra_commands="forceunmount forceumount commit" + +OVERLAY_ROOT="" +mp="" + +function getconf() { + local svc="$1" ; shift + + [[ -n "$svc" ]] || return 255 + + (( DEBUG )) && einfo "DEBUG: svc is '$svc'" + + local x="OVERLAY_ROOT_${svc}" + if [[ -n "${!x}" ]]; then + OVERLAY_ROOT="${!x}" + x="${svc}_PATH" + if [[ -n "${!x}" ]]; then + mp="${!x}" + fi + fi + + if (( DEBUG )); then + einfo "DEBUG: OVERLAY_ROOT is \"$OVERLAY_ROOT\"" + einfo "DEBUG: mp is \"$mp\"" + fi + + if ! [[ -n "${mp}" && -d "/${mp}" ]]; then + eerror "Cannot find directory \"${mp}\"" + return 1 + fi +} # getconf + +function movesvcmount() { + local action="$1" ; shift + local dir="$1"; shift + local code + + [[ -n "$svcdir" ]] || return 255 + + [[ -n "$action" && "$action" =~ (save|restore) ]] || return 255 + [[ "restore" == "$action" ]] && { [[ -n "$dir" && -d "$dir" ]] || return 255 ; } + + # OpenRC provides $svcmount... + + case "$action" in + save) + [[ -n "$mp" ]] || return 255 + [[ -n "$svcmount" ]] || return 255 + + if grep -q "var" <<<"${mp}"; then + (( DEBUG )) && einfo "DEBUG: /\$mp is /var or below ..." >&2 + if [[ "$svcmount" == "yes" ]]; then + ebegin "Moving \"$svcdir\" mount" >&2 + + tmpdir="$( mktemp --tmpdir -d )" + code=$? + if (( code )); then + eend $code "Could not create temporary directory: $code" >&2 + return $code + fi + + mount --move "$svcdir" "$tmpdir" + code=$? + if (( code )); then + eend $code "Move failed: $code" >&2 + rmdir "$tmpdir" 2>/dev/null + return $code + fi + + eend + fi + fi + + echo "$tmpdir" + return 0 + ;; + + restore) + ebegin "Moving \"$svcdir\" mount" >&2 + mkdir -p "$svcdir" >/dev/null + mount --move "$dir" "$svcdir" + code=$? + eend $code "Move failed: $code" >&2 + + rmdir "$tmpdir" 2>/dev/null + + return $code + ;; + + *) + return 254 + ;; + esac + + # Unreachable + return 254 +} # movesvcmount + +function unbindtmpfs() { + local code=1 + + [[ -n "$OVERLAY_ROOT" ]] || return 255 + [[ -n "$mp" ]] || return 255 + + ebegin "Unmounting \"/${mp}\" (\"${OVERLAY_ROOT}/${mp}\")" + + (( DEBUG )) && einfo "DEBUG: Current mounts" && mount + umount -f "/${mp}" >/dev/null 2>&1 + (( DEBUG )) && einfo "DEBUG: New mounts" && mount + + if ! mount | grep -Eq " on /${mp} type (none|tmpfs) "; then + code=0 + else + if ! [[ -x "$( type -pf lsof )" ]]; then + code=1 + else + ewarn "umount failed; attempting to kill tasks locking \"/${mp}\"" + + local count=0 + + while [[ -n "$( lsof -Xt "/${mp}" )" ]] && (( count < 5 )); do + for pid in $( lsof -Xt "/${mp}" ); do + ps $pid >/dev/null && kill -TERM $pid && (( DEBUG )) && einfo "Sending TERM signal to PID $pid" + done + + sleep 0.5 + + (( count++ )) + done + + count=0 + + while [[ -n "$( lsof -Xt "/${mp}" )" ]] && (( count < 3 )); do + for pid in $( lsof -Xt "/${mp}" ); do + ps $pid >/dev/null && kill -KILL $pid && (( DEBUG )) && einfo "Sending KILL signal to PID $pid" + done + + sleep 1 + + (( count++ )) + done + + umount -f "/${mp}" >/dev/null 2>&1 + + if mount | grep -Eq " on /${mp} type (none|tmpfs) "; then + code=1 + else + code=0 + fi + fi + fi + eend $code "umount failed: $code" + + return $code +} # unbindtmpfs + +function restoredata() { + local destination="$1" ; shift + local message="to" code=1 + + [[ -n "$OVERLAY_ROOT" ]] || return 255 + [[ -n "$mp" ]] || return 255 + + + if [[ -z "$destination" ]]; then + destination="/${mp}" + message="back to" + fi + + if ! [[ -d "${OVERLAY_ROOT}/${mp}" ]]; then + eerror "\"${OVERLAY_ROOT}/${mp}\" does not exist or is not mounted" + (( DEBUG )) && einfo "\"${OVERLAY_ROOT}/\" contains:" && ls -lAR "${OVERLAY_ROOT}/" + + code=1 + else + # We won't process EXCLUDEs here - we always + # want to back-up *everything*... + if [[ -x "$( type -pf rsync )" ]]; then + ebegin "Synchronising \"${OVERLAY_ROOT}\" ${message} \"${destination}\"" + local options="" + (( DEBUG )) && options="-vvn" || options="-q" + rsync $options -caHAXSx --super --delete --delete-before "${OVERLAY_ROOT}/${mp}/" "${destination}/" + eend $? "rsync failed: $?" + else + ebegin "Copying \"${OVERLAY_ROOT}\" ${message} \"${destination}\"" + mv "${destination}" "${destination}.old" + local options="" + (( DEBUG )) && options="-v" + cp $options -ax --sparse=always "${OVERLAY_ROOT}/${mp}/" "${destination}" + local code=$? + eend $code "File-copy failed: $code" + + if (( code )); then + rm -rf "${destination}.old" + else + rm -rf "${destination}" + mv "${destination}.old" "${destination}" + fi + fi + code=0 + fi + + return $code +} # restoredata + +depend() { + local mp="${SVCNAME#*.}" + + config /etc/conf.d/tmpfs + + before bootmisc + before logger + #after localmount + need localmount + + source /etc/conf.d/tmpfs + + local x="RC_NEED_${mp}" + [[ -n ${!x} ]] && need ${!x} + local x="RC_USE_${mp}" + [[ -n ${!x} ]] && use ${!x} + local x="RC_AFTER_${mp}" + [[ -n ${!x} ]] && after ${!x} +} + +start() { + case "${SVCNAME}" in + tmpfs) + eerror "$SVCNAME is not meant to be called directly" + return 1 + ;; + *) + local exclude="" fstab="" fs="" fsmp="" fstype="" fsopt="" code=1 + + local x="${SVCNAME#*.}_EXCLUDE" + if [[ -n "${!x}" ]]; then + exclude="${!x}" + fi + x="${SVCNAME#*.}_FSTAB" + if [[ -n "${!x}" ]]; then + fstab="${!x}" + if [[ -n "$fstab" ]]; then + fs="$( cut -d' ' -f 1 <<<"$fstab" )" + fsmp="$( cut -d' ' -f 2 <<<"$fstab" )" + fstype="$( cut -d' ' -f 3 <<<"$fstab" )" + fsopt="$( cut -d' ' -f 4 <<<"$fstab" )" + if [[ -n "$fs" && -n "$fsmp" && -n "$fstype" ]]; then + if ! mount | grep -q "^$fs on $fsmp type $fstype "; then + ebegin "Mounting '$fs' on $fsmp" + if [[ -n "$fsopt" ]]; then + mount -t "$fstype" "$fs" "$fsmp" -o "$fsopt" + else + mount -t "$fstype" "$fs" "$fsmp" + fi + eend $? + fi + fi + fi + fi + unset fsopt fstype fsmp fs fstab + + getconf "${SVCNAME#*.}" || return $? + + if mount | grep -Eq " on /${mp} type (none|tmpfs) "; then + eerror "\"/${mp}\" is already mounted! " + return 1 + fi + + if ! [[ -d "${OVERLAY_ROOT}" ]]; then + eerror "\"${OVERLAY_ROOT}\" does not exist or is not mounted" + else + ebegin "Remounting \"/$mp\" to tmpfs" + eindent + + if [[ -d "${OVERLAY_ROOT}/${mp}.old" ]]; then + rm -rf "${OVERLAY_ROOT}/${mp}.old" + fi + + local tmpdir="$( movesvcmount save )" + local movesvcdir=$? + + ebegin "Mirroring \"/${mp}\" to \"${OVERLAY_ROOT}\"" + + if [[ -x "$( type -pf rsync )" ]]; then + if [[ -n "$exclude" ]]; then + local element + for element in ${exclude}; do + options="$options --exclude $element" + done + unset element + fi + if (( DEBUG )); then + options="-vvn $options" + einfo "Additional options are '$options'" + else + options="-q $options" + fi + mkdir -p "${OVERLAY_ROOT}/${mp}/" + rsync -caHAXSx --super --delete --delete-before $options "/${mp}/" "${OVERLAY_ROOT}/${mp}/" + code=$? + eend $code "rsync failed: $code" + else + if [[ -d "${OVERLAY_ROOT}/${mp}" ]]; then + mv "${OVERLAY_ROOT}/${mp}" "${OVERLAY_ROOT}/${mp}.old" + fi + local options="" + (( DEBUG )) && options="-v" + cp $options -ax --sparse=always "/${mp}/" "${OVERLAY_ROOT}/${mp}" + code=$? + if [[ -n "$exclude" ]]; then + local element + for element in ${exclude}; do + # This is safe, as we've copied the original + # directory out of the way just above... + if [[ -d "${OVERLAY_ROOT}/${mp}/${element#/}" ]]; then + rm -r "${OVERLAY_ROOT}/${mp}/${element#/}"/* 2>/dev/null + elif [[ -e "${OVERLAY_ROOT}/${mp}/${element#/}" ]]; then + rm -r "${OVERLAY_ROOT}/${mp}/${element#/}" 2>/dev/null + fi + done + unset element + fi + eend $code "File-copy failed: $code" + fi + + if ! (( code )); then + ebegin "Mounting \"${OVERLAY_ROOT}/${mp}\" on \"/${mp}\"" + mount --rbind "${OVERLAY_ROOT}/${mp}/" "/${mp}" + code=$? + eend $code "Mount failed: $code" + fi + + if (( movesvcdir )); then + movesvcmount restore "$tmpdir" + code=$? + fi + + if ! (( code )); then + if [[ -n "$( lsof -Xt "/${mp}" )" ]]; then + ebegin "Sending HUP signal to all processes using /${mp}" + kill -HUP $( lsof -Xt "/${mp}" ) + eend + fi + fi + + eoutdent + eend + fi + + return $code + ;; + esac +} + +stop() { + #export DEBUG=1 + + case "${SVCNAME}" in + tmpfs) + eerror "$SVCNAME is not meant to be called directly" + return 1 + ;; + *) + local code=1 pid + + getconf "${SVCNAME#*.}" || return $? + + if ! mount | grep -Eq " on /${mp} type (none|tmpfs) "; then + eerror "\"/${mp}\" is not mounted from tmpfs! " + return 1 + fi + + ebegin "Restoring working-set from \"$OVERLAY_ROOT\" back to \"/$mp\"" + eindent + + sync + + local tmpdir="$( movesvcmount save )" + local movesvcdir=$? + (( DEBUG )) && einfo "DEBUG: movesvcdir is $movesvcdir" + + unbindtmpfs + code=$? + + # This is causing lock-ups on shutdown... + #(( DEBUG )) && sleep 1 && [[ -d "${OVERLAY_ROOT}/${mp}" ]] && einfo "DEBUG: OVERLAY_ROOT/mp \"$OVERLAY_ROOT/$mp\" exists" + + sleep 0.1 + + if ! (( code )); then + restoredata + code=$? + fi + + if ! (( code )) && (( movesvcdir )); then + movesvcmount restore "$tmpdir" + code=$? + fi + + if (( code )) && ((movesvcdir )); then + if [[ -w /proc/sysrq-trigger ]]; then + ewarn "We were unable to remount the in-memory service directory" + ewarn "to \"$svcdir\"." + eerror "This is very bad, and this machine must now be forcibly restarted ..." + for (( n = 1 ; n <= 5 ; n++ )); do + echo -ne "\a" + sleep 0.1 &>/dev/null ; sleep 0,1 &>/dev/null + echo -ne "\a" + sleep 1 + done + echo "sub" > /proc/sysrq-trigger + fi + fi + + eoutdent + eend $code + + return $code + ;; + esac +} + +forceunmount() { + forceumount "$@" +} + +forceumount() { + # Enable contents to be saved even if the host OS doesn't think the service is running... + #export DEBUG=1 + + case "${SVCNAME}" in + tmpfs) + eerror "$SVCNAME is not meant to be called directly" + return 1 + ;; + *) + local code=1 movesvcdir=0 + + getconf "${SVCNAME#*.}" || return $? + + ebegin "Unconditionally restoring working-set from \"$OVERLAY_ROOT\" back to \"/$mp\"" + eindent + + if ! mount | grep -Eq " on /${mp} type (none|tmpfs) "; then + einfo "\"/${mp}\" is not currently mounted from tmpfs! " + code=0 + else + local tmpdir="$( movesvcmount save )" + local movesvcdir=$? + (( DEBUG )) && einfo "DEBUG: movesvcdir is $movesvcdir" + + unbindtmpfs + code=$? + fi + + if ! (( code )); then + restoredata + code=$? + fi + + if (( movesvcdir )); then + movesvcmount restore "$tmpdir" + code=$? + fi + + eoutdent + eend $code + + return $code + ;; + esac +} + +commit() { + case "${SVCNAME}" in + tmpfs) + eerror "$SVCNAME is not meant to be called directly" + return 1 + ;; + *) + local code=1 pid mounts mountpoint tmpdir subdir + + getconf "${SVCNAME#*.}" || return $? + + if ! mount | grep -Eq " on /${mp} type (none|tmpfs) "; then + eerror "\"/${mp}\" is not mounted from tmpfs! " + return 1 + fi + + ebegin "Synchronising working-set from \"$OVERLAY_ROOT\" to \"/$mp\"" + eindent + + sync + + mounts="$( grep -Ev "bind| (cgroup|debugfs|devpts|nfs|proc|rootfs|securityfs|sysfs|tmpfs) " /proc/mounts | cut -d' ' -f 2 )" + + # Walk up the directory tree to find where $mp is mounted upon + mountpoint="/$mp" + while [[ "$mountpoint" != "/" ]]; do + if grep -oqw "$mountpoint" <<<"$mounts"; then + # We've found the relevant mountpoint + break + else + # Step back one... + mountpoint="$( dirname "$mountpoint" )" + fi + done + + tmpdir="$( mktemp --tmpdir -d )" + code=$? + if (( code )); then + eend $code "Could not create temporary directory: $code" + return $code + fi + mount --bind "$mountpoint" "$tmpdir" + code=$? + if (( code )); then + rmdir "$tmpdir" >/dev/null 2>&1 + eend $code "Could not mount filesystem '$mountpoint' on temporary directory: $code" + return $code + fi + + # Determine the path of $mp relative to its $mountpoint + subdir="/$mp" + subdir="${subdir#$mountpoint}" + subdir="${subdir#/}" + if ! [[ -d "$tmpdir"/"$subdir" ]]; then + umount -f "$tmpdir" >/dev/null 2>&1 + rmdir "$tmpdir" >/dev/null 2>&1 + eend 1 "Unable to determine bind-mounted path for '/$mp' on '$tmpdir' (got '$tmpdir/$subdir')" + return 1 + fi + + restoredata "${tmpdir}/${subdir}" + + umount -f "$tmpdir" >/dev/null 2>&1 + rmdir "$tmpdir" >/dev/null 2>&1 + + eoutdent + eend $code + + return $code + ;; + esac +} + diff --git a/sys-apps/tmpfs/tmpfs-1.0.ebuild b/sys-apps/tmpfs/tmpfs-1.0.ebuild new file mode 100644 index 00000000..b2d58344 --- /dev/null +++ b/sys-apps/tmpfs/tmpfs-1.0.ebuild @@ -0,0 +1,62 @@ +# Copyright 2010-2013 Stuart Shelton +# Distributed under the terms of the GNU General Public License v2 +# $Header: $ + +EAPI=4 + +DESCRIPTION="Mirror segments of a filesystem to a memory-based backing store" +HOMEPAGE="https://github.com/srcshelton/tmpfs" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="amd64 arm x86" +IUSE="+examples" + +# Keep portage happy, as there is nothing to unpack... +mkdir -p "${S}" + +src_install() { + newconfd "${FILESDIR}"/"${PN}".conf "${PN}" + newinitd "${FILESDIR}"/"${PN}".init "${PN}" + + if use examples; then + dodir /mnt/ram + dosym tmpfs /etc/init.d/tmpfs.ram + fi +} + +pkg_postinst() { + einfo "A sample configuration file has been deployed to /etc/conf.d/" + einfo "which will mount a ram-backed filesystem to /var/.mem" + einfo + + if use examples; then + einfo "The directory /mnt/ram has been created and /etc/init.d/tmpfs.ram" + einfo "has been created. In order to make use of this, perform the" + einfo "following actions:" + einfo + einfo " sudo rc-update add tmpfs.ram boot" + einfo " sudo /etc/init.d/tmpfs.ram start" + einfo + einfo "... and then relocate items beneath /var/ to /var/.mem/ and" + einfo "symlink them back to their original locations, e.g.:" + einfo + einfo " /var/tmp -> .mem/tmp" + einfo " /var/cache -> .mem/cache" + einfo " /var/lib/portage -> ../.mem/lib/portage" + einfo + einfo "Data stored in the memory-backed filesystem will then be" + einfo "restored to disk on shutdown." + einfo + einfo "To commit changes to disk without rebooting, run:" + einfo + einfo " /etc/init.d/tmpfs.ram commit" + einfo + ewarn "Please review /etc/conf.d/tmpfs before making any changes." + else + ewarn "If you wish to have a sample configuruation deployed, please" + ewarn "re-build with the 'examples' USE-flag enabled." + ewarn + ewarn "Please review /etc/conf.d/tmpfs before making any changes." + fi +} |