Hi Marco, On Sun, Mar 19, 2023 at 03:01:20AM +0100, Marco d'Itri wrote: > It is expected that /etc/shells can be edited by system administrators, > I have been doing that forever in my career as a professional system > administrator and until now I was not even aware of these programs from > debianutils.
That applies to configuration files in general, right? However, configuration files have an owner from a packaging point of view. > Hence my reasoning that having convert-etc-shells modify the file would > not be harmful, and so far I am not aware of any practical problem that > this has ever caused. If convert-etc-shells were some administrative tool not to be run by maintainer scripts, that would actually be correct. > I also see that you wrote update-shells in 2021, but convert-etc-shells > was added to usrmerge in 2016. update-shells is an attempt at fixing long-standing bugs in add-shell and remove-shell. Prior to update-shells, those were the canonical tools to modify /etc/shells by packages and except for usrmerge, everyone else used those interfaces. Of course, those interfaces are not up to the task posed by usrmerge, so using them wasn't really an option. However, cooperating with debianutils would have been. > Right. But both update-shells and usr-is-merged are new to bookworm, and > I remember that having the /usr/ paths in /etc/shells is not usually > needed, so this explains why nobody has reported actual problems so far. Yeah, it popped up as a reproducibility issue now. > (Also, would you mind moving /var/lib/shells.state to /var/lib/misc/?) Thank you for suggesting this. I agree that that choice of path is better. When opening that can of worms, I would like to figure out whether there are even better places. update-shells is meant to be run by maintainer scripts only. If an administrator were to run it without changes to shells.d, the expected behaviour is noop. Thus, I am wondering whether something below /usr would be a better choice wrt. hermetic /usr. I think the major question here is what should happen if /etc/shells is deleted. If it should be populated with shells by update-shells, then its state file also needs to be deleted. This would be a reason for the location in /var, which would likekly be discarded together with /etc. If however, we see update-shells purely as a packaging tool, then something below /usr could be better (in a similar vein as we consider moving the dpkg database to /usr). Would you be able to help with finding an answer to this question? Then I wonder what severity that change in location should bear. Is it something we want to do during freeze? Is it worth the effort or more like a time travel fix? In any case, I think this is a separate issue. Would you clone it if you care deeply enough? I also noticed one other flaw in my proposal: Running convert-etc-shells as part of update-shells would cause /usr variants of shells to be re-added after having been removed by administrators. So the convert-etc-shells should be a one time conversion action instead and only happen on the first run of update-shells after a /usr-merge. I think this can be achieved by adding a flag-value to shells.state. I've prepared an update for debianutils and tested it in the following cases: * Installation on a pre-merged chroot -> /usr/bin/sh is added to /etc/shells. * Installation on a chroot merged by usrmerge -> no difference * Installation on an unmerged system. Manual merge without convert-etc-shells. Manual update-shells. -> Looks the same as after convert-etc-shells. Does anyone see any bugs? Helmut
diff --minimal -Nru debianutils-5.7/debian/changelog debianutils-5.7/debian/changelog --- debianutils-5.7/debian/changelog 2022-11-02 17:31:14.000000000 +0100 +++ debianutils-5.7/debian/changelog 2023-03-19 15:00:09.000000000 +0100 @@ -1,3 +1,10 @@ +debianutils (5.7-0.5) UNRELEASED; urgency=medium + + * Non-maintainer upload. + * Absorb usrmerge's convert-etc-shells into update-shells. + + -- Helmut Grohne <hel...@subdivi.de> Sun, 19 Mar 2023 15:00:09 +0100 + debianutils (5.7-0.4) unstable; urgency=medium * Non-maintainer upload diff --minimal -Nru debianutils-5.7/debian/patches/absorb-convert-etc-shells debianutils-5.7/debian/patches/absorb-convert-etc-shells --- debianutils-5.7/debian/patches/absorb-convert-etc-shells 1970-01-01 01:00:00.000000000 +0100 +++ debianutils-5.7/debian/patches/absorb-convert-etc-shells 2023-03-19 15:00:09.000000000 +0100 @@ -0,0 +1,112 @@ +Absorb the script convert-etc-shells from usrmerge to obtain reproducible +behaviour. usrmerge will stop running convert-etc-shells and instead trigger +the shells update in debianutils. + +--- a/update-shells ++++ b/update-shells +@@ -1,11 +1,15 @@ + #!/bin/sh + # SPDX-License-Identifier: GPL-2.0-or-later + # Copyright 2021 Helmut Grohne <hel...@subdivi.de> +- ++# + # A "hashset" is a shell variable containing a sequence of elements separated + # and surrounded by hash (#) characters. None of the elements may contain a + # hash character. The character is thus chosen, because it initiates a comment + # in /etc/shells. All variables ending in _SHELLS in this file are hashsets. ++# ++# The state file contains the concatenated shells from shells.d. It optionally ++# contains a first line "usr-is-merged" if /usr variants of user shells have ++# been added. + + set -e + +@@ -93,6 +97,13 @@ + done < "$STATE_FILE" + fi + ++PERFORM_USR_MERGE=no ++if ! hashset_contains "$STATE_SHELLS" usr-is-merged && ++ test -h /bin && test -h /sbin; then ++ PERFORM_USR_MERGE=yes ++ STATE_SHELLS="#usr-is-merged$STATE_SHELLS" ++fi ++ + cleanup() { + rm -f "$NEW_ETC_FILE" "$NEW_STATE_FILE" + } +@@ -100,20 +111,52 @@ + + : > "$NEW_ETC_FILE" + ETC_SHELLS='#' ++USRMERGE_SHELLS='#' + while IFS= read -r line; do + shell=${line%%#*} +- # copy all comment lines, packaged shells and local additions +- if [ -z "$shell" ] || +- hashset_contains "$PKG_SHELLS" "$shell" || +- ! hashset_contains "$STATE_SHELLS" "$shell"; then ++ if [ -z "$shell" ]; then ++ # copy comments ++ echo "$line" >> "$NEW_ETC_FILE" ++ elif hashset_contains "$PKG_SHELLS" "$shell"; then ++ # copy packaged shells + echo "$line" >> "$NEW_ETC_FILE" + ETC_SHELLS="$ETC_SHELLS$shell#" ++ elif ! hashset_contains "$STATE_SHELLS" "$shell"; then ++ # copy local additions ++ echo "$line" >> "$NEW_ETC_FILE" ++ ETC_SHELLS="$ETC_SHELLS$shell#" ++ # If we perform a /usr-merge upgrade, add the corresponding ++ # /usr path. This is what convert-etc-shells from usrmerge ++ # formerly did. ++ if test "$PERFORM_USR_MERGE" = yes && ++ test "${shell#/bin/}" != "${shell#/sbin/}"; then ++ USRMERGE_SHELLS="$USRMERGE_SHELLS/usr$shell#" ++ fi + else + log "removing shell $shell" + fi + done < "$SOURCE_ETC_FILE" + ++if test "$PERFORM_USR_MERGE" = yes; then ++ saved_IFS=$IFS ++ IFS='#' ++ set -f ++ # shellcheck disable=SC2086 # word splitting intended, globbing disabled ++ set -- ${USRMERGE_SHELLS###} ++ set +f ++ IFS=$saved_IFS ++ for shell; do ++ if ! hashset_contains "$ETC_SHELLS" "$shell"; then ++ echo "$shell" >> "$NEW_ETC_FILE" ++ log "adding /usr-merged shell $shell" ++ fi ++ done ++fi ++ + : > "$NEW_STATE_FILE" ++if hashset_contains "$STATE_SHELLS" usr-is-merged; then ++ echo "usr-is-merged" >> "$NEW_STATE_FILE" ++fi + saved_IFS=$IFS + IFS='#' + set -f +@@ -133,13 +176,13 @@ + + if [ "$NOACT" = 0 ]; then + if [ -e "$STATE_FILE" ]; then +- chmod --reference="${STATE_FILE}" "${NEW_STATE_FILE}" || chmod $(stat -c %a "${STATE_FILE}") "${NEW_STATE_FILE}" +- chown --reference="${STATE_FILE}" "${NEW_STATE_FILE}" || chown $(stat -c %U "${STATE_FILE}") "${NEW_STATE_FILE}" ++ chmod --reference="${STATE_FILE}" "${NEW_STATE_FILE}" || chmod "$(stat -c %a "${STATE_FILE}")" "${NEW_STATE_FILE}" ++ chown --reference="${STATE_FILE}" "${NEW_STATE_FILE}" || chown "$(stat -c %U "${STATE_FILE}")" "${NEW_STATE_FILE}" + else + chmod 0644 "$NEW_STATE_FILE" + fi +- chmod --reference="${SOURCE_ETC_FILE}" "${NEW_ETC_FILE}" || chmod $(stat -c %a "${SOURCE_ETC_FILE}") "${NEW_ETC_FILE}" +- chown --reference="${SOURCE_ETC_FILE}" "${NEW_ETC_FILE}" || chown $(stat -c %U "${SOURCE_ETC_FILE}") "${NEW_ETC_FILE}" ++ chmod --reference="${SOURCE_ETC_FILE}" "${NEW_ETC_FILE}" || chmod "$(stat -c %a "${SOURCE_ETC_FILE}")" "${NEW_ETC_FILE}" ++ chown --reference="${SOURCE_ETC_FILE}" "${NEW_ETC_FILE}" || chown "$(stat -c %U "${SOURCE_ETC_FILE}")" "${NEW_ETC_FILE}" + sync -d "$NEW_ETC_FILE" "$NEW_STATE_FILE" + mv -Z "${NEW_ETC_FILE}" "${TARGET_ETC_FILE}" || mv "${NEW_ETC_FILE}" "${TARGET_ETC_FILE}" + sync "$TARGET_ETC_FILE" diff --minimal -Nru debianutils-5.7/debian/patches/series debianutils-5.7/debian/patches/series --- debianutils-5.7/debian/patches/series 2022-07-27 08:17:42.000000000 +0200 +++ debianutils-5.7/debian/patches/series 2023-03-19 15:00:09.000000000 +0100 @@ -1 +1,2 @@ dpkg-root +absorb-convert-etc-shells