Norm Jacobs
2011-04-13 4158c02ccf09e2f646cf2e9e5599f186ec8c7302
commit | author | age
f811d2 1 #!/usr/bin/python
NJ 2 #
3 # CDDL HEADER START
4 #
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
8 #
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
13 #
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
19 #
20 # CDDL HEADER END
21 #
22
23 #
694ef3 24 # Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
f811d2 25 #
NJ 26
27 # Some userland consolidation specific lint checks
28
29 import pkg.lint.base as base
694ef3 30 import pkg.elf as elf
f811d2 31 import re
NJ 32 import os.path
694ef3 33
f811d2 34
NJ 35 class UserlandActionChecker(base.ActionChecker):
36         """An opensolaris.org-specific class to check actions."""
37
38         name = "userland.action"
39
40         def __init__(self, config):
41                 self.description = _(
42                     "checks Userland packages for common content errors")
8a6659 43         path = os.getenv('PROTO_PATH')
NJ 44         if path != None:
45             self.proto_path = path.split()
46         else:
47             self.proto_path = None
f811d2 48         self.runpath_re = [
35a012 49             re.compile('^/lib(/.*)?$'),
f811d2 50             re.compile('^/usr/'),
NJ 51             re.compile('^\$ORIGIN/')
52         ]
694ef3 53         self.initscript_re = re.compile("^etc/(rc.|init)\.d")
f811d2 54                 super(UserlandActionChecker, self).__init__(config)
NJ 55
56     def startup(self, engine):
8a6659 57         pass
f811d2 58
694ef3 59         def __realpath(self, path, target):
NJ 60         """Combine path and target to get the real path."""
61
62         result = os.path.dirname(path)
63
64         for frag in target.split(os.sep):
65             if frag == '..':
66                 result = os.path.dirname(result)
67             elif frag == '.':
68                 pass
69             else:
70                 result = os.path.join(result, frag)
71
72         return result
73
74     def __elf_runpath_check(self, path):
75         result = None
76         list = []
77
78         ed = elf.get_dynamic(path)
79         for dir in ed.get("runpath", "").split(":"):
80             if dir == None or dir == '':
81                 continue
82
83             match = False
84             for expr in self.runpath_re:
85                 if expr.match(dir):
86                     match = True
87                     break
88
89             if match == False:
90                 list.append(dir)
91
92         if len(list) > 0:
93             result = _("bad RUNPATH, '%%s' includes '%s'" %
94                    ":".join(list))
95
96         return result
97
98     def __elf_wrong_location_check(self, path):
99         result = None
100
101         ei = elf.get_info(path)
102         bits = ei.get("bits")
fe3c1d 103                 elems = os.path.dirname(path).split("/")
694ef3 104
fe3c1d 105                 if ("amd64" in elems) or ("sparcv9" in elems) or ("64" in elems):
LR 106                     path64 = True
107                 else:
108                     path64 = False
109
110         if bits == 32 and path64:
694ef3 111             result = _("32-bit object '%s' in 64-bit path")
fe3c1d 112         elif bits == 64 and not path64:
694ef3 113             result = _("64-bit object '%s' in 32-bit path")
NJ 114         return result
115
116     def file_action(self, action, manifest, engine, pkglint_id="001"):
f811d2 117         """Checks for existence in the proto area."""
NJ 118
694ef3 119         if action.name not in ["file"]:
f811d2 120             return
694ef3 121
8a6659 122         path = action.hash
NJ 123         if path == None or path == 'NOHASH':
124             path = action.attrs["path"]
694ef3 125
NJ 126         # check for writable files without a preserve attribute
d05a6f 127         if "mode" in action.attrs:
694ef3 128             mode = action.attrs["mode"]
NJ 129
130             if (int(mode, 8) & 0222) != 0 and "preserve" not in action.attrs:
131                 engine.error(
132                 _("%(path)s is writable (%(mode)s), but missing a preserve"
133                   " attribute") %  {"path": path, "mode": mode},
134                 msgid="%s%s.0" % (self.name, pkglint_id))
d05a6f 135         elif "preserve" in action.attrs:
NJ 136             if "mode" in action.attrs:
137                 mode = action.attrs["mode"]
138                 if (int(mode, 8) & 0222) == 0:
139                     engine.error(
140                     _("%(path)s has a preserve action, but is not writable (%(mode)s)") %  {"path": path, "mode": mode},
141                 msgid="%s%s.4" % (self.name, pkglint_id))
142             else:
143                 engine.error(
144                 _("%(path)s has a preserve action, but no mode") %  {"path": path, "mode": mode},
145                 msgid="%s%s.3" % (self.name, pkglint_id))
694ef3 146
NJ 147         # checks that require a physical file to look at
8a6659 148         if self.proto_path is not None:
NJ 149             for directory in self.proto_path:
150                 fullpath = directory + "/" + path
151
152                 if os.path.exists(fullpath):
153                     break
694ef3 154
NJ 155             if not os.path.exists(fullpath):
156                 engine.info(
157                     _("%s missing from proto area, skipping"
158                       " content checks") % path, 
159                     msgid="%s%s.1" % (self.name, pkglint_id))
160             elif elf.is_elf_object(fullpath):
161                 # 32/64 bit in wrong place
162                 result = self.__elf_wrong_location_check(fullpath)
163                 if result != None:
164                     engine.error(result % path, 
165                         msgid="%s%s.2" % (self.name, pkglint_id))
166                 result = self.__elf_runpath_check(fullpath)
167                 if result != None:
168                     engine.error(result % path, 
169                         msgid="%s%s.3" % (self.name, pkglint_id))
170
171     file_action.pkglint_desc = _("Paths should exist in the proto area.")
172
173     def link_resolves(self, action, manifest, engine, pkglint_id="002"):
174         """Checks for link resolution."""
175
176         if action.name not in ["link", "hardlink"]:
177             return
178
179         path = action.attrs["path"]
180         target = action.attrs["target"]
181         realtarget = self.__realpath(path, target)
182         
183         resolved = False
184         for maction in manifest.gen_actions():
185             mpath = None
186             if maction.name in ["dir", "file", "link",
187                         "hardlink"]:
188                 mpath = maction.attrs["path"]
189
190             if mpath and mpath == realtarget:
191                 resolved = True
192                 break
193
194         if resolved != True:
195             engine.error(
196                 _("%s %s has unresolvable target '%s'") %
197                     (action.name, path, target),
198                 msgid="%s%s.0" % (self.name, pkglint_id))
199
200     link_resolves.pkglint_desc = _("links should resolve.")
201
202     def init_script(self, action, manifest, engine, pkglint_id="003"):
203         """Checks for SVR4 startup scripts."""
f811d2 204
NJ 205         if action.name not in ["file", "dir", "link", "hardlink"]:
206             return
207
208         path = action.attrs["path"]
694ef3 209         if self.initscript_re.match(path):
NJ 210             engine.warning(
211                 _("SVR4 startup '%s', deliver SMF"
212                   " service instead") % path,
f811d2 213                 msgid="%s%s.0" % (self.name, pkglint_id))
NJ 214
694ef3 215     init_script.pkglint_desc = _(
NJ 216         "SVR4 startup scripts should not be delivered.")
f811d2 217
NJ 218 class UserlandManifestChecker(base.ManifestChecker):
219         """An opensolaris.org-specific class to check manifests."""
220
221         name = "userland.manifest"
222
223     def __init__(self, config):
224         super(UserlandManifestChecker, self).__init__(config)
225
4158c0 226     def component_check(self, manifest, engine, pkglint_id="001"):
694ef3 227         manifest_paths = []
NJ 228         files = False
229         license = False
230
231         for action in manifest.gen_actions_by_type("file"):
232             files = True
233             break
234
235         if files == False:
f811d2 236             return
NJ 237
694ef3 238         for action in manifest.gen_actions_by_type("license"):
4158c0 239             license = True
NJ 240             break
f811d2 241
4158c0 242         if license == False:
NJ 243             engine.error( _("missing license action"),
244                 msgid="%s%s.0" % (self.name, pkglint_id))
f811d2 245
4158c0 246         if 'opensolaris.arc_url' not in manifest:
NJ 247             engine.error( _("missing ARC data (opensolaris.arc_url)"),
248                 msgid="%s%s.0" % (self.name, pkglint_id))
249
250     component_check.pkglint_dest = _(
251         "license actions and iARC information are required if you deliver files.")