2021-10-17 21:48:12 +07:00

105 lines
3.4 KiB
Bash
Executable File

#!/usr/bin/env sh
# doasedit
# Link: git.io/doasedit
# Dependencies: opendoas <https://github.com/Duncaen/OpenDoas> or doas <https://cvsweb.openbsd.org/src/usr.bin/doas/>
# Author: Stanislav <git.io/monesonn>
# Description: doas equivalent to sudoedit.
# Licence: ISC
# Contribution: If you have some ideas, how to improve this script, feel free to make pull request or write an issue.
# Tips: Use this script with enabled persistence in doas.conf file, like:
#
# `permit persist :wheel`
#
# Also make sure that your doas.conf is owned by root with read-only permissions, like:
#
# # chown -c root:root /path/to/doas.conf && chmod 0400 path/to/doas.conf
#
# Throw error message.
err() {
printf "%s%s\n" "doasedit: " "${1}" >&2
exit "${2}"
}
# Catch arguments.
if [ -n "${2}" ]; then
err "expected only one argument" 1
elif [ -z "${1}" ]; then
err "no file path provided" 2
elif [ "$(id -u)" -eq 0 ]; then
err "doasedit: cannot be run as root" 3
elif [ -d "${1}" ]; then
err "${1} is a directory" 4
fi
# Safe shell options.
set -eu
# Reset doas password promt. If you want, feel free to comment this.
case "$(uname -s | tr '[:upper:]' '[:lower:]')" in
linux) doas -L ;;
esac
# Absolute path to the source file (file for editing).
src="$(doas readlink -f "${1}")"
# Filename for the source file.
filename="${src##*/}"
# If filename have suffix then also use it for a temporary file.
# It's kinda useful for editors, that have plugins or something else for certain file extensions.
[ "$filename" = "${filename##*.}" ] && filename_ext="" || filename_ext=".${filename##*.}"
# Be sure /tmp as tmp directory by setting TMPDIR env.
export TMPDIR=/tmp
# Create a temporary directory.
tmp_d=$(mktemp -d)
# Create a temporary file for the source file in a temporary directory.
tmp_f="${tmp_d}/${filename}$(xxd -l4 -ps /dev/urandom)${filename_ext}"
# File writeability condition.
if [ -w "$src" ]; then
err "$filename: editing files in a writable directory is not permitted" 5
fi
# Other conditions: if file exist, readable, etc.
if [ -f "${src}" ] && [ ! -r "${src}" ]; then
doas cat "${src}" > "${tmp_f}" || err "the file is not readable" 6
elif [ -r "${src}" ]; then
cat "${src}" > "${tmp_f}" || err "cannot transfer to content of the file to temporary one" 7
elif [ ! -f "${src}" ]; then
# Grant .rw-r--r-- permission for newly created files by default owned by root.
:> "${tmp_f}" && doas chmod -R 0644 "${tmp_f}" || err "cannot create new file" 8
else
err "unexpected script behaviour" 9
fi
# Create copy of the temporary file.
tmp_cf="${tmp_d}/${filename}$(xxd -l4 -ps /dev/urandom).copy"
# Hooks for recursive removing of a temporary directory.
trap 'rm -rf ${tmp_d}' EXIT HUP QUIT TERM INT ABRT
# Move the contents of a temporary file to its copy for later comparison.
cat "${tmp_f}" > "${tmp_cf}"
# Editing the file by the user using the default editor, if not specified, the vi is used.
${EDITOR:-vi} "${tmp_f}"
# Compare the temporary file and the temporary copy.
if cmp -s "${tmp_f}" "${tmp_cf}"; then
printf "%s\n" "doasedit: $filename unchanged"
exit 0
else
# Replace the source file with temporary, repeats three times if it fails.
for doas_tries in $(seq 1 3); do doas cp -f "${tmp_f}" "${src}" && break; done
printf "%s\n" "doasedit: $filename changes are accepted"
exit 0
fi