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 44 45 46 47 48 49 50 51 52 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 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
|
# -*- coding: utf-8 -*-
#############################################################################
# Project : Mandriva Linux
# Module : rpmlint
# File : InitScriptCheck.py
# Author : Frederic Lepied
# Created On : Fri Aug 25 09:26:37 2000
# Purpose : check init scripts (files in /etc/rc.d/init.d)
#############################################################################
import os
import re
import sys
import rpm
from Filter import addDetails, printError, printWarning
import AbstractCheck
import Config
import Pkg
chkconfig_content_regex = re.compile('^\s*#\s*chkconfig:\s*([-0-9]+)\s+[-0-9]+\s+[-0-9]+')
subsys_regex = re.compile('/var/lock/subsys/([^/"\'\n\s;&|]+)', re.MULTILINE)
chkconfig_regex = re.compile('^[^#]*(chkconfig|add-service|del-service)', re.MULTILINE)
status_regex = re.compile('^[^#]*status', re.MULTILINE)
reload_regex = re.compile('^[^#]*reload', re.MULTILINE)
use_deflevels = Config.getOption('UseDefaultRunlevels', True)
lsb_tags_regex = re.compile('^# ([\w-]+):\s*(.*?)\s*$')
lsb_cont_regex = re.compile('^#(?:\t| )(.*?)\s*$')
use_subsys = Config.getOption('UseVarLockSubsys', True)
LSB_KEYWORDS = ('Provides', 'Required-Start', 'Required-Stop', 'Should-Start',
'Should-Stop', 'Default-Start', 'Default-Stop',
'Short-Description', 'Description')
RECOMMENDED_LSB_KEYWORDS = ('Provides', 'Required-Start', 'Required-Stop',
'Default-Stop', 'Short-Description')
class InitScriptCheck(AbstractCheck.AbstractCheck):
def __init__(self):
AbstractCheck.AbstractCheck.__init__(self, 'InitScriptCheck')
def check_binary(self, pkg):
initscript_list = []
for fname, pkgfile in pkg.files().items():
if not fname.startswith('/etc/init.d/') and \
not fname.startswith('/etc/rc.d/init.d/'):
continue
basename = os.path.basename(fname)
initscript_list.append(basename)
if pkgfile.mode & int("500", 8) != int("500", 8):
printError(pkg, 'init-script-non-executable', fname)
if "." in basename:
printError(pkg, 'init-script-name-with-dot', fname)
# check chkconfig call in %post and %preun
postin = Pkg.b2s(pkg[rpm.RPMTAG_POSTIN]) or \
pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
if not postin:
printError(pkg, 'init-script-without-chkconfig-postin', fname)
elif not chkconfig_regex.search(postin):
printError(pkg, 'postin-without-chkconfig', fname)
preun = Pkg.b2s(pkg[rpm.RPMTAG_PREUN]) or \
pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
if not preun:
printError(pkg, 'init-script-without-chkconfig-preun', fname)
elif not chkconfig_regex.search(preun):
printError(pkg, 'preun-without-chkconfig', fname)
status_found = False
reload_found = False
chkconfig_content_found = False
subsys_regex_found = False
in_lsb_tag = False
in_lsb_description = False
lastline = ''
lsb_tags = {}
# check common error in file content
content = None
try:
content = Pkg.readlines(pkgfile.path)
except Exception:
e = sys.exc_info()[1]
printWarning(pkg, 'read-error', e)
continue
content_str = "".join(content)
for line in content:
line = line[:-1] # chomp
# TODO check if there is only one line like this
if line.startswith('### BEGIN INIT INFO'):
in_lsb_tag = True
continue
if line.endswith('### END INIT INFO'):
in_lsb_tag = False
for kw, vals in lsb_tags.items():
if len(vals) != 1:
printError(pkg, 'redundant-lsb-keyword', kw)
for kw in RECOMMENDED_LSB_KEYWORDS:
if kw not in lsb_tags:
printWarning(pkg, 'missing-lsb-keyword',
"%s in %s" % (kw, fname))
if in_lsb_tag:
# TODO maybe we do not have to handle this ?
if lastline.endswith('\\'):
line = lastline + line
else:
res = lsb_tags_regex.search(line)
if not res:
cres = lsb_cont_regex.search(line)
if not (in_lsb_description and cres):
in_lsb_description = False
printError(
pkg, 'malformed-line-in-lsb-comment-block',
line)
else:
lsb_tags["Description"][-1] += \
" " + cres.group(1)
else:
tag = res.group(1)
if not tag.startswith('X-') and \
tag not in LSB_KEYWORDS:
printError(pkg, 'unknown-lsb-keyword', line)
else:
in_lsb_description = (tag == 'Description')
if tag not in lsb_tags:
lsb_tags[tag] = []
lsb_tags[tag].append(res.group(2))
lastline = line
if not status_found and status_regex.search(line):
status_found = True
if not reload_found and reload_regex.search(line):
reload_found = True
res = chkconfig_content_regex.search(line)
if res:
chkconfig_content_found = True
if use_deflevels:
if res.group(1) == '-':
printWarning(pkg, 'no-default-runlevel', fname)
elif res.group(1) != '-':
printWarning(pkg, 'service-default-enabled', fname)
res = subsys_regex.search(line)
if res:
subsys_regex_found = True
name = res.group(1)
if use_subsys and name != basename:
error = True
if name[0] == '$':
value = Pkg.substitute_shell_vars(name,
content_str)
if value == basename:
error = False
else:
i = name.find('}')
if i != -1:
name = name[0:i]
error = name != basename
if error and len(name):
if name[0] == '$':
printWarning(pkg, 'incoherent-subsys', fname,
name)
else:
printError(pkg, 'incoherent-subsys', fname,
name)
if "Default-Start" in lsb_tags:
if "".join(lsb_tags["Default-Start"]):
printWarning(pkg, 'service-default-enabled', fname)
if not status_found:
printError(pkg, 'no-status-entry', fname)
if not reload_found:
printWarning(pkg, 'no-reload-entry', fname)
if not chkconfig_content_found:
printError(pkg, 'no-chkconfig-line', fname)
if not subsys_regex_found and use_subsys:
printError(pkg, 'subsys-not-used', fname)
elif subsys_regex_found and not use_subsys:
printError(pkg, 'subsys-unsupported', fname)
if len(initscript_list) == 1:
pkgname = re.sub("-sysvinit$", "", pkg.name.lower())
goodnames = (pkgname, pkgname + 'd')
if initscript_list[0] not in goodnames:
printWarning(pkg, 'incoherent-init-script-name',
initscript_list[0], str(goodnames))
# Create an object to enable the auto registration of the test
check = InitScriptCheck()
addDetails(
'init-script-without-chkconfig-postin',
'''The package contains an init script but doesn't contain a %post with
a call to chkconfig.''',
'postin-without-chkconfig',
'''The package contains an init script but doesn't call chkconfig in its
%post script.''',
'init-script-without-chkconfig-preun',
'''The package contains an init script but doesn't contain a %preun with
a call to chkconfig.''',
'preun-without-chkconfig',
'''The package contains an init script but doesn't call chkconfig in its
%preun script.''',
'missing-lsb-keyword',
'''The package contains an init script that does not contain one of the LSB
init script comment block convention keywords that are recommendable for all
init scripts. If there is nothing to add to a keyword's value, include the
keyword in the script with an empty value. Note that as of version 3.2, the
LSB specification does not mandate presence of any keywords.''',
'no-status-entry',
'''In your init script (/etc/rc.d/init.d/your_file), you don't
have a 'status' entry, which is necessary for good functionality.''',
'no-reload-entry',
'''In your init script (/etc/rc.d/init.d/your_file), you don't
have a 'reload' entry, which is necessary for good functionality.''',
'no-chkconfig-line',
'''The init script doesn't contain a chkconfig line to specify the runlevels
at which to start and stop it.''',
'no-default-runlevel',
'''The default runlevel isn't specified in the init script.''',
'service-default-enabled',
'''The service is enabled by default after "chkconfig --add"; for security
reasons, most services should not be. Use "-" as the default runlevel in the
init script's "chkconfig:" line and/or remove the "Default-Start:" LSB keyword
to fix this if appropriate for this service.''',
'subsys-unsupported',
'''The init script uses /var/lock/subsys which is not supported by
this distribution.''',
'subsys-not-used',
'''While your daemon is running, you have to put a lock file in
/var/lock/subsys/. To see an example, look at this directory on your
machine and examine the corresponding init scripts.''',
'incoherent-subsys',
'''The filename of your lock file in /var/lock/subsys/ is incoherent
with your actual init script name. For example, if your script name
is httpd, you have to use 'httpd' as the filename in your subsys directory.
It is also possible that rpmlint gets this wrong, especially if the init
script contains nontrivial shell variables and/or assignments. These
cases usually manifest themselves when rpmlint reports that the subsys name
starts a with '$'; in these cases a warning instead of an error is reported
and you should check the script manually.''',
'incoherent-init-script-name',
'''The init script name should be the same as the package name in lower case,
or one with 'd' appended if it invokes a process by that name.''',
'init-script-name-with-dot',
'''The init script name should not contain a dot in its name. Some versions
of chkconfig don't work as expected with init script names like that.''',
'init-script-non-executable',
'''The init script should have at least the execution bit set for root
in order for it to run at boot time.''',
)
# InitScriptCheck.py ends here
# Local variables:
# indent-tabs-mode: nil
# py-indent-offset: 4
# End:
# ex: ts=4 sw=4 et
|