diff --git a/cdist/conf/type/__debconf_set_selections/explorer/state b/cdist/conf/type/__debconf_set_selections/explorer/state
new file mode 100644
index 00000000..f8a3f6c8
--- /dev/null
+++ b/cdist/conf/type/__debconf_set_selections/explorer/state
@@ -0,0 +1,142 @@
+#!/bin/sh -e
+#
+# 2021 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+# Determine current debconf selections' state.
+# Prints one of:
+# present: all selections are already set as they should.
+# different: one or more of the selections have a different value.
+# absent: one or more of the selections are not (currently) defined.
+#
+
+test -x /usr/bin/perl || {
+ # cannot find perl (no perl ~ no debconf)
+ echo 'absent'
+ exit 0
+}
+
+linesfile="${__object:?}/parameter/line"
+test -s "${linesfile}" || {
+ if test -s "${__object:?}/parameter/file"
+ then
+ echo absent
+ else
+ echo present
+ fi
+ exit 0
+}
+
+# assert __type_explorer is set (because it is used by the Perl script)
+: "${__type_explorer:?}"
+
+/usr/bin/perl -- - "${linesfile}" <<'EOF'
+use strict;
+use warnings "all";
+
+use Fcntl qw(:DEFAULT :flock);
+
+use Debconf::Db;
+use Debconf::Question;
+
+# Extract @known... arrays from debconf-set-selections
+# These values are required to distinguish flags and values in the given lines.
+# DC: I couldn't think of a more ugly solution to the problem…
+my @knownflags;
+my @knowntypes;
+my $debconf_set_selections = '/usr/bin/debconf-set-selections';
+if (-e $debconf_set_selections) {
+ my $sed_known = 's/^my \(@known\(flags\|types\) = qw([a-z ]*);\).*$/\1/p';
+ eval `sed -n '$sed_known' '$debconf_set_selections'`;
+}
+
+sub mungeline ($) {
+ my $line = shift;
+ chomp $line;
+ $line =~ s/\r$//;
+ return $line;
+}
+
+sub fatal { printf STDERR @_; exit 1; }
+
+my $state = 'present';
+
+sub state {
+ my $new = shift;
+ if ($state eq 'present'
+ or ($state eq 'different' and $new eq 'absent')) {
+ $state = $new;
+ }
+}
+
+
+# Load Debconf DB but manually lock on the state explorer script,
+# because Debconf aborts immediately if executed concurrently.
+# This is not really an ideal solution because the Debconf DB could be locked by
+# another process (e.g. apt-get), but no way to achieve this could be found.
+# If you know how to, please provide a patch.
+my $lockfile = "%ENV{'__type_explorer'}/state";
+if (open my $lock_fh, '+<', $lockfile) {
+ flock $lock_fh, LOCK_EX or die "Cannot lock $lockfile";
+}
+{
+ Debconf::Db->load(readonly => 'true');
+}
+
+
+while (<>) {
+ # Read and process lines (taken from debconf-set-selections)
+ $_ = mungeline($_);
+ while (/\\$/ && ! eof) {
+ s/\\$//;
+ $_ .= mungeline(<>);
+ }
+ next if /^\s*$/ || /^\s*\#/;
+
+ my ($owner, $label, $type, $content) = /^\s*(\S+)\s+(\S+)\s+(\S+)(?:\s(.*))?/
+ or fatal "invalid line: %s\n", $_;
+ $content = '' unless defined $content;
+
+
+ # Compare is and should state
+ my $q = Debconf::Question->get($label);
+
+ unless (defined $q) {
+ # probably a preseed
+ state 'absent';
+ next;
+ }
+
+ if (grep { $_ eq $q->type } @knownflags) {
+ # This line wants to set a flag, presumably.
+ if ($q->flag($q->type) ne $content) {
+ state 'different';
+ }
+ } else {
+ # Otherwise, it's probably a value…
+ if ($q->value ne $content) {
+ state 'different';
+ }
+
+ unless (grep { $_ eq $owner } (split /, /, $q->owners)) {
+ state 'different';
+ }
+ }
+}
+
+printf "%s\n", $state;
+EOF
diff --git a/cdist/conf/type/__debconf_set_selections/gencode-remote b/cdist/conf/type/__debconf_set_selections/gencode-remote
index e99aef40..9ba28f09 100755
--- a/cdist/conf/type/__debconf_set_selections/gencode-remote
+++ b/cdist/conf/type/__debconf_set_selections/gencode-remote
@@ -1,6 +1,7 @@
#!/bin/sh -e
#
# 2011-2014 Nico Schottelius (nico-cdist at schottelius.org)
+# 2021 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
#
# This file is part of cdist.
#
@@ -17,16 +18,37 @@
# You should have received a copy of the GNU General Public License
# along with cdist. If not, see .
#
-#
-# Setup selections
-#
-filename="$(cat "$__object/parameter/file")"
-
-if [ "$filename" = "-" ]; then
- filename="$__object/stdin"
+if test -f "${__object:?}/parameter/line"
+then
+ filename="${__object:?}/parameter/line"
+elif test -s "${__object:?}/parameter/file"
+then
+ filename=$(cat "${__object:?}/parameter/file")
+ if test "${filename}" = '-'
+ then
+ filename="${__object:?}/stdin"
+ fi
+else
+ printf 'Neither --line nor --file set.\n' >&2
+ exit 1
fi
-echo "debconf-set-selections << __file-eof"
-cat "$filename"
-echo "__file-eof"
+# setting no lines makes no sense
+test -s "${filename}" || exit 0
+
+state_is=$(cat "${__object:?}/explorer/state")
+
+if test "${state_is}" != 'present'
+then
+ cat <<-CODE
+ debconf-set-selections <<'EOF'
+ $(cat "${filename}")
+ EOF
+ CODE
+
+ awk '
+ {
+ printf "set %s %s %s %s\n", $1, $2, $3, $4
+ }' "${filename}" >>"${__messages_out:?}"
+fi
diff --git a/cdist/conf/type/__debconf_set_selections/man.rst b/cdist/conf/type/__debconf_set_selections/man.rst
index 58c25b81..fd0040ae 100644
--- a/cdist/conf/type/__debconf_set_selections/man.rst
+++ b/cdist/conf/type/__debconf_set_selections/man.rst
@@ -8,15 +8,33 @@ cdist-type__debconf_set_selections - Setup debconf selections
DESCRIPTION
-----------
-On Debian and alike systems debconf-set-selections(1) can be used
+On Debian and alike systems :strong:`debconf-set-selections`\ (1) can be used
to setup configuration parameters.
REQUIRED PARAMETERS
-------------------
+cf. ``--line``.
+
+
+OPTIONAL PARAMETERS
+-------------------
file
- Use the given filename as input for debconf-set-selections(1)
- If filename is "-", read from stdin.
+ Use the given filename as input for :strong:`debconf-set-selections`\ (1)
+ If filename is ``-``, read from stdin.
+
+ **This parameter is deprecated, because it doesn't work with state detection.**
+line
+ A line in :strong:`debconf-set-selections`\ (1) compatible format.
+ This parameter can be used multiple times to set multiple options.
+
+ (This parameter is actually required, but marked optional because the
+ deprecated ``--file`` is still accepted.)
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
EXAMPLES
@@ -24,30 +42,29 @@ EXAMPLES
.. code-block:: sh
- # Setup configuration for nslcd
- __debconf_set_selections nslcd --file /path/to/file
+ # Setup gitolite's gituser
+ __debconf_set_selections nslcd --line 'gitolite gitolite/gituser string git'
- # Setup configuration for nslcd from another type
- __debconf_set_selections nslcd --file "$__type/files/preseed/nslcd"
-
- __debconf_set_selections nslcd --file - << eof
- gitolite gitolite/gituser string git
- eof
+ # Setup configuration for nslcd from a file.
+ # NB: Multiple lines can be passed to --line, although this can be considered a hack.
+ __debconf_set_selections nslcd --line "$(cat "${__files:?}/preseed/nslcd.debconf")"
SEE ALSO
--------
-:strong:`debconf-set-selections`\ (1), :strong:`cdist-type__update_alternatives`\ (7)
+- :strong:`cdist-type__update_alternatives`\ (7)
+- :strong:`debconf-set-selections`\ (1)
AUTHORS
-------
-Nico Schottelius
+| Nico Schottelius
+| Dennis Camera
COPYING
-------
-Copyright \(C) 2011-2014 Nico Schottelius. You can redistribute it
-and/or modify it under the terms of the GNU General Public License as
-published by the Free Software Foundation, either version 3 of the
-License, or (at your option) any later version.
+Copyright \(C) 2011-2014 Nico Schottelius, 2021 Dennis Camera.
+You can redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__debconf_set_selections/manifest b/cdist/conf/type/__debconf_set_selections/manifest
new file mode 100755
index 00000000..0f4fb2e2
--- /dev/null
+++ b/cdist/conf/type/__debconf_set_selections/manifest
@@ -0,0 +1,21 @@
+#!/bin/sh -e
+#
+# 2021 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+__package_apt debconf
diff --git a/cdist/conf/type/__debconf_set_selections/nonparallel b/cdist/conf/type/__debconf_set_selections/nonparallel
new file mode 100644
index 00000000..e69de29b
diff --git a/cdist/conf/type/__debconf_set_selections/parameter/required b/cdist/conf/type/__debconf_set_selections/parameter/deprecated
similarity index 100%
rename from cdist/conf/type/__debconf_set_selections/parameter/required
rename to cdist/conf/type/__debconf_set_selections/parameter/deprecated
diff --git a/cdist/conf/type/__debconf_set_selections/parameter/optional_multiple b/cdist/conf/type/__debconf_set_selections/parameter/optional_multiple
new file mode 100644
index 00000000..a999a0c2
--- /dev/null
+++ b/cdist/conf/type/__debconf_set_selections/parameter/optional_multiple
@@ -0,0 +1 @@
+line