tools/python/pkglint/userland.py
changeset 84 b80cfd4e0a16
parent 45 536ea324b223
child 99 c15c9099bb44
equal deleted inserted replaced
83:9ab0deb97868 84:b80cfd4e0a16
    19 #
    19 #
    20 # CDDL HEADER END
    20 # CDDL HEADER END
    21 #
    21 #
    22 
    22 
    23 #
    23 #
    24 # Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
    24 # Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
    25 #
    25 #
    26 
    26 
    27 # Some userland consolidation specific lint checks
    27 # Some userland consolidation specific lint checks
    28 
    28 
    29 import pkg.lint.base as base
    29 import pkg.lint.base as base
       
    30 import pkg.elf as elf
    30 import re
    31 import re
    31 import os.path
    32 import os.path
       
    33 
    32 
    34 
    33 class UserlandActionChecker(base.ActionChecker):
    35 class UserlandActionChecker(base.ActionChecker):
    34         """An opensolaris.org-specific class to check actions."""
    36         """An opensolaris.org-specific class to check actions."""
    35 
    37 
    36         name = "userland.action"
    38         name = "userland.action"
    42 		self.runpath_re = [
    44 		self.runpath_re = [
    43 			re.compile('^/lib/'),
    45 			re.compile('^/lib/'),
    44 			re.compile('^/usr/'),
    46 			re.compile('^/usr/'),
    45 			re.compile('^\$ORIGIN/')
    47 			re.compile('^\$ORIGIN/')
    46 		]
    48 		]
       
    49 		self.initscript_re = re.compile("^etc/(rc.|init)\.d")
    47                 super(UserlandActionChecker, self).__init__(config)
    50                 super(UserlandActionChecker, self).__init__(config)
    48 
    51 
    49 	def startup(self, engine):
    52 	def startup(self, engine):
    50 		if self.prototype != None:
    53 		if self.prototype != None:
    51 			engine.info(_("including prototype checks: %s") % self.prototype, msgid=self.name)
    54 			engine.info(_("including prototype checks: %s") %
    52 
    55 					self.prototype, msgid=self.name)
    53 	def file_exists(self, action, manifest, engine, pkglint_id="001"):
    56 
       
    57         def __realpath(self, path, target):
       
    58 		"""Combine path and target to get the real path."""
       
    59 
       
    60 		result = os.path.dirname(path)
       
    61 
       
    62 		for frag in target.split(os.sep):
       
    63 			if frag == '..':
       
    64 				result = os.path.dirname(result)
       
    65 			elif frag == '.':
       
    66 				pass
       
    67 			else:
       
    68 				result = os.path.join(result, frag)
       
    69 
       
    70 		return result
       
    71 
       
    72 	def __elf_runpath_check(self, path):
       
    73 		result = None
       
    74 		list = []
       
    75 
       
    76 		ed = elf.get_dynamic(path)
       
    77 		for dir in ed.get("runpath", "").split(":"):
       
    78 			if dir == None or dir == '':
       
    79 				continue
       
    80 
       
    81 			match = False
       
    82 			for expr in self.runpath_re:
       
    83 				if expr.match(dir):
       
    84 					match = True
       
    85 					break
       
    86 
       
    87 			if match == False:
       
    88 				list.append(dir)
       
    89 
       
    90 		if len(list) > 0:
       
    91 			result = _("bad RUNPATH, '%%s' includes '%s'" %
       
    92 				   ":".join(list))
       
    93 
       
    94 		return result
       
    95 
       
    96 	def __elf_wrong_location_check(self, path):
       
    97 		result = None
       
    98 
       
    99 		ei = elf.get_info(path)
       
   100 		bits = ei.get("bits")
       
   101 		frag = os.path.basename(os.path.dirname(path))
       
   102 
       
   103 		if bits == 32 and frag in ["sparcv9", "amd64", "64"]:
       
   104 			result = _("32-bit object '%s' in 64-bit path")
       
   105 		elif bits == 64 and frag not in ["sparcv9", "amd64", "64"]:
       
   106 			result = _("64-bit object '%s' in 32-bit path")
       
   107 
       
   108 		return result
       
   109 
       
   110 	def file_action(self, action, manifest, engine, pkglint_id="001"):
    54 		"""Checks for existence in the proto area."""
   111 		"""Checks for existence in the proto area."""
    55 
   112 
    56 		if self.prototype is None:
   113 		if action.name not in ["file"]:
    57 			return
   114 			return
       
   115 
       
   116 		path = action.attrs["path"]
       
   117 
       
   118 		# check for writable files without a preserve attribute
       
   119 		if 'mode' in action.attrs:
       
   120 			mode = action.attrs["mode"]
       
   121 
       
   122 			if (int(mode, 8) & 0222) != 0 and "preserve" not in action.attrs:
       
   123 				engine.error(
       
   124 				_("%(path)s is writable (%(mode)s), but missing a preserve"
       
   125 				  " attribute") %  {"path": path, "mode": mode},
       
   126 				msgid="%s%s.0" % (self.name, pkglint_id))
       
   127 
       
   128 		# checks that require a physical file to look at
       
   129 		if self.prototype is not None:
       
   130 			fullpath = self.prototype + "/" + path
       
   131 
       
   132 			if not os.path.exists(fullpath):
       
   133 				engine.info(
       
   134 					_("%s missing from proto area, skipping"
       
   135 					  " content checks") % path, 
       
   136 					msgid="%s%s.1" % (self.name, pkglint_id))
       
   137 			elif elf.is_elf_object(fullpath):
       
   138 				# 32/64 bit in wrong place
       
   139 				result = self.__elf_wrong_location_check(fullpath)
       
   140 				if result != None:
       
   141 					engine.error(result % path, 
       
   142 						msgid="%s%s.2" % (self.name, pkglint_id))
       
   143 				result = self.__elf_runpath_check(fullpath)
       
   144 				if result != None:
       
   145 					engine.error(result % path, 
       
   146 						msgid="%s%s.3" % (self.name, pkglint_id))
       
   147 
       
   148 	file_action.pkglint_desc = _("Paths should exist in the proto area.")
       
   149 
       
   150 	def link_resolves(self, action, manifest, engine, pkglint_id="002"):
       
   151 		"""Checks for link resolution."""
       
   152 
       
   153 		if action.name not in ["link", "hardlink"]:
       
   154 			return
       
   155 
       
   156 		path = action.attrs["path"]
       
   157 		target = action.attrs["target"]
       
   158 		realtarget = self.__realpath(path, target)
       
   159 		
       
   160 		resolved = False
       
   161 		for maction in manifest.gen_actions():
       
   162 			mpath = None
       
   163 			if maction.name in ["dir", "file", "link",
       
   164 						"hardlink"]:
       
   165 				mpath = maction.attrs["path"]
       
   166 
       
   167 			if mpath and mpath == realtarget:
       
   168 				resolved = True
       
   169 				break
       
   170 
       
   171 		if resolved != True:
       
   172 			engine.error(
       
   173 				_("%s %s has unresolvable target '%s'") %
       
   174 					(action.name, path, target),
       
   175 				msgid="%s%s.0" % (self.name, pkglint_id))
       
   176 
       
   177 	link_resolves.pkglint_desc = _("links should resolve.")
       
   178 
       
   179 	def init_script(self, action, manifest, engine, pkglint_id="003"):
       
   180 		"""Checks for SVR4 startup scripts."""
    58 
   181 
    59 		if action.name not in ["file", "dir", "link", "hardlink"]:
   182 		if action.name not in ["file", "dir", "link", "hardlink"]:
    60 			return
   183 			return
    61 
   184 
    62 		path = action.attrs["path"]
   185 		path = action.attrs["path"]
    63 		fullpath = self.prototype + "/" + path
   186 		if self.initscript_re.match(path):
    64 		if not os.path.exists(fullpath):
   187 			engine.warning(
    65 			engine.error(
   188 				_("SVR4 startup '%s', deliver SMF"
    66 				_("packaged path '%s' missing from proto area") % path, 
   189 				  " service instead") % path,
    67 				msgid="%s%s.0" % (self.name, pkglint_id))
   190 				msgid="%s%s.0" % (self.name, pkglint_id))
    68 
   191 
    69 	file_exists.pkglint_desc = _("Paths should exist in the proto area.")
   192 	init_script.pkglint_desc = _(
    70 
   193 		"SVR4 startup scripts should not be delivered.")
    71 	def file_content(self, action, manifest, engine, pkglint_id="002"):
       
    72 		"""Checks for file content issues."""
       
    73 
       
    74 		if self.prototype is None:
       
    75 			return
       
    76 
       
    77 		if action.name is not "file":
       
    78 			return
       
    79 
       
    80 		import pkg.elf as elf
       
    81 
       
    82 		path = action.attrs["path"]
       
    83 		fullpath = self.prototype + "/" + path
       
    84 
       
    85 		if elf.is_elf_object(fullpath):
       
    86 			ed = elf.get_dynamic(fullpath)
       
    87 			for dir in ed.get("runpath", "").split(":"):
       
    88 				if dir == None or dir == '':
       
    89 					continue
       
    90 
       
    91 				match = False
       
    92 				for expr in self.runpath_re:
       
    93 					if expr.match(dir):
       
    94 						match = True
       
    95 						break
       
    96 
       
    97 				if match == False:
       
    98 					engine.error(
       
    99 						_("%s has bad RUNPATH, "
       
   100 					  	"includes: %s") % (path, dir),
       
   101 						msgid="%s%s.1" % (self.name, pkglint_id))
       
   102 		# additional checks for different content types should go here
       
   103 
       
   104 	file_content.pkglint_desc = _("Paths should not deliver common mistakes.")
       
   105 
   194 
   106 class UserlandManifestChecker(base.ManifestChecker):
   195 class UserlandManifestChecker(base.ManifestChecker):
   107         """An opensolaris.org-specific class to check manifests."""
   196         """An opensolaris.org-specific class to check manifests."""
   108 
   197 
   109         name = "userland.manifest"
   198         name = "userland.manifest"
   110 
   199 
   111 	def __init__(self, config):
   200 	def __init__(self, config):
   112 		self.prototype = os.getenv('PROTO_DIR')
   201 		self.prototype = os.getenv('PROTO_DIR')
   113 		super(UserlandManifestChecker, self).__init__(config)
   202 		super(UserlandManifestChecker, self).__init__(config)
   114 
   203 
   115 	def unpackaged_files(self, manifest, engine, pkglint_id="001"):
   204 	def license_check(self, manifest, engine, pkglint_id="001"):
   116 		if self.prototype == None:
       
   117 			return
       
   118 
       
   119 		return	# skip this check for now.  It needs more work
       
   120 
       
   121 		manifest_paths = []
   205 		manifest_paths = []
   122 
   206 		files = False
   123 		for action in manifest.gen_actions():
   207 		license = False
   124 			if action.name in ["file", "dir", "link", "hardlink"]:
   208 
   125 				manifest_paths.append(action.attrs.get("path"))
   209 		for action in manifest.gen_actions_by_type("file"):
   126 
   210 			files = True
   127 		for dirname, dirnames, filenames in os.walk(self.prototype):
   211 			break
   128 			dir = dirname[len(self.prototype):]
   212 
   129 			for name in filenames + dirnames:
   213 		if files == False:
   130 				path = dir + '/' + name
   214 			return
   131 				if path not in manifest_paths:
   215 
   132 					engine.error(
   216 		for action in manifest.gen_actions_by_type("license"):
   133 						_("unpackaged path '%s' missing from manifest") % path, 
   217 			return
   134 					msgid="%s%s.0" % (self.name, pkglint_id))
   218 
   135 
   219 		engine.error( _("missing license action"),
   136 	unpackaged_files.pkglint_dest = _(
   220 			msgid="%s%s.0" % (self.name, pkglint_id))
   137 		"Prototype paths should be present.")
   221 
   138 
   222 	license_check.pkglint_dest = _(
       
   223 		"license actions are required if you deliver files.")