summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-12-30 17:06:08 -0700
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-12-30 17:06:08 -0700
commit9539d82e07a22550b9a6d9ecbef47be6250e5d90 (patch)
treea7b1dcb9047550c79f27da281d194477842db7cf
parent2b57a4cf95d2a4ea4acdba010ad71b41f67135f9 (diff)
Refactor to be friendlier with OpenRC; expand README.
-rwxr-xr-x50pristine-etc-keeper.post-install8
-rw-r--r--Makefile33
-rw-r--r--README.md104
-rwxr-xr-xdrain (renamed from pristine-etc-keeper)40
-rwxr-xr-xfill43
-rw-r--r--pristine-etc-keeper.service4
-rw-r--r--pristine-etc-keeper.timer18
7 files changed, 166 insertions, 84 deletions
diff --git a/50pristine-etc-keeper.post-install b/50pristine-etc-keeper.post-install
index 096cce4..757beba 100755
--- a/50pristine-etc-keeper.post-install
+++ b/50pristine-etc-keeper.post-install
@@ -4,10 +4,4 @@
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the COPYING file for more details.
-exec 8>/var/lib/pristine-etc/lock
-flock -x -n 8
-echo 'etckeeper post-install' >> /var/lib/pristine-etc/spool
-exec 8>&-
-
-systemctl reset-failed pristine-etc-keeper.service &>/dev/null || true
-systemctl start pristine-etc-keeper.service
+/etc/etckeeper/pristine/fill 'etckeeper post-install'
diff --git a/Makefile b/Makefile
index d92ccda..ef6a552 100644
--- a/Makefile
+++ b/Makefile
@@ -7,23 +7,38 @@ topsrcdir = .
topoutdir = .
include $(topsrcdir)/build-aux/Makefile.head.mk
-std.sys_files += /etc/etckeeper/pristine-etc-keeper
+# The core of pristine-etc-keeper
+std.sys_files += /etc/etckeeper/pristine/drain
+std.sys_files += /etc/etckeeper/pristine/fill
std.sys_files += /etc/etckeeper/post-install.d/50pristine-etc-keeper
-std.sys_files += /usr/lib/systemd/system/pristine-etc-keeper.service
-std.sys_files += /usr/lib/systemd/system/pristine-etc-keeper.timer
-std.sys_files += /var/lib/pristine-etc/lock
-std.sys_files += /usr/share/doc/pristine-etc-keeper/README.md
+std.sys_files += /var/lib/pristine-etc/etc.lock
+std.sys_files += /var/lib/pristine-etc/spool.lock
-$(DESTDIR)/etc/etckeeper/pristine-etc-keeper: $(srcdir)/pristine-etc-keeper
+$(DESTDIR)/etc/etckeeper/pristine/%: $(srcdir)/%
install -Dm755 $< $@
$(DESTDIR)/etc/etckeeper/post-install.d/%: $(srcdir)/%.post-install
install -Dm755 $< $@
+$(DESTDIR)/var/lib/pristine-etc/%.lock:
+ mkdir -p $(@D)
+ touch $@
+
+# Convenience symlinks in bindir
+std.sys_files += /usr/bin/pristine-etc-keeper
+
+$(DESTDIR)/usr/bin/pristine-etc-keeper: $(DESTDIR)/etc/etckeeper/pristine/fill
+ mkdir -p $(@D)
+ ln -srfT $< $@
+
+# systemd integration
+std.sys_files += /usr/lib/systemd/system/pristine-etc-keeper.service
+
$(DESTDIR)/usr/lib/systemd/system/%: $(srcdir)/%
install -Dm644 $< $@
+
+# Documentation
+std.sys_files += /usr/share/doc/pristine-etc-keeper/README.md
+
$(DESTDIR)/usr/share/doc/pristine-etc-keeper/%: %
install -Dm644 $< $@
-$(DESTDIR)/var/lib/pristine-etc/lock:
- mkdir -p $(@D)
- touch $@
include $(topsrcdir)/build-aux/Makefile.tail.mk
diff --git a/README.md b/README.md
index d355615..df4ae55 100644
--- a/README.md
+++ b/README.md
@@ -5,17 +5,48 @@ The main functionality
maintain a git branch that is a "pristine" version of `/etc`--just the
raw files provided by installed packages.
-It assumes that the real directory being tracked by etckeeper is
-`/etc`, that the VCS using used is `git`, and that the system package
-manager is `pacman`.
+It makes several assumptions that make it a bit less general than
+etckeeper:
+ - the real directory being tracked by etckeeper is `/etc`
+ - the VCS using used is `git`
+ - the system package manager is `pacman`
It works by asking `pacman` which packages are installed, and
extracting `/etc` files from the cached copies of the package
tarballs. A litteral pristine version of `/etc` lives at
`/var/lib/pristine-etc/etc`.
-The spool (AKA, invoking pristine-etc-keeper)
-=============================================
+Invocation
+==========
+
+The normal operation of pristine-etc-keeper is for it to be called by
+etckeeper's post-install hook. You usually don't need to worry about
+it.
+
+However, sometimes it may be useful to call it from somewhere else.
+
+The `pristine-etc-keeper` program essentially has two modes of
+operation:
+
+ 1. With arguments: Run, and use "$*" for the commit message.
+
+ 2. No arguments: If a run was previously requested, but never
+ fulfilled; do that now.
+
+Really though, if it has arguments, it requests a run with that commit
+message; then, whether it requested a run or not, it launches another
+process to fulfill any outstanding requests.
+
+If you're thinking "What!? Why request runs? Why not just run
+directly? It's needless complexity!" Instinctively, I'd agree with
+that line of reasoning; but see "The spool" below for justification of
+the complexity.
+
+Implementation details
+======================
+
+The spool
+---------
It would be really simple to just provide a program that you run when
you want to update the pristine-etc. Unfortunately,
@@ -26,29 +57,46 @@ But that opens a whole other slew of problems. What happens if we try
to run it again while it is already running? The second instance
should wait until the first instance is finished. But only one
instance should be queued at a time; if a 3rd instance tries to start,
-it should just be discarded/merged with the one already waiting.
+it should just be discarded/merged with the one already waiting
+("coalescing").
A simple thing to do would have been to write a daemon that is always
running, waiting to receive a signal that it should run. I don't like
-that because I don't really want the process to be long lived.
-
-So, I created a sort of spool. You can think of the spool as a queue
-of jobs to run, and that when you run `pristine-etc-keeper.service`,
-it runs repeatedly until the spool is empty. What makes this
-different than the job queue that I just described it as is that it
-drains the entire spool each run; if it has to run again, it's because
-the spool filled back up while it was running.
-
-Anyway, to put a job in the spool, you do 4 things:
-
- 1. `open(2)` the file `/var/lib/pristine-etc/lock`
- 1. `flock(2)` the lockfile file descriptor with an exclusive lock
- 2. write to /var/lib/pristine-etc/spool with your message and
- `O_CREAT` | `O_APPEND`.
- 3. close the `flock`ed file descriptor
- 4. run `systemctl start pristine-etc-keeper.service` to start (if
- nescessary) it draining the spool.
-
-You may wish to run
-`systemctl reset-failed pristine-etc-keeper.service` before trying to
-start it.
+that because I don't really want the process to be long lived, nor for
+it to be started at boot.
+
+But, let's take that idea, tweak it a bit.
+
+Let's conceptualize the "signal" as the invoker adding an item to a
+filesystem spool; each time the daemon runs a job, it empties the
+spool.
+
+Now, we modify this idea by saying that the daemon may exit when the
+spool is empty (ie, it has no work to do, and would just idle-wait
+until it is asked to run again). When the invoker adds an item to the
+spool, we simply have it ensure that the daemon is running.
+
+To translate this into the source code names, the invoker is "fill"
+because it adds an item to the spool, and the so-called-daemon that
+runs the jobs is "drain" because it drains the spool.
+
+Systemd integration
+-------------------
+
+In the above section, we conceptualized the "drain" program as a
+daemon, even though it isn't truly one. This conceptualization is
+useful in administration as well.
+
+So, if systemd is being used, we have it litterally show up as
+pristine-etc-keeper.service. This has a number of benefits:
+
+ - cgroup isolation
+ - uniform logging with other system services
+ - most importantly: systemd v230 and up won't kill a run in progress
+ when you log out
+
+But, if systemd wasn't used to boot the system, then it simply forks
+to the background.
+
+If you'd like to see similar integration with another service manager,
+patches welcome!
diff --git a/pristine-etc-keeper b/drain
index 394af55..c739b12 100755
--- a/pristine-etc-keeper
+++ b/drain
@@ -4,9 +4,9 @@
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the COPYING file for more details.
-# This script gets called by the etckeeper post-install hook.
+# The user should not call this script directly.
-# TODO: better error handling
+declare -r workdir=/var/lib/pristine-etc
pacman-etc-name-ver() {
LC_ALL=C pacman -Qo /etc | sed 's|^/etc/ is owned by ||'
@@ -19,7 +19,7 @@ pacman-all-name-arch() {
commit() (
local msg="$1"
- cd /var/lib/pristine-etc
+ cd "$workdir"
if ! [[ -d etc.git ]]; then
mkdir -p etc
(cd etc && etckeeper init -d "$PWD")
@@ -59,40 +59,40 @@ commit() (
pull() (
cd /etc
- git remote add pristine /var/lib/pristine-etc/etc &>/dev/null || true
+ git remote add pristine "${workdir}/etc" &>/dev/null || true
git fetch pristine
)
lock() {
- exec 8>/var/lib/pristine-etc/lock
- flock -x -n 8
+ local fd=$1
+ local file=$2
+ eval "exec $fd>"'"$file"'
+ flock "${@:3}" "$fd"
}
unlock() {
- exec 8>&-
+ local fd=$1
+ exec {fd}>&-
}
main() {
- set -e
+ set -e -o pipefail
umask 0022
- if [[ $# -gt 0 ]]; then
- lock
- printf '%s\n' "$*" >> /var/lib/pristine-etc/spool
- unlock
+ if ! lock 7 "${workdir}/etc.lock" -n; then
+ return 0
fi
-
while true; do
- lock
- if ! [[ -f /var/lib/pristine-etc/spool ]]; then
+ lock 8 "${workdir}/spool.lock"
+ if ! [[ -f "${workdir}/spool" ]]; then
return 0
fi
- msg="$(cat /var/lib/pristine-etc/spool)"
- rm -f /var/lib/pristine-etc/spool
- unlock
+ msg="$(cat "${workdir}/spool")"
+ rm -f "${workdir}/spool"
+ unlock 8
- commit "$msg" || return $?
- pull || return $?
+ commit "$msg"
+ pull
done
}
diff --git a/fill b/fill
new file mode 100755
index 0000000..f12ae2a
--- /dev/null
+++ b/fill
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# Copyright 2016 Luke Shumaker
+# This work is free. You can redistribute it and/or modify it under the
+# terms of the Do What The Fuck You Want To Public License, Version 2,
+# as published by Sam Hocevar. See the COPYING file for more details.
+
+declare -r workdir=/var/lib/pristine-etc
+
+lock() {
+ local fd=$1
+ local file=$2
+ eval "exec $fd>"'"$file"'
+ flock "${@:3}" "$fd"
+}
+
+unlock() {
+ local fd=$1
+ exec {fd}>&-
+}
+
+drain() {
+ if [[ -d /run/systemd/system ]]; then
+ systemctl reset-failed pristine-etc-keeper.service &>/dev/null || true
+ systemctl start pristine-etc-keeper.service
+ else
+ /etc/etckeeper/pristine/drain &
+ fi
+}
+
+main() {
+ set -e
+ umask 0022
+
+ if [[ $# -gt 0 ]]; then
+ lock 8 "${workdir}/spool.spool"
+ printf '%s\n' "$*" >> "${workdir}/spool"
+ unlock 8
+ fi
+
+ drain
+}
+
+main "$@"
diff --git a/pristine-etc-keeper.service b/pristine-etc-keeper.service
index 5024265..4cf4305 100644
--- a/pristine-etc-keeper.service
+++ b/pristine-etc-keeper.service
@@ -3,7 +3,7 @@
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See the COPYING file for more details.
[Unit]
-Description=Autocommit of changes in pristine /etc directory
+Description=Update pristine /etc directory
Documentation=man:pristine-etc-keeper(8)
DefaultDependencies=no
Conflicts=shutdown.target
@@ -13,5 +13,5 @@ Before=shutdown.target
[Service]
# Can't use Type=oneshot because that would block when starting
Type=simple
-ExecStart=/etc/etckeeper/pristine-etc-keeper
+ExecStart=/etc/etckeeper/pristine/drain
IOSchedulingClass=idle
diff --git a/pristine-etc-keeper.timer b/pristine-etc-keeper.timer
deleted file mode 100644
index 463d582..0000000
--- a/pristine-etc-keeper.timer
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright 2016 Luke Shumaker
-# This work is free. You can redistribute it and/or modify it under the
-# terms of the Do What The Fuck You Want To Public License, Version 2,
-# as published by Sam Hocevar. See the COPYING file for more details.
-
-# This unit shouldn't be nescessary; the etckeeper post-install hook
-# should take care of everthing, but have this just to be safe.
-
-[Unit]
-Description=Weekly autocommit of changes in pristine /etc directory
-Documentation=man:pristine-etc-keeper(8)
-
-[Timer]
-OnBootSec=1d
-OnUnitActiveSec=1w
-
-[Install]
-WantedBy=multi-user.target