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
76 def reject(self, message):
78 if we were given a reject function, send the reject message,
79 otherwise send it to stderr.
81 print >> sys.stderr, message
82 if self.wrapped_reject:
83 self.wrapped_reject(message)
87 make sure we cleanup when we are garbage collected.
93 we need to remove the temporary directory, if we created one
95 if self.tmpdir and os.path.exists(self.tmpdir):
96 shutil.rmtree(self.tmpdir)
100 # get a list of the ar contents
103 cmd = "ar t %s" % (self.filename)
104 (result, output) = commands.getstatusoutput(cmd)
107 print("%s: 'ar t' invocation failed." % (self.filename))
108 self.reject("%s: 'ar t' invocation failed." % (self.filename))
109 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
110 self.chunks = output.split('\n')
115 # Internal function which extracts the contents of the .ar to
116 # a temporary directory
119 tmpdir = utils.temp_dirname()
123 cmd = "ar x %s %s %s" % (os.path.join(cwd,self.filename), self.chunks[1], self.chunks[2])
124 (result, output) = commands.getstatusoutput(cmd)
126 print("%s: '%s' invocation failed." % (self.filename, cmd))
127 self.reject("%s: '%s' invocation failed." % (self.filename, cmd))
128 self.reject(utils.prefix_multi_line_string(output, " [ar output:] "))
131 atexit.register( self._cleanup )
136 def valid_deb(self, relaxed=False):
138 Check deb contents making sure the .deb contains:
141 3. data.tar.gz or data.tar.bz2
142 in that order, and nothing else.
145 rejected = not self.chunks
147 if len(self.chunks) < 3:
149 self.reject("%s: found %d chunks, expected at least 3." % (self.filename, len(self.chunks)))
151 if len(self.chunks) != 3:
153 self.reject("%s: found %d chunks, expected 3." % (self.filename, len(self.chunks)))
154 if self.chunks[0] != "debian-binary":
156 self.reject("%s: first chunk is '%s', expected 'debian-binary'." % (self.filename, self.chunks[0]))
157 if not rejected and self.chunks[1] != "control.tar.gz":
159 self.reject("%s: second chunk is '%s', expected 'control.tar.gz'." % (self.filename, self.chunks[1]))
160 if not rejected and self.chunks[2] not in [ "data.tar.bz2", "data.tar.gz" ]:
162 self.reject("%s: third chunk is '%s', expected 'data.tar.gz' or 'data.tar.bz2'." % (self.filename, self.chunks[2]))
166 def scan_package(self, bootstrap_id=0, relaxed=False, session=None):
168 Unpack the .deb, do sanity checking, and gather info from it.
170 Currently information gathering consists of getting the contents list. In
171 the hopefully near future, it should also include gathering info from the
174 @type bootstrap_id: int
175 @param bootstrap_id: the id of the binary these packages
176 should be associated or zero meaning we are not bootstrapping
177 so insert into a temporary table
179 @return: True if the deb is valid and contents were imported
182 rejected = not self.valid_deb(relaxed)
188 if not rejected and self.tmpdir:
190 os.chdir(self.tmpdir)
191 if self.chunks[1] == "control.tar.gz":
192 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
193 control.extract('./control', self.tmpdir )
194 if self.chunks[2] == "data.tar.gz":
195 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
196 elif self.chunks[2] == "data.tar.bz2":
197 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
200 result = insert_content_paths(bootstrap_id, [tarinfo.name for tarinfo in data if not tarinfo.isdir()], session)
202 pkgs = deb822.Packages.iter_paragraphs(file(os.path.join(self.tmpdir,'control')))
204 result = insert_pending_content_paths(pkg, [tarinfo.name for tarinfo in data if not tarinfo.isdir()], session)
207 traceback.print_exc()
213 def check_utf8_package(self, package):
215 Unpack the .deb, do sanity checking, and gather info from it.
217 Currently information gathering consists of getting the contents list. In
218 the hopefully near future, it should also include gathering info from the
221 @type package: string
222 @param package: the name of the package to be checked
225 @return: True if the deb is valid and contents were imported
227 rejected = not self.valid_deb(True)
230 if not rejected and self.tmpdir:
233 os.chdir(self.tmpdir)
234 if self.chunks[1] == "control.tar.gz":
235 control = tarfile.open(os.path.join(self.tmpdir, "control.tar.gz" ), "r:gz")
236 control.extract('control', self.tmpdir )
237 if self.chunks[2] == "data.tar.gz":
238 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.gz"), "r:gz")
239 elif self.chunks[2] == "data.tar.bz2":
240 data = tarfile.open(os.path.join(self.tmpdir, "data.tar.bz2" ), "r:bz2")
244 unicode( tarinfo.name )
246 print >> sys.stderr, "E: %s has non-unicode filename: %s" % (package,tarinfo.name)
249 traceback.print_exc()
254 __all__.append('Binary')
256 def copy_temporary_contents(package, version, archname, deb, reject, session=None):
258 copy the previously stored contents from the temp table to the permanant one
260 during process-unchecked, the deb should have been scanned and the
261 contents stored in pending_content_associations
264 # first see if contents exist:
268 session = DBConn().session()
270 arch = get_architecture(archname, session=session)
272 in_pcaq = """SELECT 1 FROM pending_content_associations
273 WHERE package=:package
275 AND architecture=:archid LIMIT 1"""
277 vals = {'package': package,
279 'archid': arch.arch_id}
282 check = session.execute(in_pcaq, vals)
284 if check.rowcount > 0:
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)
298 exists = Binary(deb, reject).scan_package()
301 sql = """INSERT INTO content_associations(binary_pkg,filepath,filename)
302 SELECT currval('binaries_id_seq'), filepath, filename FROM pending_content_associations
303 WHERE package=:package AND version=:version AND architecture=:archid"""
304 session.execute(sql, vals)
306 sql = """DELETE from pending_content_associations
307 WHERE package=:package AND version=:version AND architecture=:archid"""
308 session.execute(sql, vals)
313 __all__.append('copy_temporary_contents')