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)
255 traceback.print_exc()
260 __all__.append('Binary')
263 def copy_temporary_contents(binary, bin_association, reject, session=None):
265 copy the previously stored contents from the temp table to the permanant one
267 during process-unchecked, the deb should have been scanned and the
268 contents stored in pending_content_associations
275 session = DBConn().session()
278 arch = get_architecture(archname, session=session)
280 pending = session.query(PendingBinContents).filter_by(package=binary.package,
281 version=binary.version,
282 arch=binary.arch).first()
285 # This should NOT happen. We should have added contents
286 # during process-unchecked. if it did, log an error, and send
289 "__PACKAGE__": package,
290 "__VERSION__": version,
292 "__TO_ADDRESS__": cnf["Dinstall::MyAdminAddress"],
293 "__DAK_ADDRESS__": cnf["Dinstall::MyEmailAddress"] }
295 message = utils.TemplateSubst(subst, cnf["Dir::Templates"]+"/missing-contents")
296 utils.send_mail(message)
299 exists = Binary(deb, reject).scan_package()
305 component = binary.poolfile.location.component
306 override = session.query(Override).filter_by(package=binary.package,
307 suite=bin_association.suite,
308 component=component.id).first()
314 if not override.overridetype.type.endswith('deb'):
317 if override.overridetype.type == "udeb":
318 table = "udeb_contents"
319 elif override.overridetype.type == "deb":
320 table = "deb_contents"
325 if component.name == "main":
328 component_str = component.name + "/"
330 vals = { 'package':binary.package,
331 'version':binary.version,
332 'arch':binary.architecture,
333 'binary_id': binary.id,
334 'component':component_str,
335 'section':override.section.section
338 session.execute( """INSERT INTO %s
339 (binary_id,package,version.component,arch,section,filename)
340 SELECT :binary_id, :package, :version, :component, :arch, :section
341 FROM pending_bin_contents pbc
342 WHERE pbc.package=:package
343 AND pbc.version=:version
344 AND pbc.arch=:arch""" % table, vals )
346 session.execute( """DELETE from pending_bin_contents package=:package
348 AND arch=:arch""", vals )
356 __all__.append('copy_temporary_contents')