Added lots more modules from lintian. Maemian appears to work.
[maemian] / checks / init.d
1 # init.d -- lintian check script -*- perl -*-
2
3 # Copyright (C) 1998 Christian Schwarz
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, you can find it on the World Wide
17 # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
18 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
21 package Maemian::init_d;
22 use strict;
23 use Tags;
24 use Util;
25
26 # A list of valid LSB keywords.  The value is 0 if optional and 1 if required.
27 my %lsb_keywords = (provides            => 1,
28                     'required-start'    => 1,
29                     'required-stop'     => 1,
30                     'should-start'      => 0,
31                     'should-stop'       => 0,
32                     'default-start'     => 1,
33                     'default-stop'      => 1,
34                     'short-description' => 1,
35                     'description'       => 0);
36
37 sub run {
38
39 my $pkg = shift;
40 my $type = shift;
41
42 my $postinst = "control/postinst";
43 my $preinst = "control/preinst";
44 my $postrm = "control/postrm";
45 my $prerm = "control/prerm";
46 my $conffiles = "control/conffiles";
47
48 my %initd_postinst;
49 my %initd_postrm;
50 my %conffiles;
51
52 my $opts_r = qr/-\S+\s*/;
53 my $name_r = qr/[\w.-]+/;
54 my $action_r = qr/\w+/;
55 my $exclude_r = qr/if\s+\[\s+-x\s+\S*update-rc\.d/;
56
57 # read postinst control file
58 if (open(IN, '<', $postinst)) {
59     while (<IN>) {
60         next if /$exclude_r/o;
61         s/\#.*$//o;
62         next unless /^(?:.+;|^\s*system[\s\(\']+)?\s*update-rc\.d\s+
63             (?:$opts_r)*($name_r)\s+($action_r)/xo;
64         my ($name,$opt) = ($1,$2);
65         next if $opt eq 'remove';
66         if ($initd_postinst{$name}++ == 1) {
67             tag "duplicate-updaterc.d-calls-in-postinst", "$name";
68             next;
69         }
70         unless (m,>\s*/dev/null,o) {
71             tag "output-of-updaterc.d-not-redirected-to-dev-null", "$name postinst";
72         }
73     }
74 }
75 close(IN);
76
77 # read preinst control file
78 if (open(IN, '<', $preinst)) {
79     while (<IN>) {
80         next if /$exclude_r/o;
81         s/\#.*$//o;
82         next unless m/update-rc\.d\s+(?:$opts_r)*($name_r)\s+($action_r)/o;
83         my ($name,$opt) = ($1,$2);
84         next if $opt eq 'remove';
85         tag "preinst-calls-updaterc.d", "$name";
86     }
87     close(IN);
88 }
89
90 # read postrm control file
91 if (open(IN, '<', $postrm)) {
92     while (<IN>) {
93         next if /$exclude_r/o;
94         s/\#.*$//o;
95         next unless m/update-rc\.d\s+($opts_r)*($name_r)/o;
96         if ($initd_postrm{$2}++ == 1) {
97             tag "duplicate-updaterc.d-calls-in-postrm", "$2";
98             next;
99         }
100         unless (m,>\s*/dev/null,o) {
101             tag "output-of-updaterc.d-not-redirected-to-dev-null", "$2 postrm";
102         }
103     }
104     close(IN);
105 }
106
107 # read prerm control file
108 if (open(IN, '<', $prerm)) {
109     while (<IN>) {
110         next if /$exclude_r/o;
111         s/\#.*$//o;
112         next unless m/update-rc\.d\s+($opts_r)*($name_r)/o;
113         tag "prerm-calls-updaterc.d", "$2";
114     }
115     close(IN);
116 }
117
118 # init.d scripts have to be removed in postrm
119 for (keys %initd_postinst) {
120     if ($initd_postrm{$_}) {
121         delete $initd_postrm{$_};
122     } else {
123         tag "postrm-does-not-call-updaterc.d-for-init.d-script", "/etc/init.d/$_";
124     }
125 }
126 for (keys %initd_postrm) {
127     tag "postrm-contains-additional-updaterc.d-calls", "/etc/init.d/$_";
128 }
129
130 # load conffiles
131 if (open(IN, '<', $conffiles)) {
132     while (<IN>) {
133         chop;
134         next if m/^\s*$/o;
135         $conffiles{$_} = 1;
136
137         if (m,^/?etc/rc.\.d,o) {
138             tag "file-in-etc-rc.d-marked-as-conffile", "$_";
139         }
140     }
141     close(IN);
142 }
143
144 for (keys %initd_postinst) {
145     next if /^\$/;
146     # init.d scripts have to be marked as conffiles
147     unless ($conffiles{"/etc/init.d/$_"} or $conffiles{"etc/init.d/$_"}) {
148         tag "init.d-script-not-marked-as-conffile", "/etc/init.d/$_";
149     }
150
151     # check if file exists in package
152     my $initd_file = "init.d/$_";
153     if (-f $initd_file) {
154         # yes! check it...
155         open(IN, '<', $initd_file)
156             or fail("cannot open init.d file $initd_file: $!");
157         my (%tag, %lsb);
158         while (defined(my $l = <IN>)) {
159             if ($l =~ m/^\#\#\# BEGIN INIT INFO/) {
160                 if ($lsb{BEGIN}) {
161                     tag "init.d-script-has-duplicate-lsb-section", "/etc/init.d/$_";
162                     next;
163                 }
164                 $lsb{BEGIN} = 1;
165                 my $last;
166
167                 # We have an LSB keyword section.  Parse it and save the data
168                 # in %lsb for analysis.
169                 while (defined(my $l = <IN>)) {
170                     if ($l =~ /^\#\#\# END INIT INFO/) {
171                         $lsb{END} = 1;
172                         last;
173                     } elsif ($l !~ /^\#/) {
174                         tag "init.d-script-has-unterminated-lsb-section", "/etc/init.d/$_:$.";
175                         last;
176                     } elsif ($l =~ /^\# ([a-zA-Z-]+):\s*(.*?)\s*$/) {
177                         my $keyword = lc $1;
178                         my $value = $2;
179                         tag "init.d-script-has-duplicate-lsb-keyword", "/etc/init.d/$_:$. $keyword"
180                             if (defined $lsb{$keyword});
181                         tag "init.d-script-has-unknown-lsb-keyword", "/etc/init.d/$_:$. $keyword"
182                             unless (defined ($lsb_keywords{$keyword}) || $keyword =~ /^x-/);
183                         $lsb{$keyword} = $value || '';
184                         $last = $keyword;
185                     } elsif ($l =~ /^\#(\t|  )/ && $last eq 'description') {
186                         my $value = $l;
187                         $value =~ s/^\#\s*//;
188                         $lsb{description} .= ' ' . $value;
189                     } else {
190                         tag "init.d-script-has-bad-lsb-line", "/etc/init.d/$_:$.";
191                     }
192                 }
193             }
194
195             while ($l =~ s/(start|stop|restart|force-reload)//o) {
196                 $tag{$1} = 1;
197             }
198         }
199         close(IN);
200
201         # Make sure all of the required keywords are present.
202         if (not $lsb{BEGIN}) {
203             tag "init.d-script-missing-lsb-section", "/etc/init.d/$_";
204         } else {
205             for my $keyword (keys %lsb_keywords) {
206                 if ($lsb_keywords{$keyword} && !defined $lsb{$keyword}) {
207                     if ($keyword eq 'short-description') {
208                         tag "init.d-script-missing-lsb-short-description", "/etc/init.d/$_";
209                     } else {
210                         tag "init.d-script-missing-lsb-keyword", "/etc/init.d/$_ $keyword";
211                     }
212                 }
213             }
214         }
215
216         # Check the runlevels.
217         my %start;
218         if ($lsb{'default-start'}) {
219             for my $runlevel (split (/\s+/, $lsb{'default-start'})) {
220                 if ($runlevel =~ /^[sS0-6]$/) {
221                     $start{lc $runlevel} = 1;
222                 } else {
223                     tag "init.d-script-has-bad-start-runlevel", "/etc/init.d/$_ $runlevel";
224                 }
225             }
226         }
227         if ($lsb{'default-stop'}) {
228             for my $runlevel (split (/\s+/, $lsb{'default-stop'})) {
229                 if ($runlevel =~ /^[sS0-6]$/) {
230                     if ($start{$runlevel}) {
231                         tag "init.d-script-has-conflicting-start-stop", "/etc/init.d/$_ $runlevel";
232                     }
233                     if ($runlevel =~ /[sS]/) {
234                         tag "init-d-script-stops-in-s-runlevel", "/etc/init.d/$_";
235                     }
236                 } else {
237                     tag "init.d-script-has-bad-stop-runlevel", "/etc/init.d/$_ $runlevel";
238                 }
239             }
240         }
241
242         # all tags included in file?
243         $tag{'start'} or tag "init.d-script-does-not-implement-required-option", "/etc/init.d/$_ start";
244         $tag{'stop'} or tag "init.d-script-does-not-implement-required-option", "/etc/init.d/$_ stop";
245         $tag{'restart'} or tag "init.d-script-does-not-implement-required-option", "/etc/init.d/$_ restart";
246         $tag{'force-reload'} or tag "init.d-script-does-not-implement-required-option", "/etc/init.d/$_ force-reload";
247     } else {
248         tag "init.d-script-not-included-in-package", "/etc/init.d/$_";
249     }
250 }
251
252 # files actually installed in /etc/init.d should match our list :-)
253 opendir(INITD, "init.d") or fail("cannot read init.d directory: $!");
254 for (readdir(INITD)) {
255     next if $_ eq '.' || $_ eq '..';
256     tag "script-in-etc-init.d-not-registered-via-update-rc.d", "/etc/init.d/$_"
257         unless $initd_postinst{$_};
258 }
259 closedir(INITD);
260
261 }
262
263 1;
264
265 # Local Variables:
266 # indent-tabs-mode: t
267 # cperl-indent-level: 4
268 # End:
269 # vim: syntax=perl ts=8