From 18c9c9023114c4344a62dd9f96b09b9028f63b74 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Sun, 20 Nov 2016 18:30:20 -0800 Subject: [PATCH] install.sh: make updates less disruptive Although there is no way to atomically replace one directory with another in a POSIX-ish way that works on all platforms, we can quickly rename the previous one out of the way and then rename the new one into place. Instead of the previous hodgepodge updating in place, we now install into basedir-new, webroot-new and cgibin-new and then rename the old out of the way and the new into place. Unfortunately the two renames are not atomic, but they are very fast and compared to the previous technique should substantially reduce the window of opportunity for something to be in a non-working order during an update. To further reduce the liklihood of something trying to operate during the brief window of the update, a utility could be created that makes the library rename function calls consequtively and almost instantly after one another, but that would only further reduce the window not eliminate it and so that's left for a future potential update. Signed-off-by: Kyle J. McKay --- install.sh | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index 545a186..736241a 100755 --- a/install.sh +++ b/install.sh @@ -287,6 +287,23 @@ chown_make() { fi } +# Make sure $cfg_cgiroot, $cfg_webroot and $cfg_cgiroot are absolute paths +case "$cfg_basedir" in /*) :;; *) + echo "ERROR: invalid Girocco::Config::basedir setting" >&2 + echo "ERROR: \"$cfg_basedir\" must be an absolute path (start with '/')" >&2 + exit 1 +esac +case "$cfg_webroot" in /*) :;; *) + echo "ERROR: invalid Girocco::Config::webroot setting" >&2 + echo "ERROR: \"$cfg_webroot\" must be an absolute path (start with '/')" >&2 + exit 1 +esac +case "$cfg_cgiroot" in /*) :;; *) + echo "ERROR: invalid Girocco::Config::cgiroot setting" >&2 + echo "ERROR: \"$cfg_cgiroot\" must be an absolute path (start with '/')" >&2 + exit 1 +esac + # Use basedir, webroot and cgiroot for easier control of filesystem locations # Wherever we are writing/copying/installing files we use these, but where we # are editing, adding config settings or printing advice we always stick to the @@ -294,9 +311,9 @@ chown_make() { # Only the file system directories that could be asynchronously accessed (by # the web server, jobd.pl, taskd.pl or incoming pushes) get these special vars. # The chroot is handled specially and does not need one of these. -basedir="$cfg_basedir" -webroot="$cfg_webroot" -cgiroot="$cfg_cgiroot" +basedir="$cfg_basedir-new" +webroot="$cfg_webroot-new" +cgiroot="$cfg_cgiroot-new" echo "*** Setting up basedir..." "$MAKE" --no-print-directory --silent apache.conf @@ -414,6 +431,10 @@ mkdir -p "$basedir"/bin mv -f "$basedir"/bin/Markdown.pl.$$ "$basedir"/bin/Markdown.pl) test $? -eq 0 +# Some permission sanity on basedir/bin just in case +find "$basedir"/bin -type f -print0 | xargs -0 chmod go-w +chown -R -h "$cfg_mirror_user""$owngroup" "$basedir"/bin + if [ -n "$cfg_mirror" ]; then echo "--- Remember to start $cfg_basedir/taskd/taskd.pl" fi @@ -435,6 +456,15 @@ chmod 02775 "$cfg_reporoot/_recyclebin" || echo "WARNING: Cannot chmod $cfg_repo if [ -n "$cfg_chrooted" ]; then echo "*** Setting up chroot jail for pushing..." if [ "$(id -u)" -eq 0 ]; then + # jailsetup may install things from $cfg_basedir/bin into the + # chroot so we do a mini-update of just that portion now + mkdir -p "$cfg_basedir" + rm -rf "$cfg_basedir/bin-new" + cp -pR "$basedir/bin" "$cfg_basedir/bin-new" >/dev/null 2>&1 + rm -rf "$cfg_basedir/bin-old" + ! [ -d "$cfg_basedir/bin" ] || mv -f "$cfg_basedir/bin" "$cfg_basedir/bin-old" + mv -f "$cfg_basedir/bin-new" "$cfg_basedir/bin" + rm -rf "$cfg_basedir/bin-old" ./jailsetup.sh else echo "WARNING: Skipping jail setup, not root" @@ -454,12 +484,21 @@ chmod g+w "$cfg_chroot/etc/passwd" "$cfg_chroot/etc/group" || echo "WARNING: Cannot chmod g+w the etc/passwd and/or etc/group files" chmod 02775 "$cfg_chroot/etc" || echo "WARNING: Cannot chmod 02775 $cfg_chroot/etc" + echo "*** Setting up gitweb from git.git..." if [ ! -f git.git/Makefile ]; then echo "ERROR: git.git is not checked out! Did you _REALLY_ read INSTALL?" >&2 exit 1 fi + +# We do not wholesale replace either webroot or cgiroot so if they exist we must +# make a copy to start working on them. We make a copy using -p which can result +# in some warnings so we suppress error output as it's of no consequence in this case. +rm -rf "$webroot" "$cgiroot" +! [ -d "$cfg_webroot" ] || cp -pR "$cfg_webroot" "$webroot" >/dev/null 2>&1 +! [ -d "$cfg_cgiroot" ] || cp -pR "$cfg_cgiroot" "$cgiroot" >/dev/null 2>&1 mkdir -p "$webroot" "$cgiroot" + (cd git.git && "$MAKE" --no-print-directory --silent NO_SUBDIR=: bindir="$(dirname "$cfg_git_bin")" \ GITWEB_CONFIG="$cfg_basedir/gitweb/gitweb_config.perl" SHELL_PATH="$shbin" gitweb && \ chown_make gitweb && \ @@ -512,6 +551,7 @@ cp "$basedir"/bin/snapshot.cgi "$basedir/cgi" cp "$basedir"/bin/authrequired.cgi "$basedir/cgi" [ -n "$cfg_httpspushurl" ] || rm -f "$basedir/cgi"/usercert.cgi "$cgiroot"/usercert.cgi cp "$basedir/cgi"/*.cgi "$cgiroot" +rm -rf "$basedir/cgi" ln -fs "$cfg_basedir"/Girocco "$cgiroot" [ -z "$cfg_webreporoot" ] || { rm -f "$cfg_webreporoot" && ln -s "$cfg_reporoot" "$cfg_webreporoot"; } if [ -z "$cfg_httpspushurl" ]; then @@ -646,6 +686,16 @@ else fi -echo "*** Finalizing permissions..." +echo "*** Finalizing permissions and moving into place..." chown -R -h "$cfg_mirror_user""$owngroup" "$basedir" "$webroot" "$cgiroot" [ -z "$cfg_httpspushurl" ] || chown -R -h "$cfg_mirror_user""$owngroup" "$cfg_certsdir" + +# This should always be the very last thing install.sh does +rm -rf "$cfg_basedir-old" "$cfg_webroot-old" "$cfg_cgiroot-old" +! [ -d "$cfg_basedir" ] || mv -f "$cfg_basedir" "$cfg_basedir-old" +mv -f "$basedir" "$cfg_basedir" +! [ -d "$cfg_webroot" ] || mv -f "$cfg_webroot" "$cfg_webroot-old" +mv -f "$webroot" "$cfg_webroot" +! [ -d "$cfg_cgiroot" ] || mv -f "$cfg_cgiroot" "$cfg_cgiroot-old" +mv -f "$cgiroot" "$cfg_cgiroot" +rm -rf "$cfg_basedir-old" "$cfg_webroot-old" "$cfg_cgiroot-old" -- 2.11.4.GIT