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, [tarinfo.name for tarinfo in data if not tarinfo.isdir()], session)
210 traceback.print_exc()
216 def check_utf8_package(self, package):
218 Unpack the .deb, do sanity checking, and gather info from it.
220 Currently information gathering consists of getting the contents list. In
221 the hopefully near future, it should also include gathering info from the
224 @type package: string
225 @param package: the name of the package to be checked
228 @return: True if the deb is valid and contents were imported
230 rejected = not self.valid_deb(True)
233 if not rejected and self.tmpdir:
236 os.chdir(self.tmpdir)
237 if self.chunks[1] == "control.tar.gz":
238 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
239 control.extract('control', self.tmpdir )
240 if self.chunks[2] == "data.tar.gz":
241 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
242 elif self.chunks[2] == "data.tar.bz2":
243 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
247 unicode( tarinfo.name )
249 print >> sys.stderr, "E: %s has non-unicode filename: %s" % (package,tarinfo.name)
252 traceback.print_exc()
257 __all__.append('Binary')
259 def copy_temporary_contents(package, version, archname, deb, reject, session=None):
261 copy the previously stored contents from the temp table to the permanant one
263 during process-unchecked, the deb should have been scanned and the
264 contents stored in pending_content_associations
271 session = DBConn().session()
274 arch = get_architecture(archname, session=session)
276 # first see if contents exist:
277 in_pcaq = """SELECT 1 FROM pending_content_associations
278 WHERE package=:package
280 AND architecture=:archid LIMIT 1"""
282 vals = {'package': package,
284 'archid': arch.arch_id}
287 check = session.execute(in_pcaq, vals)
289 if check.rowcount > 0:
290 # This should NOT happen. We should have added contents
291 # during process-unchecked. if it did, log an error, and send
294 "__PACKAGE__": package,
295 "__VERSION__": version,
297 "__TO_ADDRESS__": cnf["Dinstall::MyAdminAddress"],
298 "__DAK_ADDRESS__": cnf["Dinstall::MyEmailAddress"] }
300 message = utils.TemplateSubst(subst, cnf["Dir::Templates"]+"/missing-contents")
301 utils.send_mail(message)
303 exists = Binary(deb, reject).scan_package()
306 sql = """INSERT INTO content_associations(binary_pkg,filepath,filename)
307 SELECT currval('binaries_id_seq'), filepath, filename FROM pending_content_associations
308 WHERE package=:package AND version=:version AND architecture=:archid"""
309 session.execute(sql, vals)
311 sql = """DELETE from pending_content_associations
312 WHERE package=:package AND version=:version AND architecture=:archid"""
313 session.execute(sql, vals)
321 __all__.append('copy_temporary_contents')