#!/usr/bin/env sh # doasedit # Link: git.io/doasedit # Dependencies: opendoas or doas # Author: Stanislav # 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