diff --git a/cdist/conf/type/__package_apt/explorer/state b/cdist/conf/type/__package_apt/explorer/state index 7ccd6fce..ae2f0618 100755 --- a/cdist/conf/type/__package_apt/explorer/state +++ b/cdist/conf/type/__package_apt/explorer/state @@ -1,6 +1,7 @@ #!/bin/sh # # 2011-2012 Nico Schottelius (nico-cdist at schottelius.org) +# 2021 Dennis Camera (cdist at dtnr.ch) # # This file is part of cdist. # @@ -21,19 +22,48 @@ # Retrieve the status of a package - parsed dpkg output # -if [ -f "$__object/parameter/name" ]; then - name="$(cat "$__object/parameter/name")" +breify() { + # Convert arguments to a POSIX BRE-compatible form, i.e. escape special + # characters (incl. delimiter) + printf '%s' "$*" | sed -e 's/[].^$*\[]/\\&/g' -e 's:/:\\/:g' +} + + +if test -f "${__object}/parameter/name" +then + name=$(cat "${__object:?}/parameter/name") else - name="$__object_id" + name=${__object_id:?} fi -# Except dpkg failing, if package is not known / installed -packages="$(apt-cache showpkg "$name" | sed -e "1,/Reverse Provides:/d" | cut -d ' ' -f 1) $name" -for p in $packages; do - if dpkg -s "$p" 2>/dev/null | grep --quiet "^Status: install ok installed$" ; then - version=$(dpkg -s "$p" 2>/dev/null | grep "^Version:" | cut -d ' ' -f 2) - echo "present $p $version" - exit 0 - fi +state_dir=$(apt-config dump | sed -n -e 's/^Dir::State *"\(.*\)";$/\/\1/p') +extended_states_file=${state_dir%/}/extended_states + +# if $name is e.g. editor, check if any editor is installed instead +rprovides=$(apt-cache showpkg "${name}" | sed -e '1,/^Reverse Provides:/d' -e 's/ .*$//') + +for pkg in ${name} ${rprovides} +do + if dpkg -s "${pkg}" 2>/dev/null | grep -qxF 'Status: install ok installed' + then + version=$(dpkg -s "${pkg}" 2>/dev/null | sed -n -e 's/^Version: *//p') + + if test -f "${extended_states_file}" + then + # On Debian >= 5 check if the package is auto installed + + # NOTE: instead of using apt-mark(8) parse the extended_states file + # directly. apt-mark is sloow and didn't have a stable + # interface in older Debian versions. + is_auto=$(sed -n -e '/^Package: '$(breify "${name}")'$/,/^$/!d' -e 's/^Auto-Installed: *//p' "${extended_states_file}") + fi + + # NOTE: older versions don't have apt-mark -> all packages are manual + auto_word=$(test $((is_auto)) -ne 0 && echo auto || echo manual) + + echo "present ${auto_word} ${pkg} ${version}" + exit 0 + fi done + echo absent diff --git a/cdist/conf/type/__package_apt/gencode-remote b/cdist/conf/type/__package_apt/gencode-remote index 79c0d9d3..aceb37de 100755 --- a/cdist/conf/type/__package_apt/gencode-remote +++ b/cdist/conf/type/__package_apt/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2011-2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2021 Dennis Camera (cdist at dtnr.ch) # # This file is part of cdist. # @@ -56,31 +57,36 @@ else fi -# FIXME: use grep directly, state is a list, not a line! -state_is="$(cat "$__object/explorer/state")" -case "$state_is" in - present*) - name="$(echo "$state_is" | cut -d ' ' -f 2)" - version_is="$(echo "$state_is" | cut -d ' ' -f 3)" - state_is="present" - ;; - *) - version_is="" - ;; -esac - -if [ "$state_is" = "$state_should" ]; then - if [ -z "$version" ] || [ "$version" = "$version_is" ]; then - exit 0; - fi -fi +read -r state_is auto_state name version_is <"${__object:?}/explorer/state" # Hint if we need to avoid questions at some point: # DEBIAN_PRIORITY=critical can reduce the number of questions aptget="DEBIAN_FRONTEND=noninteractive apt-get --quiet --yes -o Dpkg::Options::=\"--force-confdef\" -o Dpkg::Options::=\"--force-confold\"" -case "$state_should" in +case ${state_should} +in present) + if test "${state_is}" = 'present' + then + if test -z "${version}" || test "${version}" = "${version_is}" + then + if test "${auto_state}" = 'auto' + then + # package is installed, but marked auto -> mark it manual so + # that it doesn't get removed by apt-get autoremove. + + echo 'head -n1 "$(command -v apt-mark)" | grep -qvF python \' + # new implementation (Debian >= 7) + echo "&& apt-mark manual '${name}' \\" + # old Python implementation (Debian 5-7) + echo "|| apt-mark unmarkauto '${name}'" + # NOTE: older versions don't need to be supported because + # they will always be manual + fi + exit 0 + fi + fi + # There are special arguments to apt(8) to prevent aborts if apt woudn't been # updated after the 19th April 2021 till the bullseye release. The additional # arguments acknoledge the happend suite change (the apt(8) update does the @@ -104,12 +110,15 @@ EOF if [ -n "$version" ]; then name="${name}=${version}" fi - echo "$aptget $recommendsparam install $target_release '$name'" - echo "installed" >> "$__messages_out" + + echo "${aptget} ${recommendsparam} install ${target_release} '${name}'" + echo 'installed' >>"${__messages_out:?}" ;; absent) - echo "$aptget remove $purgeparam '$name'" - echo "removed" >> "$__messages_out" + test "${state_is}" != 'absent' || exit 0 + + echo "${aptget} remove ${purgeparam} '${name}'" + echo 'removed' >>"${__messages_out:?}" ;; *) echo "Unknown state: $state_should" >&2 diff --git a/docs/changelog b/docs/changelog index 99a8c08b..4008e3c2 100644 --- a/docs/changelog +++ b/docs/changelog @@ -11,6 +11,7 @@ next: * Type __package_apt: Fix complaint about suite change (Matthias Stecher) * Type __debconf_set_selections: Fix bug where --file was unsupported (Evilham) * Types __letsencrypt_cert, __grafana_dashboard: Improve bullseye support (Evilham) + * Type __package_apt: Mark already installed packages as manual (Dennis Camera) 6.9.8: 2021-08-24 * Type __rsync: Rewrite (Ander Punnar)