Subject: [PATCH] Check for missing dependency on $remote_fs or $local_fs in init scripts

From: Raphael Geissert <[email-masked]>
Date: Wed, 16 Sep 2009 17:39:48 -0500

Init scripts needing files or binaries from /usr need to depend on
$remote_fs and those using files or needing read/write access to /var
need to depend on $local_fs.
---
 checks/init.d                                      |   18 +++++++++++
 checks/init.d.desc                                 |   32 ++++++++++++++++++++
 .../init.d-lsb-headers/debian/debian/control.in    |   16 ++++++++++
 .../debian/debian/init.d-lsb-headers-local.init    |   20 ++++++++++++
 .../debian/debian/init.d-lsb-headers-remote.init   |   21 +++++++++++++
 t/tests/init.d-lsb-headers/desc                    |    2 +
 t/tests/init.d-lsb-headers/tags                    |    2 +
 7 files changed, 111 insertions(+), 0 deletions(-)
 create mode 100644 t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-local.init
 create mode 100644 t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-remote.init

diff --git a/checks/init.d b/checks/init.d
index 1b1d649..68464e4 100644
--- a/checks/init.d
+++ b/checks/init.d
@@ -195,6 +195,7 @@ sub check_init {
 	or fail("cannot open init.d file $initd_file: $!");
     my (%tag, %lsb);
     my $in_file_test = 0;
+    my %needs_fs = ('remote' => 0, 'local' => 0);
     while (defined(my $l = <IN>)) {
 	if ($. eq 1 && $l =~ m,^\#!\s*(/usr/[^\s]+),) {
 	    tag "init.d-script-uses-usr-interpreter", "/etc/init.d/$_ $1";
@@ -243,6 +244,9 @@ sub check_init {
 	    tag "init.d-script-sourcing-without-test", "/etc/init.d/$_:$. $1";
 	}
 
+	$needs_fs{'remote'} = 1 if ($l =~ m,^[^#]*/usr,);
+	$needs_fs{'local'}  = 1 if ($l =~ m,^[^#]*/var,);
+
 	while ($l =~ s/^[^\#]*?(start|stop|restart|force-reload)//o) {
 	    $tag{$1} = 1;
 	}
@@ -324,9 +328,23 @@ sub check_init {
 	    unless $provides_self;
     }
 
+    # if $remote_fs is needed $local_fs is not, as it is implied
+    $needs_fs{'local'} = 0 if $needs_fs{'remote'};
+
     for my $keyword qw(required-start should-start required-stop should-stop) {
+	if ($keyword =~ m/^required-(start|stop)$/
+	    && defined $lsb{"default-$1"} && length($lsb{"default-$1"})) {
+
+	    tag "missing-dependency-on-remote_fs", "/etc/init.d/$_: $keyword"
+		if ($needs_fs{'remote'} && (!defined $lsb{$keyword}
+		    || $lsb{$keyword} !~ m,(?:^|\s)\$remote_fs(?:\s|$),));
+	    tag "missing-dependency-on-local_fs", "/etc/init.d/$_: $keyword"
+		if ($needs_fs{'local'} && (!defined $lsb{$keyword}
+		    || $lsb{$keyword} !~ m,(?:^|\s)\$(local|remote)_fs(?:\s|$),));
+	}
 	next unless defined $lsb{$keyword};
 	for my $dependency (split(/\s+/, $lsb{$keyword})) {
+
 	    if (defined $implied_dependencies{$dependency}) {
 		tag 'init.d-script-should-depend-on-virtual-facility',
 		    "/etc/init.d/$_",
diff --git a/checks/init.d.desc b/checks/init.d.desc
index d85a259..895a4f3 100644
--- a/checks/init.d.desc
+++ b/checks/init.d.desc
@@ -293,3 +293,35 @@ Info: The given <tt>/etc/init.d</tt> script indicates it should be
  needs special shutdown handling when rebooting the system (runlevel 6),
  it probably needs the same handling when halting the system (runlevel 0)
  or switching to single-user mode (runlevel 1).
+
+Tag: missing-dependency-on-remote_fs
+Severity: important
+Certainty: possible
+Info: The given init script seems to refer to <tt>/usr</tt>, possibly
+ using a file or binary from there.  Without a dependency on
+ <tt>$remote_fs</tt> on, possibly both, required-start and
+ required-stop the init script might be started too early or stopped
+ too late.  That is, the init script might be called before
+ <tt>/usr</tt> is mounted, or after it is unmounted, respectively.
+ .
+ Using should-start or should-stop to declare the dependency is
+ conceptually incorrect.  By doing so, the init script will always be
+ positioned as if <tt>$remote_fs</tt> had been set on a required-
+ field, and is therefore ignored by this check.
+Ref: http://wiki.debian.org/LSBInitScripts
+
+Tag: missing-dependency-on-local_fs
+Severity: important
+Certainty: possible
+Info: The given init script seems to refer to <tt>/var</tt>, possibly
+ using a file.  Without a dependency on <tt>$local_fs</tt> on, possibly
+ both, required-start and required-stop the init script might be started
+ too early or stopped too late.  That is, the init script might be
+ called before <tt>/var</tt> is mounted read/write, or after it is
+ unmounted, respectively.
+ .
+ Using should-start or should-stop to declare the dependency is
+ conceptually incorrect.  By doing so, the init script will always be
+ positioned as if <tt>$local_fs</tt> had been set on a required-
+ field, and is therefore ignored by this check.
+Ref: http://wiki.debian.org/LSBInitScripts
diff --git a/t/tests/init.d-lsb-headers/debian/debian/control.in b/t/tests/init.d-lsb-headers/debian/debian/control.in
index 7a5d193..0aec17c 100644
--- a/t/tests/init.d-lsb-headers/debian/debian/control.in
+++ b/t/tests/init.d-lsb-headers/debian/debian/control.in
@@ -20,3 +20,19 @@ Description: {$description} -- headers parsing
  This is a test package designed to exercise the parsing of init scripts
  by Lintian.  It is part of the Lintian test suite and may do very odd
  things.  It should not be installed like a regular package.
+
+Package: {$srcpkg}-remote
+Architecture: {$architecture}
+Depends: $\{shlibs:Depends\}, $\{misc:Depends\}
+Description: {$description} -- using /usr files
+ This is a test package designed to exercise the checking of init scripts
+ by Lintian.  It is part of the Lintian test suite and may do very odd
+ things.  It should not be installed like a regular package.
+
+Package: {$srcpkg}-local
+Architecture: {$architecture}
+Depends: $\{shlibs:Depends\}, $\{misc:Depends\}
+Description: {$description} -- using /var files
+ This is another test package designed to exercise the checking of init
+ scripts by Lintian.  It is part of the Lintian test suite and may do
+ very odd things.  It should not be installed like a regular package.
diff --git a/t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-local.init b/t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-local.init
new file mode 100644
index 0000000..c9b1074
--- /dev/null
+++ b/t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-local.init
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -e
+
+### BEGIN INIT INFO
+# Provides: init.d-lsb-headers-local
+# Required-Start:
+# Required-Stop: $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Do nothing at all
+### END INIT INFO
+
+exec >/var/log/foo.log
+
+case "$1" in
+    start|stop|force-reload|restart|status|*)
+	echo hello world!
+    ;;
+esac
diff --git a/t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-remote.init b/t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-remote.init
new file mode 100644
index 0000000..17de3c0
--- /dev/null
+++ b/t/tests/init.d-lsb-headers/debian/debian/init.d-lsb-headers-remote.init
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+set -e
+
+### BEGIN INIT INFO
+# Provides: init.d-lsb-headers-remote
+# Required-Start: $local_fs
+# Required-Stop:
+# Should-Stop: $remote_fs
+# Default-Start: 2 3 4 5
+# Default-Stop:
+# Short-Description: Do nothing at all
+### END INIT INFO
+
+. /usr/share/foo/lib.sh
+
+case "$1" in
+    start|stop|force-reload|restart|status|*)
+	echo hello world!
+    ;;
+esac
diff --git a/t/tests/init.d-lsb-headers/desc b/t/tests/init.d-lsb-headers/desc
index a0c512c..3645eb0 100644
--- a/t/tests/init.d-lsb-headers/desc
+++ b/t/tests/init.d-lsb-headers/desc
@@ -12,3 +12,5 @@ Test-For:
  init.d-script-provides-virtual-facility
  init.d-script-should-depend-on-virtual-facility
  init.d-script-starts-in-stop-runlevel
+ missing-dependency-on-remote_fs
+ missing-dependency-on-local_fs
diff --git a/t/tests/init.d-lsb-headers/tags b/t/tests/init.d-lsb-headers/tags
index 1503b44..d218a8a 100644
--- a/t/tests/init.d-lsb-headers/tags
+++ b/t/tests/init.d-lsb-headers/tags
@@ -1,3 +1,5 @@
+E: init.d-lsb-headers-local: missing-dependency-on-local_fs /etc/init.d/init.d-lsb-headers-local: required-start
+E: init.d-lsb-headers-remote: missing-dependency-on-remote_fs /etc/init.d/init.d-lsb-headers-remote: required-start
 E: init.d-lsb-headers: init.d-script-should-depend-on-virtual-facility /etc/init.d/init.d-lsb-headers mountall -> $local_fs
 E: init.d-lsb-headers: init.d-script-should-depend-on-virtual-facility /etc/init.d/init.d-lsb-headers mountnfs -> $remote_fs
 E: init.d-lsb-headers: init.d-script-should-depend-on-virtual-facility /etc/init.d/init.d-lsb-headers networking -> $network
-- 
1.6.3.3