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
57 def __init__(self, filename, reject=None):
59 @type filename: string
60 @param filename: path of a .deb
62 @type reject: function
63 @param reject: a function to log reject messages to
65 self.filename = filename
68 self.wrapped_reject = reject
70 def reject(self, message):
72 if we were given a reject function, send the reject message,
73 otherwise send it to stderr.
75 print >> sys.stderr, message
76 if self.wrapped_reject:
77 self.wrapped_reject(message)
81 make sure we cleanup when we are garbage collected.
87 we need to remove the temporary directory, if we created one
89 if self.tmpdir and os.path.exists(self.tmpdir):
90 shutil.rmtree(self.tmpdir)
94 # get a list of the ar contents
97 cmd = "ar t %s" % (self.filename)
98 (result, output) = commands.getstatusoutput(cmd)
101 print("%s: 'ar t' invocation failed." % (self.filename))
102 self.reject("%s: 'ar t' invocation failed." % (self.filename))
103 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
104 self.chunks = output.split('\n')
109 # Internal function which extracts the contents of the .ar to
110 # a temporary directory
113 tmpdir = utils.temp_dirname()
117 cmd = "ar x %s %s %s" % (os.path.join(cwd,self.filename), self.chunks[1], self.chunks[2])
118 (result, output) = commands.getstatusoutput(cmd)
120 print("%s: '%s' invocation failed." % (self.filename, cmd))
121 self.reject("%s: '%s' invocation failed." % (self.filename, cmd))
122 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
125 atexit.register( self._cleanup )
130 def valid_deb(self, relaxed=False):
132 Check deb contents making sure the .deb contains:
135 3. data.tar.gz or data.tar.bz2
136 in that order, and nothing else.
139 rejected = not self.chunks
141 if len(self.chunks) < 3:
143 self.reject("%s: found %d chunks, expected at least 3." % (self.filename, len(self.chunks)))
145 if len(self.chunks) != 3:
147 self.reject("%s: found %d chunks, expected 3." % (self.filename, len(self.chunks)))
148 if self.chunks[0] != "debian-binary":
150 self.reject("%s: first chunk is '%s', expected 'debian-binary'." % (self.filename, self.chunks[0]))
151 if not rejected and self.chunks[1] != "control.tar.gz":
153 self.reject("%s: second chunk is '%s', expected 'control.tar.gz'." % (self.filename, self.chunks[1]))
154 if not rejected and self.chunks[2] not in [ "data.tar.bz2", "data.tar.gz" ]:
156 self.reject("%s: third chunk is '%s', expected 'data.tar.gz' or 'data.tar.bz2'." % (self.filename, self.chunks[2]))
160 def scan_package(self, bootstrap_id=0, relaxed=False, session=None):
162 Unpack the .deb, do sanity checking, and gather info from it.
164 Currently information gathering consists of getting the contents list. In
165 the hopefully near future, it should also include gathering info from the
168 @type bootstrap_id: int
169 @param bootstrap_id: the id of the binary these packages
170 should be associated or zero meaning we are not bootstrapping
171 so insert into a temporary table
173 @return: True if the deb is valid and contents were imported
176 rejected = not self.valid_deb(relaxed)
182 if not rejected and self.tmpdir:
184 os.chdir(self.tmpdir)
185 if self.chunks[1] == "control.tar.gz":
186 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
187 control.extract('./control', self.tmpdir )
188 if self.chunks[2] == "data.tar.gz":
189 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
190 elif self.chunks[2] == "data.tar.bz2":
191 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
194 result = insert_content_paths(bootstrap_id, [tarinfo.name for tarinfo in data if not tarinfo.isdir()], session)
196 pkgs = deb822.Packages.iter_paragraphs(file(os.path.join(self.tmpdir,'control')))
198 result = insert_pending_content_paths(pkg, [tarinfo.name for tarinfo in data if not tarinfo.isdir()], session)
201 traceback.print_exc()
207 def check_utf8_package(self, package):
209 Unpack the .deb, do sanity checking, and gather info from it.
211 Currently information gathering consists of getting the contents list. In
212 the hopefully near future, it should also include gathering info from the
215 @type package: string
216 @param package: the name of the package to be checked
219 @return: True if the deb is valid and contents were imported
221 rejected = not self.valid_deb(True)
224 if not rejected and self.tmpdir:
227 os.chdir(self.tmpdir)
228 if self.chunks[1] == "control.tar.gz":
229 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
230 control.extract('control', self.tmpdir )
231 if self.chunks[2] == "data.tar.gz":
232 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
233 elif self.chunks[2] == "data.tar.bz2":
234 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
238 unicode( tarinfo.name )
240 print >> sys.stderr, "E: %s has non-unicode filename: %s" % (package,tarinfo.name)
243 traceback.print_exc()
248 __all__.append('Binary')
250 def copy_temporary_contents(package, version, archname, deb, reject, session=None):
252 copy the previously stored contents from the temp table to the permanant one
254 during process-unchecked, the deb should have been scanned and the
255 contents stored in pending_content_associations
258 # first see if contents exist:
262 session = DBConn().session()
264 arch = get_architecture(archname, session=session)
266 in_pcaq = """SELECT 1 FROM pending_content_associations
267 WHERE package=:package
269 AND architecture=:archid LIMIT 1"""
271 vals = {'package': package,
273 'archid': arch.arch_id}
276 check = session.execute(in_pcaq, vals)
278 if check.rowcount > 0:
279 # This should NOT happen. We should have added contents
280 # during process-unchecked. if it did, log an error, and send
283 "__PACKAGE__": package,
284 "__VERSION__": version,
286 "__TO_ADDRESS__": cnf["Dinstall::MyAdminAddress"],
287 "__DAK_ADDRESS__": cnf["Dinstall::MyEmailAddress"] }
289 message = utils.TemplateSubst(subst, cnf["Dir::Templates"]+"/missing-contents")
290 utils.send_mail(message)
292 exists = Binary(deb, reject).scan_package()
295 sql = """INSERT INTO content_associations(binary_pkg,filepath,filename)
296 SELECT currval('binaries_id_seq'), filepath, filename FROM pending_content_associations
297 WHERE package=:package AND version=:version AND architecture=:archid"""
298 session.execute(sql, vals)
300 sql = """DELETE from pending_content_associations
301 WHERE package=:package AND version=:version AND architecture=:archid"""
302 session.execute(sql, vals)
306 __all__.append('copy_temporary_contents')