state 3.57 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
#!/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 <http://www.gnu.org/licenses/>.
#
# 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
}

44 45 46
# assert __type_explorer is set (because it is used by the Perl script)
: "${__type_explorer:?}"

47 48 49 50
/usr/bin/perl -- - "${linesfile}" <<'EOF'
use strict;
use warnings "all";

51 52
use Fcntl qw(:DEFAULT :flock);

53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
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;
	}
}

86 87 88 89 90 91 92 93 94 95 96 97 98 99

# 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');
}

100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142

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