Initial commit.

This commit is contained in:
2021-05-24 22:18:33 +03:00
commit e2954d55f4
3701 changed files with 330017 additions and 0 deletions

17
etckeeper/commit.d/10vcs-test Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
set -e
not_enabled_warning() {
echo "etckeeper warning: etckeeper is not yet enabled for $(pwd)" >&2
echo "etckeeper warning: run etckeeper init to enable it" >&2
}
if [ "$VCS" = git ] && [ ! -d .git ]; then
not_enabled_warning
elif [ "$VCS" = hg ] && [ ! -d .hg ]; then
not_enabled_warning
elif [ "$VCS" = bzr ] && [ ! -d .bzr ]; then
not_enabled_warning
elif [ "$VCS" = darcs ] && [ ! -d _darcs ]; then
not_enabled_warning
fi

View File

@@ -0,0 +1 @@
../pre-commit.d/30store-metadata

8
etckeeper/commit.d/30bzr-add Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
if [ "$VCS" = bzr ] && [ -d .bzr ]; then
if ! bzr add -q .; then
echo "etckeeper warning: bzr add failed" >&2
fi
fi

14
etckeeper/commit.d/30darcs-add Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
set -e
if [ "$VCS" = darcs ] && [ -d _darcs ]; then
rc=0
res=$( darcs add -qr . 2>&1 ) || rc=$?
if test $rc -ne 0; then
if ! test $rc -eq 2 -a "${res%No files were added}" != "$res"; then
printf "%s" "$res"
echo "etckeeper warning: darcs add failed" >&2
fi
fi
unset rc res
fi

8
etckeeper/commit.d/30git-add Executable file
View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
if [ "$VCS" = git ] && [ -d .git ]; then
if ! git add --all; then
echo "etckeeper warning: git add --all" >&2
fi
fi

View File

@@ -0,0 +1,8 @@
#!/bin/sh
set -e
if [ "$VCS" = hg ] && [ -d .hg ]; then
if ! hg addremove .; then
echo "etckeeper warning: hg addremove failed" >&2
fi
fi

142
etckeeper/commit.d/50vcs-commit Executable file
View File

@@ -0,0 +1,142 @@
#!/bin/sh
set -e
cleanup () {
if [ -n "$logfile" ]; then
rm -f "$logfile"
fi
}
if [ -n "$1" ]; then
trap cleanup EXIT
logfile="$(mktemp -t etckeeper-$VCS.XXXXXXXXXX)"
if [ "x$1" = "x--stdin" ]; then
cat > "$logfile"
else
sed '1s/^-m \{0,1\}//' >"$logfile" <<-EOF
$*
EOF
fi
else
logfile=""
fi
hostname=`hostname 2>/dev/null || cat /etc/hostname`
hostname="${hostname%%.*}"
dnsdomainname=`dnsdomainname 2>/dev/null || true`
if [ -n "$dnsdomainname" ]; then
hostname="$hostname.$dnsdomainname"
fi
ORIG_USER=$USER
USER=
if [ -n "$SUDO_USER" ]; then
USER="$SUDO_USER"
else
# try to check tty ownership, in case user su'd to root
TTY="$(tty 2>/dev/null || true)"
if [ -n "$TTY" ] && [ -c "$TTY" ]; then
USER="$(find "$TTY" -printf "%u")"
fi
fi
if [ "$VCS" = git ] && [ -d .git ]; then
# When not su'd to root, still set environment variables,
# since git's own code to determine the author and committer
# has several edge cases where it fails and would prevent the
# commit.
if [ -z "$USER" ]; then
USER="$(whoami)"
fi
if [ -n "$USER" ]; then
# Use user.name and user.email from the gitconfig belonging
# to USER.
USER_HOME="$(getent passwd "$USER" | cut -d: -f6)"
if [ -n "$USER_HOME" ] && [ -e "$USER_HOME/.gitconfig" ]; then
if [ -z "$GIT_AUTHOR_NAME" ]; then
GIT_AUTHOR_NAME="$(git config -f "$USER_HOME/.gitconfig" user.name)" || true
export GIT_AUTHOR_NAME
fi
if [ -z "$GIT_AUTHOR_EMAIL" ]; then
GIT_AUTHOR_EMAIL="$(git config -f "$USER_HOME/.gitconfig" user.email)" || true
export GIT_AUTHOR_EMAIL
fi
fi
if [ -z "$GIT_AUTHOR_NAME" ] || [ -z "$GIT_AUTHOR_EMAIL" ]; then
if [ -n "$USER_HOME" ] && [ -e "$USER_HOME/.config/git/config" ]; then
if [ -z "$GIT_AUTHOR_NAME" ]; then
GIT_AUTHOR_NAME="$(git config -f "$USER_HOME/.config/git/config" user.name)" || true
export GIT_AUTHOR_NAME
fi
if [ -z "$GIT_AUTHOR_EMAIL" ]; then
GIT_AUTHOR_EMAIL="$(git config -f "$USER_HOME/.config/git/config" user.email)" || true
export GIT_AUTHOR_EMAIL
fi
fi
fi
if [ -z "$GIT_COMMITTER_EMAIL" ]; then
GIT_COMMITTER_EMAIL="$(git config --global user.email)" || true
export GIT_COMMITTER_EMAIL
fi
if [ -z "$GIT_AUTHOR_NAME" ]; then
GIT_AUTHOR_NAME="$USER"
export GIT_AUTHOR_NAME
fi
if [ -z "$GIT_AUTHOR_EMAIL" ]; then
GIT_AUTHOR_EMAIL="$USER@$hostname"
export GIT_AUTHOR_EMAIL
fi
if [ -z "$GIT_COMMITTER_EMAIL" ]; then
GIT_COMMITTER_EMAIL=`whoami`"@$hostname"
export GIT_COMMITTER_EMAIL
fi
fi
# gc ten times more frequently than the default
# (unless some other config is set)
GIT_GC_OPTIONS=
if ! git config gc.auto >/dev/null; then
GIT_GC_OPTIONS="-c gc.auto=670"
fi
if [ -n "$logfile" ]; then
git $GIT_GC_OPTIONS commit $GIT_COMMIT_OPTIONS -F "$logfile"
else
git $GIT_GC_OPTIONS commit $GIT_COMMIT_OPTIONS
fi
elif [ "$VCS" = hg ] && [ -d .hg ]; then
if [ -n "$USER" ]; then
LOGNAME="$USER"
export LOGNAME
fi
if [ -z "$HGUSER" ]; then
HGUSER="$USER@$hostname"
export HGUSER
fi
if [ -n "$logfile" ]; then
hg commit $HG_COMMIT_OPTIONS -l "$logfile"
else
hg commit $HG_COMMIT_OPTIONS
fi
elif [ "$VCS" = bzr ] && [ -d .bzr ]; then
if [ -z "$EMAIL" ] && [ -n "$USER" ]; then
EMAIL="$USER <$USER@$hostname>"
export EMAIL
else
bzr whoami >/dev/null 2>&1 || export EMAIL="$ORIG_USER <$ORIG_USER@$hostname>"
fi
if [ -n "$logfile" ]; then
bzr commit $BZR_COMMIT_OPTIONS -F "$logfile"
else
bzr commit $BZR_COMMIT_OPTIONS
fi
elif [ "$VCS" = darcs ] && [ -d _darcs ]; then
if [ -z "$USER" ]; then
USER=root
fi
if [ -n "$logfile" ]; then
darcs record --author="$USER" $DARCS_COMMIT_OPTIONS --logfile="$logfile"
else
darcs record --author="$USER" $DARCS_COMMIT_OPTIONS
fi
fi

14
etckeeper/commit.d/99push Executable file
View File

@@ -0,0 +1,14 @@
#!/bin/sh
if [ -n "$PUSH_REMOTE" ]; then
if [ "$VCS" = git ] && [ -d .git ]; then
for REMOTE in $PUSH_REMOTE; do
git push "$REMOTE" || true
done
elif [ "$VCS" = hg ] && [ -d .hg ]; then
for REMOTE in $PUSH_REMOTE; do
hg push "$REMOTE" || true
done
else
echo "PUSH_REMOTE not yet supported for $VCS" >&2
fi
fi

View File

@@ -0,0 +1,3 @@
Files in this directory are run when there might be changes to commit.
(Before and after packages are installed, upgraded, etc.)
They should commit changes and new files in /etc to repository.

17
etckeeper/daily Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/sh
# Script that can be run daily to autocommit /etc changes.
set -e
if [ -x /usr/bin/etckeeper ] && [ -e /etc/etckeeper/etckeeper.conf ]; then
# avoid autocommit if an install run is in progress
lockfile=/var/cache/etckeeper/packagelist.pre-install
if [ -e "$lockfile" ] && [ -n "$(find "$lockfile" -mtime +1)" ]; then
rm -f "$lockfile" # stale
fi
if [ ! -e "$lockfile" ]; then
AVOID_SPECIAL_FILE_WARNING=1
export AVOID_SPECIAL_FILE_WARNING
if etckeeper unclean; then
etckeeper commit "daily autocommit" >/dev/null
fi
fi
fi

45
etckeeper/etckeeper.conf Normal file
View File

@@ -0,0 +1,45 @@
# The VCS to use.
#VCS="hg"
VCS="git"
#VCS="bzr"
#VCS="darcs"
# Options passed to git commit when run by etckeeper.
GIT_COMMIT_OPTIONS=""
# Options passed to hg commit when run by etckeeper.
HG_COMMIT_OPTIONS=""
# Options passed to bzr commit when run by etckeeper.
BZR_COMMIT_OPTIONS=""
# Options passed to darcs record when run by etckeeper.
DARCS_COMMIT_OPTIONS="-a"
# Etckeeper includes both a cron job and a systemd timer, which each
# can commit exiting changes to /etc automatically once per day.
# To enable the systemd timer, run: systemctl enable etckeeper.timer
# The cron job is enabled by default; to disable it, uncomment this next line.
#AVOID_DAILY_AUTOCOMMITS=1
# Uncomment the following to avoid special file warning
# (the option is enabled automatically for daily autocommits regardless).
#AVOID_SPECIAL_FILE_WARNING=1
# Uncomment to avoid etckeeper committing existing changes to
# /etc before installation. It will cancel the installation,
# so you can commit the changes by hand.
#AVOID_COMMIT_BEFORE_INSTALL=1
# The high-level package manager that's being used.
# (apt, pacman, pacman-g2, yum, dnf, zypper, apk etc)
HIGHLEVEL_PACKAGE_MANAGER=dnf
# The low-level package manager that's being used.
# (dpkg, rpm, pacman, pacmatic, pacman-g2, apk etc)
LOWLEVEL_PACKAGE_MANAGER=yum
# To push each commit to a remote, put the name of the remote here.
# (eg, "origin" for git). Space-separated lists of multiple remotes
# also work (eg, "origin gitlab github" for git).
PUSH_REMOTE="origin"

View File

@@ -0,0 +1,14 @@
#!/bin/sh
set -e
# Note that metastore doesn't check that the .metastore file only changes
# perms of files in the current directory. It's ok to trust the .metastore
# file won't do anything shady, because, as documented, etckeeper-init
# should only be run on repositories you trust.
if [ -e .metadata ]; then
if command -v metastore >/dev/null; then
metastore --apply --mtime
else
echo "etckeeper warning: legacy .metastore file is present but metastore is not installed" >&2
fi
fi

View File

@@ -0,0 +1,22 @@
#!/bin/sh
set -e
# Used by .etckeeper to run a command if the file it acts on
# (the last parameter) exists.
maybe () {
command="$1"
shift 1
if eval [ -e "\"\$$#\"" ]; then
"$command" "$@"
fi
}
# Yes, this runs code from the repository. As documented, etckeeper-init
# should only be run on repositories you trust.
if [ -e .etckeeper ]; then
. ./.etckeeper
else
touch .etckeeper
chmod 600 .etckeeper
fi

18
etckeeper/init.d/40vcs-init Executable file
View File

@@ -0,0 +1,18 @@
#!/bin/sh
set -e
description="$(hostname 2>/dev/null || cat /etc/hostname) /etc repository"
if [ "$VCS" = git ] && [ ! -e .git ]; then
git init
echo "$description" > .git/description
elif [ "$VCS" = hg ] && [ ! -e .hg ]; then
hg init
echo "[web]" > .hg/hgrc
echo "description = $description" >> .hg/hgrc
elif [ "$VCS" = bzr ] && [ ! -e .bzr ]; then
bzr init
bzr nick "$description"
elif [ "$VCS" = darcs ] && [ ! -e _darcs ]; then
darcs initialize
echo "$description" > _darcs/prefs/motd
fi

4
etckeeper/init.d/50vcs-ignore Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
set -e
etckeeper update-ignore -a || true

12
etckeeper/init.d/50vcs-perm Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
set -e
if [ "$VCS" = git ]; then
chmod 700 .git
elif [ "$VCS" = hg ]; then
chmod 700 .hg
elif [ "$VCS" = bzr ]; then
chmod 700 .bzr
elif [ "$VCS" = darcs ]; then
chmod 700 _darcs
fi

View File

@@ -0,0 +1,49 @@
#!/bin/sh
set -e
case "$VCS" in
git)
if [ -x .git/hooks/pre-commit ]; then
if ! grep -q "etckeeper pre-commit" .git/hooks/pre-commit; then
echo "etckeeper warning: .git/hooks/pre-commit needs to be manually modified to run: etckeeper pre-commit -d `pwd`" >&2
fi
else
cat >.git/hooks/pre-commit <<EOF
#!/bin/sh
# pre-commit hook for etckeeper, to store metadata and do sanity checks
set -e
etckeeper pre-commit -d `pwd`
EOF
chmod +x .git/hooks/pre-commit
fi
;;
hg)
if [ -e .hg/hgrc ] && grep "^\[hooks\]" .hg/hgrc; then
if ! grep "^pre-commit" .hg/hgrc | grep -q "etckeeper pre-commit"; then
echo "etckeeper warning: [hooks] section in .hg/hgrc needs to be manually modified to contain: pre-commit = etckeeper pre-commit -d `pwd`" >&2
fi
else
touch .hg/hgrc
cat >>.hg/hgrc <<EOF
[hooks]
# pre-commit hook for etckeeper, to store metadata and do sanity checks
pre-commit = etckeeper pre-commit -d `pwd`
EOF
fi
;;
darcs)
if [ -e _darcs/prefs/defaults ]; then
if ! ( grep -q "record prehook etckeeper pre-commit" _darcs/prefs/defaults &&
grep -q "whatsnew prehook etckeeper pre-commit" _darcs/prefs/defaults ); then
echo "etckeeper warning: _darcs/prefs/defaults needs to be manually modified to run: etckeeper pre-commit -d `pwd`" >&2
fi
else
cat >_darcs/prefs/defaults <<EOF
record prehook etckeeper pre-commit -d `pwd`
record run-prehook
whatsnew prehook etckeeper pre-commit -d `pwd`
whatsnew run-prehook
EOF
fi
;;
esac

View File

@@ -0,0 +1,48 @@
#!/bin/sh
set -e
filter_ignore() {
if [ "$VCS" = darcs ]; then
ignorefile=.darcsignore
fi
if [ "$VCS" = darcs ] && [ -e "$ignorefile" ]; then
# Spaces embedded into patterns would break it.
# But really, why would anyone want to use ' ' instead of '\s' ?
#patterns=$( grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" | xargs -n 1 printf " -e %s" )
#grep -Ev $patterns
#unset patterns
# Alternative using a temp file
patternsfile="$( mktemp -t etckeeper-$VCS.XXXXXXXXXX )"
grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" > "$patternsfile" || true
grep -Evf "$patternsfile"
rm -f "$patternsfile"
unset patternsfile
else
cat -
fi
}
if [ "$VCS" = darcs ];then
NOVCS='. -path ./.git -prune -o -path ./.bzr -prune -o -path ./.hg -prune -o -path ./_darcs -prune -o'
# We assume that if .etckeeper is empty this is the first run
if [ -s .etckeeper ]; then
linksindex="$( mktemp -t etckeeper-$VCS.XXXXXXXXXX )"
grep '^ln -s' .etckeeper | while IFS="'" read n n n link n; do
printf "%s\n" "$link" >> "$linksindex"
done
# Warn about symbolic links that shouldn't exist
if links=$( find $NOVCS -type l -print | filter_ignore | grep -vFf "$linksindex" ); then
printf "%s\n%s\n" \
"The following symbolic links should not exist:" \
"$links" >&2
fi
rm -f "$linksindex"
unset links linksindex
fi
fi

27
etckeeper/init.d/70vcs-add Executable file
View File

@@ -0,0 +1,27 @@
#!/bin/sh
set -e
if [ "$VCS" = git ]; then
if ! git add .; then
echo "etckeeper warning: git add failed" >&2
fi
elif [ "$VCS" = hg ]; then
if ! hg add .; then
echo "etckeeper warning: hg add failed" >&2
fi
elif [ "$VCS" = bzr ]; then
if ! bzr add .; then
echo "etckeeper warning: bzr add failed" >&2
fi
elif [ "$VCS" = darcs ]; then
# Don't warn if all the files were already added.
rc=0
res=$( darcs add -qr . 2>&1 ) || rc=$?
if test $rc -ne 0; then
if ! test $rc -eq 2 -a "${res%No files were added}" != "$res"; then
printf "%s" "$res"
echo "etckeeper warning: darcs add failed" >&2
fi
fi
unset rc res
fi

13
etckeeper/init.d/README Normal file
View File

@@ -0,0 +1,13 @@
Executable files in this directory are run to initialise the working directory
for use by etckeeper. If the working directory is not already in version
control, that includes setting up the version control, but not actually
committing anything. If the working directory is in version control,
it includes applying stored metadata to the checked out files in the
working directory.
Please be careful to *never* overwrite existing files/directories
in the working directory (or use absolute care when doing so). If a file
you need to write already exists, check if its contents are sane, and
if not, emit a warning on stderr.
If initialisation fails, exit nonzero and no later files will be run.

View File

@@ -0,0 +1,32 @@
#!/bin/sh
if [ "$1" = fmt ]; then
# If the list format changes, change the fmt
if [ "$LOWLEVEL_PACKAGE_MANAGER" = dpkg ]; then
echo 2
else
echo ""
fi
else
# Keep the sort order the same at all times.
LC_COLLATE=C
export LC_COLLATE
unset LC_ALL
# Output to stdout a *sorted* list of all currently installed
# (or removed but still with config-files) packages, in the
# format "package version\n" (or something similar).
if [ "$LOWLEVEL_PACKAGE_MANAGER" = dpkg ]; then
dpkg-query -W -f '${Status}\t${Package} ${Version} ${Architecture}\n' | \
egrep '(ok installed|ok config-files)' | cut -f2,3
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = rpm ]; then
rpm -qa --qf "%|epoch?{%{epoch}}:{0}|:%{name}-%{version}-%{release}.%{arch}\n" | sort
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = pacman ]; then
pacman -Q
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = pacmatic ]; then
pacmatic -Q
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = pkgng ]; then
pkg info -E "*"
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = apk ]; then
apk info -v | sort
fi
fi

View File

@@ -0,0 +1,91 @@
#!/bin/sh
set -e
pl="/var/cache/etckeeper/packagelist"
# Parent process is etckeeper
# (Only procps ps is currently supported, others will fail,
# so this may end up empty.)
ETCKEEPER_PID=$( ps --no-headers -o ppid "${PPID}" 2>/dev/null | sed 's/^ *//' )
# Find the parent of etckeeper and get the command line of the process
if ! [ -z "${ETCKEEPER_PID}" ]; then
ETCKEEPER_PPID=$( ps --no-headers -o ppid "${ETCKEEPER_PID}" | sed 's/^ *//' )
ETCKEEPER_PARENT_COMMAND_LINE=$( ps --no-headers -o args "${ETCKEEPER_PPID}" )
fi
get_changes () {
if [ "$VCS" = git ]; then
git diff --stat | grep '|' | cut -d'|' -f1 | cut -b2-
git ls-files --exclude-standard --others
fi
if [ "$VCS" = hg ]; then
hg status --no-status
fi
if [ "$VCS" = bzr ]; then
bzr status -S | cut -b5-
fi
if [ "$VCS" = darcs ]; then
# ignore ' file -> file' lines for moved files
# trim ' -M +N rP' from change summary
darcs whatsnew --summary | grep -v '^ .* -> ' | cut -d' ' -f2- | sed 's/ [-+r][0-9]\+//g;s/^\.\///'
# lines beginning with 'a' show unversioned files
darcs whatsnew --look-for-adds --boring --summary | grep '^a' | cut -d' ' -f2- | sed 's/^\.\///'
fi
}
get_changed_packages () {
if [ "$LOWLEVEL_PACKAGE_MANAGER" = dpkg ]; then
get_changes | sed 's/^/\/etc\//;s/\s*$//' | xargs -d '\n' dpkg 2>/dev/null -S | cut -d':' -f1 | sed 's/, /\n/g'
fi
if [ "$LOWLEVEL_PACKAGE_MANAGER" = rpm ]; then
# if output contains file path, file was not found
get_changes | sed 's/^/\/etc\//;s/\s*$//' | xargs -d '\n' rpm --qf '%{NAME}\n' -qf | grep -v "/etc/"
fi
# is it even possible to use pacmatic without pacman?
if [ "$LOWLEVEL_PACKAGE_MANAGER" = pacman -o "$LOWLEVEL_PACKAGE_MANAGER" = pacmatic ]; then
get_changes | sed 's/^/\/etc\//;s/\s*$//' | xargs -d '\n' pacman 2>/dev/null -Qo | rev | cut -d' ' -f1-2 | rev | cut -d' ' -f1
fi
if [ "$LOWLEVEL_PACKAGE_MANAGER" = pkgng ]; then
get_changes | sed 's/^/\/etc\//;s/\s*$//' | xargs -d '\n' pkg which --quiet | rev | cut -d'-' -f2- | rev
fi
}
if etckeeper unclean; then
if [ -z "${ETCKEEPER_PARENT_COMMAND_LINE}" ]; then
message="committing changes in /etc after $HIGHLEVEL_PACKAGE_MANAGER run"
else
message="committing changes in /etc made by \"$ETCKEEPER_PARENT_COMMAND_LINE\""
fi
set +e
if [ -e $pl.pre-install ] && [ "$(cat $pl.fmt 2>/dev/null || true)" = "$(etckeeper list-installed fmt)" ]; then
(
echo "$message"
echo
get_changed_packages | sort | uniq > $pl.found-pkgs
if [ -s $pl.found-pkgs ]; then
sed -i 's/^/^[-+]/;s/$/ /' $pl.found-pkgs
etckeeper list-installed | diff -U0 $pl.pre-install - | tail -n+4 | egrep '^[-+]' | grep -f $pl.found-pkgs > $pl.found-packages
if [ -s $pl.found-packages ]; then
echo "Packages with configuration changes:"
cat $pl.found-packages || true
echo
fi
fi
echo "Package changes:"
etckeeper list-installed | diff -U0 $pl.pre-install - | tail -n+4 | egrep '^[-+]' || true
) | etckeeper commit --stdin
else
etckeeper commit "$(printf "$message")"
fi
status=$?
set -e
if [ "$status" != 0 ]; then
echo "warning: etckeeper failed to commit changes in /etc using $VCS" >&2
fi
fi
rm -f $pl.pre-install $pl.fmt
rm -f $pl.found-pkgs $pl.found-packages

View File

@@ -0,0 +1,2 @@
Files in this directory are run after packages are installed, upgraded, etc.
They should commit changes and new files in /etc to repository.

View File

@@ -0,0 +1,31 @@
#!/bin/sh
set -e
# (Note that when using this, the find expression must end with
# -print or -exec, else the excluded directories will actually be
# printed!)
NOVCS='. -path ./.git -prune -o -path ./.bzr -prune -o -path ./.hg -prune -o -path ./_darcs -prune -o'
if [ "$VCS" = bzr ] || [ "$VCS" = darcs ]; then
special=$(find $NOVCS ! -type d ! -type f ! -type l -print) || true
hardlinks=$(find $NOVCS -type f ! -links 1 -print) || true
elif [ "$VCS" = hg ]; then
special=$(find $NOVCS ! -type d ! -type f ! -type l -print) || true
hardlinks=$(find $NOVCS -type f ! -links 1 -exec hg status {} \; -print) || true
elif [ "$VCS" = git ]; then
special=$(find $NOVCS ! -type d ! -type f ! -type l -exec git ls-files --exclude-standard --cached --others {} + -print) || true
hardlinks=$(find $NOVCS -type f ! -links 1 -exec git ls-files --exclude-standard --cached --others {} + -print) || true
else
special=""
fi
if [ -n "$special" ] && [ -z "$AVOID_SPECIAL_FILE_WARNING" ]; then
echo "etckeeper warning: special files could cause problems with $VCS:" >&2
echo "$special" >&2
fi
if [ -n "$hardlinks" ] && [ -z "$AVOID_SPECIAL_FILE_WARNING" ]; then
echo "etckeeper warning: hardlinked files could cause problems with $VCS:" >&2
echo "$hardlinks" >&2
fi
true

View File

@@ -0,0 +1,171 @@
#!/bin/sh
set -e
# Keep the sort order the same at all times.
LC_COLLATE=C
export LC_COLLATE
unset LC_ALL
filter_ignore() {
case "$VCS" in
darcs) ignorefile=.darcsignore ;;
git) ignorefile=.gitignore ;;
esac
if [ -n "$ignorefile" ] && [ -e "$ignorefile" ]; then
listfile="$( mktemp -t etckeeper-$VCS.XXXXXXXXXX )"
case "$VCS" in
darcs)
grep -v '^[[:space:]]*\(#\|$\)' "$ignorefile" > "$listfile" || true
grep -Evf "$listfile"
;;
git)
(git ls-files -oi --exclude-standard; git ls-files -oi --exclude-standard --directory) | sort | uniq > "$listfile" || true
if [ -s "$listfile" ]; then
sed 's/^\.\///' | grep -xFvf "$listfile"
else
cat -
fi
;;
esac
rm -f "$listfile"
unset listfile
else
cat -
fi
}
shellquote() {
# Single quotes text, escaping existing single quotes.
sed -e "s/'/'\"'\"'/g" -e "s/^/'/" -e "s/$/'/"
}
generate_metadata() {
# This function generates the script commands to fix any file
# ownerships that aren't owner=root, group=root, as well as to
# store the permissions of files.
# The script is produced on stdout. Errors go to stderr.
#
# The script can use a 'maybe' function, which only runs a command
# if the file in its last argument exists.
# We want files in the directory containing VCS data
# but we want find to ignore the VCS files themselves.
#
# (Note that when using this, the find expression must end with
# -print or -exec, else the excluded directories will actually be
# printed!)
NOVCS='. -path ./.git -prune -o -path ./.bzr -prune -o -path ./.hg -prune -o -path ./_darcs -prune -o'
if [ "$VCS" = git ] || [ "$VCS" = hg ]; then
# These version control systems do not track directories,
# so empty directories must be stored specially.
find $NOVCS -type d -empty -print |
sort | shellquote | sed -e "s/^/mkdir -p /"
fi
if [ "$VCS" = darcs ]; then
# This version control system does not track symlinks,
# so they must be stored specially.
find $NOVCS -type l -print | sort | filter_ignore | while read link; do
dest=$( readlink "$link" )
printf "ln -sf '%s' '%s'\n" "$(echo "$dest" | shellquote)" "$(echo "$link" | shellquote)"
done
fi
# Store things that don't have the default user or group.
# Store all file modes, in case the user has an unusual umask.
find $NOVCS \( -type f -or -type d \) -print | filter_ignore | sort | maybe_chmod_chown
# We don't handle xattrs.
# Maybe check for getfattr/setfattr and use them if they're available?
}
maybe_chmod_chown() {
if command -v perl >/dev/null; then
perl -ne '
BEGIN { $q=chr(39) }
sub uidname {
my $want=shift;
if (exists $uidcache{$want}) {
return $uidcache{$want};
}
my $name=scalar getpwuid($want);
return $uidcache{$want}=defined $name ? $name : $want;
}
sub gidname {
my $want=shift;
if (exists $gidcache{$want}) {
return $gidcache{$want};
}
my $name=scalar getgrgid($want);
return $gidcache{$want}=defined $name ? $name : $want;
}
chomp;
my @stat=stat($_);
my $mode = $stat[2];
my $uid = $stat[4];
my $gid = $stat[5];
s/$q/$q"$q"$q/g; # escape single quotes
s/^/$q/;
s/$/$q/;
if ($uid != $>) {
printf "maybe chown $q%s$q %s\n", uidname($uid), $_;
}
if ($gid != $)) {
printf "maybe chgrp $q%s$q %s\n", gidname($gid), $_;
}
printf "maybe chmod %04o %s\n", $mode & 07777, $_;
'
return $?
else
# fallback if perl isn't present
euid=$(id -u)
egid=$(id -g)
q="'"
while read x; do
stat=$(stat -c "%f:%u:%g:%a:%U:%G" $x)
IFS=":" read mode uid gid perm uname gname <<EOF
$stat
EOF
x=$q$(echo $x | sed "s/$q/$q\"$q\"$q/")$q
if [ $uid -ne $euid ]; then
echo maybe chown "'$uname'" $x
fi
if [ $gid -ne $egid ]; then
echo maybe chgrp "'$gname'" $x
fi
echo maybe chmod 0$perm $x
done
fi
}
if [ "$VCS" = git ] || [ "$VCS" = hg ] || [ "$VCS" = bzr ] || [ "$VCS" = darcs ]; then
if [ -f .metadata ]; then
# remove obsolete .metadata file
# git allows fully deleting it at this point, other VCS
# may not (the repo is locked for hg).
if [ "$VCS" = git ]; then
$VCS rm .metadata
else
rm -f .metadata
fi
fi
echo "# Generated by etckeeper. Do not edit." > .etckeeper
echo >> .etckeeper
# Make sure the file is not readable by others, since it can leak
# information about contents of non-readable directories in /etc.
chmod 700 .etckeeper
generate_metadata >> .etckeeper
# stage the file as part of the current commit
if [ "$VCS" = git ]; then
# this will do nothing if the metadata file is unchanged.
git add .etckeeper
fi
# hg, bzr and darcs add not done, they will automatically
# include the file in the current commit
fi

View File

@@ -0,0 +1,2 @@
This is run by a git pre-commit hook before committing changes to the
repository. This can be used for storing metadata, and for sanity checks.

View File

@@ -0,0 +1,5 @@
#!/bin/sh
# This list will be later used when committing.
mkdir -p /var/cache/etckeeper/
etckeeper list-installed > /var/cache/etckeeper/packagelist.pre-install
etckeeper list-installed fmt > /var/cache/etckeeper/packagelist.fmt

View File

@@ -0,0 +1,15 @@
#!/bin/sh
set -e
if etckeeper unclean; then
if [ "$AVOID_COMMIT_BEFORE_INSTALL" = 1 ]; then
echo "" >&2
echo "** etckeeper detected uncommitted changes in /etc prior to $HIGHLEVEL_PACKAGE_MANAGER run" >&2
echo "** Aborting $HIGHLEVEL_PACKAGE_MANAGER run. Manually commit and restart." >&2
echo "" >&2
exit 1
fi
if ! etckeeper commit "saving uncommitted changes in /etc prior to $HIGHLEVEL_PACKAGE_MANAGER run"; then
echo "warning: etckeeper failed to commit changes in /etc using $VCS" >&2
fi
fi

View File

@@ -0,0 +1,3 @@
Files in this directory are run before packages are installed, upgraded,
etc. This is mostly used for sanity checks, ie, does /etc have any
uncommitted changes?

12
etckeeper/unclean.d/50test Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/sh
set -e
if [ "$VCS" = git ]; then
[ -d .git ] && [ -n "`git status --porcelain`" ]
elif [ "$VCS" = hg ]; then
[ -d .hg ] && ! hg status 2>&1 | wc -l | grep -q "^0$"
elif [ "$VCS" = bzr ]; then
[ -d .bzr ] && ! bzr version-info --custom --template="{clean}\n" | grep -q "^1$"
elif [ "$VCS" = darcs ]; then
[ -d _darcs ] && darcs whatsnew -l >/dev/null
fi

View File

@@ -0,0 +1,2 @@
Files in this directory are used to test if the working copy has
uncommitted changes.

20
etckeeper/uninit.d/01prompt Executable file
View File

@@ -0,0 +1,20 @@
#!/bin/sh
set -e
if [ "$1" != "-f" ]; then
echo "** Warning: This will DESTROY all recorded history for $ETCKEEPER_DIR,"
echo "** including the $VCS repository."
echo ""
printf "Are you sure you want to do this? [yN] "
read answer
case "$answer" in
[Yy]*)
echo "Proceeding.."
exit 0
;;
*)
echo "Aborting etckeeper uninit."
exit 1
;;
esac
fi

View File

@@ -0,0 +1,6 @@
#!/bin/sh
set -e
# Files generated by etckeeper to store metadata the VCS cannot preserve.
rm -f .etckeeper
rm -f .metadata # only generated by old versions

54
etckeeper/uninit.d/50vcs-uninit Executable file
View File

@@ -0,0 +1,54 @@
#!/bin/sh
set -e
if [ "$VCS" = git ]; then
rm -rf .git
file=.gitignore
elif [ "$VCS" = hg ]; then
rm -rf .hg
file=.hgignore
elif [ "$VCS" = bzr ]; then
rm -rf .bzr
file=.bzrignore
elif [ "$VCS" = darcs ]; then
rm -rf _darcs
file=.darcsignore
fi
managed_by_etckeeper="managed by etckeeper"
if ! grep -q "$managed_by_etckeeper" "$file"; then
exit 0
else
realfile="$file"
if command -v mktemp >/dev/null; then
tempfile="mktemp"
elif command -v tempfile >/dev/null; then
tempfile="tempfile"
else
echo "etckeeper warning: can't find tempfile or mktemp" >&2
exit 1
fi
file=$($tempfile)
otherentries=
skipping=
while read -r line; do
if echo "$line" | grep -q "$managed_by_etckeeper"; then
if [ ! "$skipping" ]; then
skipping=1
else
skipping=
fi
elif [ ! "$skipping" ]; then
echo "$line" >> "$file"
otherentries=1
fi
done <"$realfile"
if [ "$otherentries" ]; then
mv -f "$file" "$realfile"
else
rm -f "$file"
rm -f "$realfile"
fi
fi

View File

@@ -0,0 +1,2 @@
Executable files in this directory are run to uninitialise the working
directory, removing files added by `etckeeper init`.

View File

@@ -0,0 +1,216 @@
#!/bin/sh
set -e
if [ "$VCS" = git ]; then
dir=.git
file=.gitignore
elif [ "$VCS" = hg ]; then
dir=.hg
file=.hgignore
elif [ "$VCS" = bzr ]; then
dir=.bzr
file=.bzrignore
elif [ "$VCS" = darcs ]; then
dir=_darcs
file=.darcsignore
else
echo "etckeeper: unsupported VCS $VCS" >&2
exit 1
fi
if [ ! -d "$dir" ]; then
exit 0
fi
managed_by_etckeeper="managed by etckeeper"
nl() {
echo >>"$file"
}
comment() {
comment="$1"
echo "# $comment" >>"$file"
}
ignore() {
glob="$1"
case "$VCS" in
git)
# escape "#" in ignores, as otherwise it may
# be considered a comment
echo "$glob" | sed 's/#/\\#/g' >>"$file"
;;
bzr)
echo "$glob" >>"$file"
;;
hg)
# rather than converting the glob to a regexp, just
# configure hg to use globs
if [ -z "$hg_syntax_printed" ]; then
comment "use glob syntax"
echo "syntax: glob" >>"$file"
nl
hg_syntax_printed=1
fi
echo "$glob" | sed 's/#/\\#/g' >>"$file"
;;
darcs)
# darcs doesn't understand globs, so we need to
# translate them into regexs. Not a complete converter,
# but suitable for given globs.
if [ "${glob%\*}" != "$glob" ]; then
glob="${glob%\*}"
else
glob="$glob"'($|/)'
fi
if [ "${glob#\*}" != "$glob" ]; then
glob="${glob#\*}"
else
glob='(^|/)'"$glob"
fi
glob="$( printf %s $glob | sed -e 's/\./\\./g;s/\*/[^\/]*/g;s/\?/[^\/]/g' )"
echo "$glob" >>"$file"
esac
}
writefile () {
comment "begin section $managed_by_etckeeper (do not edit this section by hand)"
nl
if [ "$VCS" = darcs ]; then
darcs setpref boringfile .darcsignore
fi
if [ "$LOWLEVEL_PACKAGE_MANAGER" = dpkg ]; then
comment "new and old versions of conffiles, stored by dpkg"
ignore "*.dpkg-*"
comment "new and old versions of conffiles, stored by ucf"
ignore "*.ucf-*"
nl
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = "rpm" ]; then
comment "new and old versions of conffiles, stored by apt/rpm"
ignore "*.rpmnew"
ignore "*.rpmorig"
ignore "*.rpmsave"
nl
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = "pacman-g2" -o "$LOWLEVEL_PACKAGE_MANAGER" = "pacman" -o "$LOWLEVEL_PACKAGE_MANAGER" = "pacmatic" ]; then
comment "new and old versions of conffiles, stored by pacman"
ignore "*.pacnew"
ignore "*.pacorig"
ignore "*.pacsave"
nl
elif [ "$LOWLEVEL_PACKAGE_MANAGER" = "apk" ]; then
comment "new versions of conffiles, stored by apk"
ignore "*.apk-new"
nl
fi
comment "old versions of files"
ignore "*.old"
# Not currently ignored as admins tend to rely on these files.
#ignore "passwd-"
#ignore "group-"
#ignore "shadow-"
#ignore "gshadow-"
nl
comment "mount(8) records system state here, no need to store these"
ignore blkid.tab
ignore blkid.tab.old
nl
comment "some other files in /etc that typically do not need to be tracked"
ignore nologin
ignore ld.so.cache
ignore prelink.cache
ignore mtab
ignore mtab.fuselock
ignore .pwd.lock
ignore "*.LOCK"
ignore network/run
ignore adjtime
ignore lvm/cache
ignore lvm/archive
ignore "X11/xdm/authdir/authfiles/*"
ignore ntp.conf.dhcp
ignore .initctl
ignore "webmin/fsdump/*.status"
ignore "webmin/webmin/oscache"
ignore "apparmor.d/cache/*"
ignore "service/*/supervise/*"
ignore "service/*/log/supervise/*"
ignore "sv/*/supervise/*"
ignore "sv/*/log/supervise/*"
ignore "*.elc"
ignore "*.pyc"
ignore "*.pyo"
ignore "init.d/.depend.*"
ignore "openvpn/openvpn-status.log"
ignore "cups/subscriptions.conf"
ignore "cups/subscriptions.conf.O"
ignore "fake-hwclock.data"
ignore "check_mk/logwatch.state"
nl
comment "editor temp files"
ignore "*~"
ignore ".*.sw?"
ignore ".sw?"
ignore "#*#"
ignore DEADJOE
nl
comment "end section $managed_by_etckeeper"
}
if [ -e "$file" ]; then
if ! grep -q "$managed_by_etckeeper" "$file"; then
if [ "$1" != "-a" ]; then
echo "etckeeper: "$file" does not contain \"$managed_by_etckeeper\" comment; not updating"
exit 1
else
echo "etckeeper: "$file" exists but does not contain \"$managed_by_etckeeper\" comment; updating"
writefile
exit 0
fi
fi
realfile="$file"
if command -v mktemp >/dev/null; then
tempfile="mktemp"
elif command -v tempfile >/dev/null; then
tempfile="tempfile"
else
echo "etckeeper warning: can't find tempfile or mktemp" >&2
fi
file=$($tempfile)
# preserve permissions and ownership
cp -fp "$realfile" "$file"
cat /dev/null >"$file"
(
skipping=
while read -r line; do
if echo "$line" | grep -q "$managed_by_etckeeper"; then
if [ ! "$skipping" ]; then
skipping=1
else
skipping=
writefile
fi
elif [ ! "$skipping" ]; then
echo "$line" >> "$file"
fi
done
if [ "$skipping" ]; then
# reached end of file w/o ending block
writefile
fi
) <"$realfile"
mv -f "$file" "$realfile"
else
writefile
fi

View File

@@ -0,0 +1,2 @@
Executable files in this directory are run to update the VCS ignore file,
or create it if it does not exist.

11
etckeeper/vcs.d/50vcs-cmd Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
set -e
# check whether we can locate the vcs binary
if [ -n "$VCS" ] && command -v "$VCS" > /dev/null; then
# pass commands to the VCS application
$VCS "$@"
else
echo "error: VCS ($VCS) not set or not in PATH" >&2
exit 1
fi