4 Functions related debian binary packages
6 @contact: Debian FTPMaster <ftpmaster@debian.org>
7 @copyright: 2009 Mike O'Connor <stew@debian.org>
8 @license: GNU General Public License version 2 or later
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 ################################################################################
27 # <Ganneff> are we going the xorg way?
28 # <Ganneff> a dak without a dak.conf?
29 # <stew> automatically detect the wrong settings at runtime?
31 # <mhy> well, we'll probably always need dak.conf (how do you get the database setting
32 # <mhy> but removing most of the config into the database seems sane
33 # <Ganneff> mhy: dont spoil the fun
34 # <Ganneff> mhy: and i know how. we nmap localhost and check all open ports
35 # <Ganneff> maybe one answers to sql
36 # <stew> we will discover projectb via avahi
37 # <mhy> you're both sick
38 # <mhy> really fucking sick
40 ################################################################################
50 from debian_bundle import deb822
53 from config import Config
56 ################################################################################
60 ################################################################################
63 def __init__(self, filename, reject=None):
65 @type filename: string
66 @param filename: path of a .deb
68 @type reject: function
69 @param reject: a function to log reject messages to
71 self.filename = filename
74 self.wrapped_reject = reject
75 # Store rejects for later use
78 def reject(self, message):
80 if we were given a reject function, send the reject message,
81 otherwise send it to stderr.
83 print >> sys.stderr, message
84 self.rejects.append(message)
85 if self.wrapped_reject:
86 self.wrapped_reject(message)
90 make sure we cleanup when we are garbage collected.
96 we need to remove the temporary directory, if we created one
98 if self.tmpdir and os.path.exists(self.tmpdir):
99 shutil.rmtree(self.tmpdir)
103 # get a list of the ar contents
106 cmd = "ar t %s" % (self.filename)
107 (result, output) = commands.getstatusoutput(cmd)
110 print("%s: 'ar t' invocation failed." % (self.filename))
111 self.reject("%s: 'ar t' invocation failed." % (self.filename))
112 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
113 self.chunks = output.split('\n')
118 # Internal function which extracts the contents of the .ar to
119 # a temporary directory
122 tmpdir = utils.temp_dirname()
126 cmd = "ar x %s %s %s" % (os.path.join(cwd,self.filename), self.chunks[1], self.chunks[2])
127 (result, output) = commands.getstatusoutput(cmd)
129 print("%s: '%s' invocation failed." % (self.filename, cmd))
130 self.reject("%s: '%s' invocation failed." % (self.filename, cmd))
131 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
134 atexit.register( self._cleanup )
139 def valid_deb(self, relaxed=False):
141 Check deb contents making sure the .deb contains:
144 3. data.tar.gz or data.tar.bz2
145 in that order, and nothing else.
148 rejected = not self.chunks
150 if len(self.chunks) < 3:
152 self.reject("%s: found %d chunks, expected at least 3." % (self.filename, len(self.chunks)))
154 if len(self.chunks) != 3:
156 self.reject("%s: found %d chunks, expected 3." % (self.filename, len(self.chunks)))
157 if self.chunks[0] != "debian-binary":
159 self.reject("%s: first chunk is '%s', expected 'debian-binary'." % (self.filename, self.chunks[0]))
160 if not rejected and self.chunks[1] != "control.tar.gz":
162 self.reject("%s: second chunk is '%s', expected 'control.tar.gz'." % (self.filename, self.chunks[1]))
163 if not rejected and self.chunks[2] not in [ "data.tar.bz2", "data.tar.gz" ]:
165 self.reject("%s: third chunk is '%s', expected 'data.tar.gz' or 'data.tar.bz2'." % (self.filename, self.chunks[2]))
169 def scan_package(self, bootstrap_id=0, relaxed=False, session=None):
171 Unpack the .deb, do sanity checking, and gather info from it.
173 Currently information gathering consists of getting the contents list. In
174 the hopefully near future, it should also include gathering info from the
177 @type bootstrap_id: int
178 @param bootstrap_id: the id of the binary these packages
179 should be associated or zero meaning we are not bootstrapping
180 so insert into a temporary table
182 @return: True if the deb is valid and contents were imported
185 rejected = not self.valid_deb(relaxed)
191 if not rejected and self.tmpdir:
193 os.chdir(self.tmpdir)
194 if self.chunks[1] == "control.tar.gz":
195 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
196 control.extract('./control', self.tmpdir )
197 if self.chunks[2] == "data.tar.gz":
198 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
199 elif self.chunks[2] == "data.tar.bz2":
200 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
203 result = insert_content_paths(bootstrap_id, [tarinfo.name for tarinfo in data if not tarinfo.isdir()], session)
205 pkgs = deb822.Packages.iter_paragraphs(file(os.path.join(self.tmpdir,'control')))
207 result = insert_pending_content_paths(pkg,
208 self.filename.endswith('.udeb'),
209 [tarinfo.name for tarinfo in data if not tarinfo.isdir()],
213 traceback.print_exc()
219 def check_utf8_package(self, package):
221 Unpack the .deb, do sanity checking, and gather info from it.
223 Currently information gathering consists of getting the contents list. In
224 the hopefully near future, it should also include gathering info from the
227 @type package: string
228 @param package: the name of the package to be checked
231 @return: True if the deb is valid and contents were imported
233 rejected = not self.valid_deb(True)
236 if not rejected and self.tmpdir:
239 os.chdir(self.tmpdir)
240 if self.chunks[1] == "control.tar.gz":
241 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
242 control.extract('control', self.tmpdir )
243 if self.chunks[2] == "data.tar.gz":
244 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
245 elif self.chunks[2] == "data.tar.bz2":
246 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
250 unicode( tarinfo.name )
252 print >> sys.stderr, "E: %s has non-unicode filename: %s" % (package,tarinfo.name)
257 traceback.print_exc()
264 __all__.append('Binary')
267 def copy_temporary_contents(binary, bin_association, reject, session=None):
269 copy the previously stored contents from the temp table to the permanant one
271 during process-unchecked, the deb should have been scanned and the
272 contents stored in pending_content_associations
279 session = DBConn().session()
282 arch = get_architecture(archname, session=session)
284 pending = session.query(PendingBinContents).filter_by(package=binary.package,
285 version=binary.version,
286 arch=binary.arch).first()
289 # This should NOT happen. We should have added contents
290 # during process-unchecked. if it did, log an error, and send
293 "__PACKAGE__": package,
294 "__VERSION__": version,
296 "__TO_ADDRESS__": cnf["Dinstall::MyAdminAddress"],
297 "__DAK_ADDRESS__": cnf["Dinstall::MyEmailAddress"] }
299 message = utils.TemplateSubst(subst, cnf["Dir::Templates"]+"/missing-contents")
300 utils.send_mail(message)
303 exists = Binary(deb, reject).scan_package()
309 component = binary.poolfile.location.component
310 override = session.query(Override).filter_by(package=binary.package,
311 suite=bin_association.suite,
312 component=component.id).first()
318 if not override.overridetype.type.endswith('deb'):
321 if override.overridetype.type == "udeb":
322 table = "udeb_contents"
323 elif override.overridetype.type == "deb":
324 table = "deb_contents"
329 if component.name == "main":
332 component_str = component.name + "/"
334 vals = { 'package':binary.package,
335 'version':binary.version,
336 'arch':binary.architecture,
337 'binary_id': binary.id,
338 'component':component_str,
339 'section':override.section.section
342 session.execute( """INSERT INTO %s
343 (binary_id,package,version.component,arch,section,filename)
344 SELECT :binary_id, :package, :version, :component, :arch, :section
345 FROM pending_bin_contents pbc
346 WHERE pbc.package=:package
347 AND pbc.version=:version
348 AND pbc.arch=:arch""" % table, vals )
350 session.execute( """DELETE from pending_bin_contents package=:package
352 AND arch=:arch""", vals )
360 __all__.append('copy_temporary_contents')