3 # Installs Debian packaes
4 # Copyright (C) 2000 James Troup <james@nocrew.org>
5 # $Id: katie,v 1.21 2001-01-22 22:32:47 troup Exp $
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 # Based (almost entirely) on dinstall by Guy Maor <maor@debian.org>
23 #########################################################################################
25 # Cartman: "I'm trying to make the best of a bad situation, I don't
26 # need to hear crap from a bunch of hippy freaks living in
27 # denial. Screw you guys, I'm going home."
29 # Kyle: "But Cartman, we're trying to..."
31 # Cartman: "uhh.. screw you guys... home."
33 #########################################################################################
35 import FCNTL, commands, fcntl, getopt, gzip, os, pg, pwd, re, shutil, stat, string, sys, tempfile, time
36 import apt_inst, apt_pkg
37 import utils, db_access
39 ###############################################################################
41 re_isanum = re.compile (r'^\d+$');
42 re_isadeb = re.compile (r'.*\.u?deb$');
43 re_issource = re.compile (r'(.+)_(.+?)\.(orig\.tar\.gz|diff\.gz|tar\.gz|dsc)');
44 re_changes = re.compile (r'changes$');
45 re_default_answer = re.compile(r"\[(.*)\]");
46 re_fdnic = re.compile("\n\n");
47 re_bad_diff = re.compile("^[\-\+][\-\+][\-\+] /dev/null");
49 ###############################################################################
52 reject_footer = """If you don't understand why your files were rejected, or if the
53 override file requires editing, reply to this email.
55 Your rejected files are in incoming/REJECT/. (Some may also be in
56 incoming/ if your .changes file was unparsable.) If only some of the
57 files need to repaired, you may move any good files back to incoming/.
58 Please remove any bad files from incoming/REJECT/."""
60 new_ack_footer = """Your package contains new components which requires manual editing of
61 the override file. It is ok otherwise, so please be patient. New
62 packages are usually added to the override file about once a week.
64 You may have gotten the distribution wrong. You'll get warnings above
65 if files already exist in other distributions."""
67 installed_footer = """If the override file requires editing, file a bug on ftp.debian.org.
69 Thank you for your contribution to Debian GNU."""
71 #########################################################################################
87 legacy_source_untouchable = {};
89 #########################################################################################
91 def usage (exit_code):
92 print """Usage: dinstall [OPTION]... [CHANGES]...
93 -a, --automatic automatic run
94 -d, --debug=VALUE debug
95 -k, --ack-new acknowledge new packages
96 -m, --manual-reject=MSG manual reject with `msg'
97 -n, --no-action don't do anything
98 -p, --no-lock don't check lockfile !! for cron.daily only !!
99 -r, --no-version-check override version check
100 -u, --distribution=DIST override distribution to `dist'"""
103 def check_signature (filename):
104 global reject_message
106 (result, output) = commands.getstatusoutput("gpg --emulate-md-encode-bug --batch --no-options --no-default-keyring --always-trust --keyring=%s --keyring=%s < %s >/dev/null" % (Cnf["Dinstall::PGPKeyring"], Cnf["Dinstall::GPGKeyring"], filename))
108 reject_message = "Rejected: GPG signature check failed on `%s'.\n%s\n" % (os.path.basename(filename), output)
112 #####################################################################################################################
114 # See if a given package is in the override table
116 def in_override_p (package, component, suite, binary_type, file):
119 if binary_type == "": # must be source
124 # Override suite name; used for example with proposed-updates
125 if Cnf.Find("Suite::%s::OverrideSuite" % (suite)) != "":
126 suite = Cnf["Suite::%s::OverrideSuite" % (suite)];
128 # Avoid <undef> on unknown distributions
129 suite_id = db_access.get_suite_id(suite);
132 component_id = db_access.get_component_id(component);
133 type_id = db_access.get_override_type_id(type);
135 # FIXME: nasty non-US speficic hack
136 if string.lower(component[:7]) == "non-us/":
137 component = component[7:];
139 q = projectB.query("SELECT s.section, p.priority FROM override o, section s, priority p WHERE package = '%s' AND suite = %s AND component = %s AND type = %s AND o.section = s.id AND o.priority = p.id"
140 % (package, suite_id, component_id, type_id));
141 result = q.getresult();
142 # If checking for a source package fall back on the binary override type
143 if type == "dsc" and not result:
144 type_id = db_access.get_override_type_id("deb");
145 q = projectB.query("SELECT s.section, p.priority FROM override o, section s, priority p WHERE package = '%s' AND suite = %s AND component = %s AND type = %s AND o.section = s.id AND o.priority = p.id"
146 % (package, suite_id, component_id, type_id));
147 result = q.getresult();
149 # Remember the section and priority so we can check them later if appropriate
151 files[file]["override section"] = result[0][0];
152 files[file]["override priority"] = result[0][1];
156 #####################################################################################################################
158 def check_changes(filename):
159 global reject_message, changes, files
161 # Parse the .changes field into a dictionary [FIXME - need to trap errors, pass on to reject_message etc.]
163 changes = utils.parse_changes(filename)
164 except utils.cant_open_exc:
165 reject_message = "Rejected: can't read changes file '%s'.\n" % (filename)
167 except utils.changes_parse_error_exc, line:
168 reject_message = "Rejected: error parsing changes file '%s', can't grok: %s.\n" % (filename, line)
169 changes["maintainer822"] = Cnf["Dinstall::MyEmailAddress"];
172 # Parse the Files field from the .changes into another dictionary [FIXME need to trap errors as above]
174 files = utils.build_file_list(changes, "");
175 except utils.changes_parse_error_exc, line:
176 reject_message = "Rejected: error parsing changes file '%s', can't grok: %s.\n" % (filename, line);
178 # Check for mandatory fields
179 for i in ("source", "binary", "architecture", "version", "distribution","maintainer", "files"):
180 if not changes.has_key(i):
181 reject_message = "Rejected: Missing field `%s' in changes file." % (i)
182 return 0 # Avoid <undef> errors during later tests
184 # Fix the Maintainer: field to be RFC822 compatible
185 (changes["maintainer822"], changes["maintainername"], changes["maintaineremail"]) = utils.fix_maintainer (changes["maintainer"])
187 # Override the Distribution: field if appropriate
188 if Cnf["Dinstall::Options::Override-Distribution"] != "":
189 reject_message = reject_message + "Warning: Distribution was overriden from %s to %s.\n" % (changes["distribution"], Cnf["Dinstall::Options::Override-Distribution"])
190 changes["distribution"] = Cnf["Dinstall::Options::Override-Distribution"]
192 # Split multi-value fields into a lower-level dictionary
193 for i in ("architecture", "distribution", "binary", "closes"):
194 o = changes.get(i, "")
198 for j in string.split(o):
201 # Ensure all the values in Closes: are numbers
202 if changes.has_key("closes"):
203 for i in changes["closes"].keys():
204 if re_isanum.match (i) == None:
205 reject_message = reject_message + "Rejected: `%s' from Closes field isn't a number.\n" % (i)
207 # Map frozen to unstable if frozen doesn't exist
208 if changes["distribution"].has_key("frozen") and not Cnf.has_key("Suite::Frozen"):
209 del changes["distribution"]["frozen"]
210 changes["distribution"]["unstable"] = 1;
211 reject_message = reject_message + "Mapping frozen to unstable.\n"
213 # Map testing to unstable
214 if changes["distribution"].has_key("testing"):
215 del changes["distribution"]["testing"]
216 changes["distribution"]["unstable"] = 1;
217 reject_message = reject_message + "Mapping testing to unstable.\n"
219 # Ensure target distributions exist
220 for i in changes["distribution"].keys():
221 if not Cnf.has_key("Suite::%s" % (i)):
222 reject_message = reject_message + "Rejected: Unknown distribution `%s'.\n" % (i)
224 # Map unreleased arches from stable to unstable
225 if changes["distribution"].has_key("stable"):
226 for i in changes["architecture"].keys():
227 if not Cnf.has_key("Suite::Stable::Architectures::%s" % (i)):
228 reject_message = reject_message + "Mapping stable to unstable for unreleased arch `%s'.\n" % (i)
229 del changes["distribution"]["stable"]
230 changes["distribution"]["unstable"] = 1;
232 # Map arches not being released from frozen to unstable
233 if changes["distribution"].has_key("frozen"):
234 for i in changes["architecture"].keys():
235 if not Cnf.has_key("Suite::Frozen::Architectures::%s" % (i)):
236 reject_message = reject_message + "Mapping frozen to unstable for non-releasing arch `%s'.\n" % (i)
237 del changes["distribution"]["frozen"]
238 changes["distribution"]["unstable"] = 1;
240 # Handle uploads to stable
241 if changes["distribution"].has_key("stable"):
242 # If running from within proposed-updates; assume an install to stable
243 if string.find(os.getcwd(), 'proposed-updates') != -1:
244 # FIXME: should probably remove anything that != stable
245 for i in ("frozen", "unstable"):
246 if changes["distribution"].has_key(i):
247 reject_message = reject_message + "Removing %s from distribution list.\n"
248 del changes["distribution"][i]
249 changes["stable upload"] = 1;
250 # If we can't find a file from the .changes; assume it's a package already in the pool and move into the pool
251 file = files.keys()[0];
252 if os.access(file, os.R_OK) == 0:
253 pool_dir = Cnf["Dir::PoolDir"] + '/' + utils.poolify(changes["source"], files[file]["component"]);
255 # Otherwise (normal case) map stable to updates
257 reject_message = reject_message + "Mapping stable to updates.\n";
258 del changes["distribution"]["stable"];
259 changes["distribution"]["proposed-updates"] = 1;
261 # chopversion = no epoch; chopversion2 = no epoch and no revision (e.g. for .orig.tar.gz comparison)
262 changes["chopversion"] = utils.re_no_epoch.sub('', changes["version"])
263 changes["chopversion2"] = utils.re_no_revision.sub('', changes["chopversion"])
265 if string.find(reject_message, "Rejected:") != -1:
271 global reject_message
273 archive = utils.where_am_i();
275 for file in files.keys():
276 # Check the file is readable
277 if os.access(file,os.R_OK) == 0:
278 reject_message = reject_message + "Rejected: Can't read `%s'.\n" % (file)
279 files[file]["type"] = "unreadable";
281 # If it's byhand skip remaining checks
282 if files[file]["section"] == "byhand":
283 files[file]["byhand"] = 1;
284 files[file]["type"] = "byhand";
285 # Checks for a binary package...
286 elif re_isadeb.match(file) != None:
287 # Extract package information using dpkg-deb
289 control = apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(file,"r")))
291 reject_message = reject_message + "Rejected: %s: debExtractControl() raised %s.\n" % (file, sys.exc_type);
293 # Check for mandatory fields
294 if control.Find("Package") == None:
295 reject_message = reject_message + "Rejected: %s: No package field in control.\n" % (file)
296 if control.Find("Architecture") == None:
297 reject_message = reject_message + "Rejected: %s: No architecture field in control.\n" % (file)
298 if control.Find("Version") == None:
299 reject_message = reject_message + "Rejected: %s: No version field in control.\n" % (file)
301 # Ensure the package name matches the one give in the .changes
302 if not changes["binary"].has_key(control.Find("Package", "")):
303 reject_message = reject_message + "Rejected: %s: control file lists name as `%s', which isn't in changes file.\n" % (file, control.Find("Package", ""))
305 # Validate the architecture
306 if not Cnf.has_key("Suite::Unstable::Architectures::%s" % (control.Find("Architecture", ""))):
307 reject_message = reject_message + "Rejected: Unknown architecture '%s'.\n" % (control.Find("Architecture", ""))
309 # Check the architecture matches the one given in the .changes
310 if not changes["architecture"].has_key(control.Find("Architecture", "")):
311 reject_message = reject_message + "Rejected: %s: control file lists arch as `%s', which isn't in changes file.\n" % (file, control.Find("Architecture", ""))
312 # Check the section & priority match those given in the .changes (non-fatal)
313 if control.Find("Section") != None and files[file]["section"] != "" and files[file]["section"] != control.Find("Section"):
314 reject_message = reject_message + "Warning: %s control file lists section as `%s', but changes file has `%s'.\n" % (file, control.Find("Section", ""), files[file]["section"])
315 if control.Find("Priority") != None and files[file]["priority"] != "" and files[file]["priority"] != control.Find("Priority"):
316 reject_message = reject_message + "Warning: %s control file lists priority as `%s', but changes file has `%s'.\n" % (file, control.Find("Priority", ""), files[file]["priority"])
318 epochless_version = utils.re_no_epoch.sub('', control.Find("Version", ""))
320 files[file]["package"] = control.Find("Package");
321 files[file]["architecture"] = control.Find("Architecture");
322 files[file]["version"] = control.Find("Version");
323 files[file]["maintainer"] = control.Find("Maintainer", "");
324 if file[-5:] == ".udeb":
325 files[file]["dbtype"] = "udeb";
326 elif file[-4:] == ".deb":
327 files[file]["dbtype"] = "deb";
329 reject_message = reject_message + "Rejected: %s is neither a .deb or a .udeb.\n " % (file);
330 files[file]["type"] = "deb";
331 files[file]["fullname"] = "%s_%s_%s.deb" % (control.Find("Package", ""), epochless_version, control.Find("Architecture", ""))
332 files[file]["source"] = control.Find("Source", "");
333 if files[file]["source"] == "":
334 files[file]["source"] = files[file]["package"];
335 # Checks for a source package...
337 m = re_issource.match(file)
339 files[file]["package"] = m.group(1)
340 files[file]["version"] = m.group(2)
341 files[file]["type"] = m.group(3)
343 # Ensure the source package name matches the Source filed in the .changes
344 if changes["source"] != files[file]["package"]:
345 reject_message = reject_message + "Rejected: %s: changes file doesn't say %s for Source\n" % (file, files[file]["package"])
347 # Ensure the source version matches the version in the .changes file
348 if files[file]["type"] == "orig.tar.gz":
349 changes_version = changes["chopversion2"]
351 changes_version = changes["chopversion"]
352 if changes_version != files[file]["version"]:
353 reject_message = reject_message + "Rejected: %s: should be %s according to changes file.\n" % (file, changes_version)
355 # Ensure the .changes lists source in the Architecture field
356 if not changes["architecture"].has_key("source"):
357 reject_message = reject_message + "Rejected: %s: changes file doesn't list `source' in Architecture field.\n" % (file)
359 # Check the signature of a .dsc file
360 if files[file]["type"] == "dsc":
361 check_signature(file)
363 files[file]["fullname"] = file
365 # Not a binary or source package? Assume byhand...
367 files[file]["byhand"] = 1;
368 files[file]["type"] = "byhand";
370 files[file]["oldfiles"] = {}
371 for suite in changes["distribution"].keys():
373 if files[file].has_key("byhand"):
376 if Cnf.has_key("Suite:%s::Components" % (suite)) and not Cnf.has_key("Suite::%s::Components::%s" % (suite, files[file]["component"])):
377 reject_message = reject_message + "Rejected: unknown component `%s' for suite `%s'.\n" % (files[file]["component"], suite)
380 # See if the package is NEW
381 if not in_override_p(files[file]["package"], files[file]["component"], suite, files[file].get("dbtype",""), file):
382 files[file]["new"] = 1
384 # Find any old binary packages
385 if files[file]["type"] == "deb":
386 q = projectB.query("SELECT b.id, b.version, f.filename, l.path, c.name FROM binaries b, bin_associations ba, suite s, location l, component c, architecture a, files f WHERE b.package = '%s' AND s.suite_name = '%s' AND a.arch_string = '%s' AND ba.bin = b.id AND ba.suite = s.id AND b.architecture = a.id AND f.location = l.id AND l.component = c.id AND b.file = f.id"
387 % (files[file]["package"], suite, files[file]["architecture"]))
388 oldfiles = q.dictresult()
389 for oldfile in oldfiles:
390 files[file]["oldfiles"][suite] = oldfile
391 # Check versions [NB: per-suite only; no cross-suite checking done (yet)]
392 if apt_pkg.VersionCompare(files[file]["version"], oldfile["version"]) != 1:
393 if Cnf["Dinstall::Options::No-Version-Check"]:
394 reject_message = reject_message + "Overriden rejection"
396 reject_message = reject_message + "Rejected"
397 reject_message = reject_message + ": %s Old version `%s' >= new version `%s'.\n" % (file, oldfile["version"], files[file]["version"])
398 # Check for existing copies of the file
399 if not changes.has_key("stable upload"):
400 q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND a.arch_string = '%s' AND a.id = b.architecture" % (files[file]["package"], files[file]["version"], files[file]["architecture"]))
401 if q.getresult() != []:
402 reject_message = reject_message + "Rejected: can not overwrite existing copy of '%s' already in the archive.\n" % (file)
404 # Find any old .dsc files
405 elif files[file]["type"] == "dsc":
406 q = projectB.query("SELECT s.id, s.version, f.filename, l.path, c.name FROM source s, src_associations sa, suite su, location l, component c, files f WHERE s.source = '%s' AND su.suite_name = '%s' AND sa.source = s.id AND sa.suite = su.id AND f.location = l.id AND l.component = c.id AND f.id = s.file"
407 % (files[file]["package"], suite))
408 oldfiles = q.dictresult()
409 if len(oldfiles) >= 1:
410 files[file]["oldfiles"][suite] = oldfiles[0]
412 # Validate the component
413 component = files[file]["component"];
414 component_id = db_access.get_component_id(component);
415 if component_id == -1:
416 reject_message = reject_message + "Rejected: file '%s' has unknown component '%s'.\n" % (file, component);
419 # Check the md5sum & size against existing files (if any)
420 location = Cnf["Dir::PoolDir"];
421 files[file]["location id"] = db_access.get_location_id (location, component, archive);
423 files[file]["pool name"] = utils.poolify (changes["source"], files[file]["component"]);
424 files_id = db_access.get_files_id(files[file]["pool name"] + file, files[file]["size"], files[file]["md5sum"], files[file]["location id"]);
426 reject_message = reject_message + "Rejected: INTERNAL ERROR, get_files_id() returned multiple matches for %s.\n" % (file)
428 reject_message = reject_message + "Rejected: md5sum and/or size mismatch on existing copy of %s.\n" % (file)
429 files[file]["files id"] = files_id
431 # Check for packages that have moved from one component to another
432 if files[file]["oldfiles"].has_key(suite) and files[file]["oldfiles"][suite]["name"] != files[file]["component"]:
433 files[file]["othercomponents"] = files[file]["oldfiles"][suite]["name"];
436 if string.find(reject_message, "Rejected:") != -1:
441 ###############################################################################
444 global dsc, dsc_files, reject_message, reprocess, orig_tar_id, legacy_source_untouchable;
446 for file in files.keys():
447 if files[file]["type"] == "dsc":
449 dsc = utils.parse_changes(file)
450 except utils.cant_open_exc:
451 reject_message = reject_message + "Rejected: can't read changes file '%s'.\n" % (filename)
453 except utils.changes_parse_error_exc, line:
454 reject_message = reject_message + "Rejected: error parsing changes file '%s', can't grok: %s.\n" % (filename, line)
457 dsc_files = utils.build_file_list(dsc, 1)
458 except utils.no_files_exc:
459 reject_message = reject_message + "Rejected: no Files: field in .dsc file.\n";
461 except utils.changes_parse_error_exc, line:
462 reject_message = "Rejected: error parsing .dsc file '%s', can't grok: %s.\n" % (filename, line);
465 # Try and find all files mentioned in the .dsc. This has
466 # to work harder to cope with the multiple possible
467 # locations of an .orig.tar.gz.
468 for dsc_file in dsc_files.keys():
469 if files.has_key(dsc_file):
470 actual_md5 = files[dsc_file]["md5sum"];
471 actual_size = int(files[dsc_file]["size"]);
472 found = "%s in incoming" % (dsc_file)
473 # Check the file does not already exist in the archive
474 if not changes.has_key("stable upload"):
475 q = projectB.query("SELECT f.id FROM files f, location l WHERE (f.filename ~ '/%s$' OR f.filename = '%s') AND l.id = f.location" % (utils.regex_safe(dsc_file), dsc_file));
477 # "It has not broken them. It has fixed a
478 # brokenness. Your crappy hack exploited a
479 # bug in the old dinstall.
481 # "(Come on! I thought it was always obvious
482 # that one just doesn't release different
483 # files with the same name and version.)"
484 # -- ajk@ on d-devel@l.d.o
486 if q.getresult() != []:
487 reject_message = reject_message + "Rejected: can not overwrite existing copy of '%s' already in the archive.\n" % (dsc_file)
488 elif dsc_file[-12:] == ".orig.tar.gz":
490 q = projectB.query("SELECT l.path, f.filename, l.type, f.id FROM files f, location l WHERE (f.filename ~ '/%s$' OR f.filename = '%s') AND l.id = f.location" % (utils.regex_safe(dsc_file), dsc_file));
495 # Unfortunately, we make get more than one match
496 # here if, for example, the package was in potato
497 # but had a -sa upload in woody. So we need to a)
498 # choose the right one and b) mark all wrong ones
499 # as excluded from the source poolification (to
500 # avoid file overwrites).
502 x = ql[0]; # default to something sane in case we don't match any or have only one
506 old_file = i[0] + i[1];
507 actual_md5 = apt_pkg.md5sum(utils.open_file(old_file,"r"));
508 actual_size = os.stat(old_file)[stat.ST_SIZE];
509 if actual_md5 == dsc_files[dsc_file]["md5sum"] and actual_size == int(dsc_files[dsc_file]["size"]):
512 legacy_source_untouchable[i[3]] = "";
514 old_file = x[0] + x[1];
515 actual_md5 = apt_pkg.md5sum(utils.open_file(old_file,"r"));
516 actual_size = os.stat(old_file)[stat.ST_SIZE];
519 dsc_files[dsc_file]["files id"] = x[3]; # need this for updating dsc_files in install()
521 if suite_type == "legacy" or suite_type == "legacy-mixed":
524 # Not there? Check in Incoming...
525 # [See comment above process_it() for explanation
526 # of why this is necessary...]
527 if os.access(dsc_file, os.R_OK) != 0:
528 files[dsc_file] = {};
529 files[dsc_file]["size"] = os.stat(dsc_file)[stat.ST_SIZE];
530 files[dsc_file]["md5sum"] = dsc_files[dsc_file]["md5sum"];
531 files[dsc_file]["section"] = files[file]["section"];
532 files[dsc_file]["priority"] = files[file]["priority"];
533 files[dsc_file]["component"] = files[file]["component"];
534 files[dsc_file]["type"] = "orig.tar.gz";
538 reject_message = reject_message + "Rejected: %s refers to %s, but I can't find it in Incoming or in the pool.\n" % (file, dsc_file);
541 reject_message = reject_message + "Rejected: %s refers to %s, but I can't find it in Incoming." % (file, dsc_file);
543 if actual_md5 != dsc_files[dsc_file]["md5sum"]:
544 reject_message = reject_message + "Rejected: md5sum for %s doesn't match %s.\n" % (found, file);
545 if actual_size != int(dsc_files[dsc_file]["size"]):
546 reject_message = reject_message + "Rejected: size for %s doesn't match %s.\n" % (found, file);
548 if string.find(reject_message, "Rejected:") != -1:
553 ###############################################################################
555 # Some cunning stunt broke dpkg-source in dpkg 1.8{,.1}; detect the
556 # resulting bad source packages and reject them.
558 # Even more amusingly the fix in 1.8.1.1 didn't actually fix the
559 # problem just changed the symptoms.
562 global dsc, dsc_files, reject_message, reprocess, orig_tar_id;
564 for filename in files.keys():
565 if files[filename]["type"] == "diff.gz":
566 file = gzip.GzipFile(filename, 'r');
567 for line in file.readlines():
568 if re_bad_diff.search(line):
569 reject_message = reject_message + "Rejected: [dpkg-sucks] source package was produced by a broken version of dpkg-dev 1.8.x; please rebuild with >= 1.8.3 version installed.\n";
572 if string.find(reject_message, "Rejected:") != -1:
577 ###############################################################################
579 def check_md5sums ():
580 global reject_message;
582 for file in files.keys():
584 file_handle = utils.open_file(file,"r");
585 except utils.cant_open_exc:
588 if apt_pkg.md5sum(file_handle) != files[file]["md5sum"]:
589 reject_message = reject_message + "Rejected: md5sum check failed for %s.\n" % (file);
591 def check_override ():
592 # Only check section & priority on sourceful uploads
593 if not changes["architecture"].has_key("source"):
597 for file in files.keys():
598 if not files[file].has_key("new") and (files[file]["type"] == "dsc" or files[file]["type"] == "deb"):
599 section = files[file]["section"];
600 override_section = files[file]["override section"];
601 if section != override_section and section != "-":
602 summary = summary + "%s: section is overridden from %s to %s.\n" % (file, section, override_section);
603 if files[file]["type"] == "deb": # don't do priority for source
604 priority = files[file]["priority"];
605 override_priority = files[file]["override priority"];
606 if priority != override_priority and priority != "-":
607 summary = summary + "%s: priority is overridden from %s to %s.\n" % (file, priority, override_priority);
612 mail_message = """Return-Path: %s
615 Bcc: troup@auric.debian.org
616 Subject: %s override disparity
618 There are disparities between your recently installed upload and the
619 override file for the following file(s):
622 Either the package or the override file is incorrect. If you think
623 the override is correct and the package wrong please fix the package
624 so that this disparity is fixed in the next upload. If you feel the
625 override is incorrect then please reply to this mail and explain why.
628 Debian distribution maintenance software
630 (This message was generated automatically; if you believe that there
631 is a problem with it please contact the archive administrators by
632 mailing ftpmaster@debian.org)
633 """ % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], changes["source"], summary);
634 utils.send_mail (mail_message, "")
636 #####################################################################################################################
638 def action (changes_filename):
639 byhand = confirm = suites = summary = new = "";
641 # changes["distribution"] may not exist in corner cases
642 # (e.g. unreadable changes files)
643 if not changes.has_key("distribution"):
644 changes["distribution"] = {};
646 for suite in changes["distribution"].keys():
647 if Cnf.has_key("Suite::%s::Confirm"):
648 confirm = confirm + suite + ", "
649 suites = suites + suite + ", "
650 confirm = confirm[:-2]
653 for file in files.keys():
654 if files[file].has_key("byhand"):
656 summary = summary + file + " byhand\n"
657 elif files[file].has_key("new"):
659 summary = summary + "(new) %s %s %s\n" % (file, files[file]["priority"], files[file]["section"])
660 if files[file].has_key("othercomponents"):
661 summary = summary + "WARNING: Already present in %s distribution.\n" % (files[file]["othercomponents"])
662 if files[file]["type"] == "deb":
663 summary = summary + apt_pkg.ParseSection(apt_inst.debExtractControl(utils.open_file(file,"r")))["Description"] + '\n';
665 files[file]["pool name"] = utils.poolify (changes["source"], files[file]["component"])
666 destination = Cnf["Dir::PoolRoot"] + files[file]["pool name"] + file
667 summary = summary + file + "\n to " + destination + "\n"
669 short_summary = summary;
671 # This is for direport's benefit...
672 f = re_fdnic.sub("\n .\n", changes.get("changes",""));
674 if confirm or byhand or new:
675 summary = summary + "Changes: " + f;
677 summary = summary + announce (short_summary, 0)
679 (prompt, answer) = ("", "XXX")
680 if Cnf["Dinstall::Options::No-Action"] or Cnf["Dinstall::Options::Automatic"]:
683 if string.find(reject_message, "Rejected") != -1:
684 if time.time()-os.path.getmtime(changes_filename) < 86400:
685 print "SKIP (too new)\n" + reject_message,;
686 prompt = "[S]kip, Manual reject, Quit ?";
688 print "REJECT\n" + reject_message,;
689 prompt = "[R]eject, Manual reject, Skip, Quit ?";
690 if Cnf["Dinstall::Options::Automatic"]:
693 print "NEW to %s\n%s%s" % (suites, reject_message, summary),;
694 prompt = "[S]kip, New ack, Manual reject, Quit ?";
695 if Cnf["Dinstall::Options::Automatic"] and Cnf["Dinstall::Options::Ack-New"]:
698 print "BYHAND\n" + reject_message + summary,;
699 prompt = "[I]nstall, Manual reject, Skip, Quit ?";
701 print "CONFIRM to %s\n%s%s" % (confirm, reject_message, summary),
702 prompt = "[I]nstall, Manual reject, Skip, Quit ?";
704 print "INSTALL\n" + reject_message + summary,;
705 prompt = "[I]nstall, Manual reject, Skip, Quit ?";
706 if Cnf["Dinstall::Options::Automatic"]:
709 while string.find(prompt, answer) == -1:
711 answer = utils.our_raw_input()
712 m = re_default_answer.match(prompt)
715 answer = string.upper(answer[:1])
718 reject (changes_filename, "");
720 manual_reject (changes_filename);
722 install (changes_filename, summary, short_summary);
724 acknowledge_new (changes_filename, summary);
728 #####################################################################################################################
730 def install (changes_filename, summary, short_summary):
731 global install_count, install_bytes
733 # Stable uploads are a special case
734 if changes.has_key("stable upload"):
735 stable_install (changes_filename, summary, short_summary);
740 archive = utils.where_am_i();
742 # Begin a transaction; if we bomb out anywhere between here and the COMMIT WORK below, the DB will not be changed.
743 projectB.query("BEGIN WORK");
745 # Add the .dsc file to the DB
746 for file in files.keys():
747 if files[file]["type"] == "dsc":
748 package = dsc["source"]
749 version = dsc["version"] # NB: not files[file]["version"], that has no epoch
750 maintainer = dsc["maintainer"]
751 maintainer = string.replace(maintainer, "'", "\\'")
752 maintainer_id = db_access.get_or_set_maintainer_id(maintainer);
753 filename = files[file]["pool name"] + file;
754 dsc_location_id = files[file]["location id"];
755 if not files[file]["files id"]:
756 files[file]["files id"] = db_access.set_files_id (filename, files[file]["size"], files[file]["md5sum"], dsc_location_id)
757 projectB.query("INSERT INTO source (source, version, maintainer, file) VALUES ('%s', '%s', %d, %d)"
758 % (package, version, maintainer_id, files[file]["files id"]))
760 for suite in changes["distribution"].keys():
761 suite_id = db_access.get_suite_id(suite);
762 projectB.query("INSERT INTO src_associations (suite, source) VALUES (%d, currval('source_id_seq'))" % (suite_id))
764 # Add the source files to the DB (files and dsc_files)
765 projectB.query("INSERT INTO dsc_files (source, file) VALUES (currval('source_id_seq'), %d)" % (files[file]["files id"]));
766 for dsc_file in dsc_files.keys():
767 filename = files[file]["pool name"] + dsc_file;
768 # If the .orig.tar.gz is already in the pool, it's
769 # files id is stored in dsc_files by check_dsc().
770 files_id = dsc_files[dsc_file].get("files id", None);
772 files_id = db_access.get_files_id(filename, dsc_files[dsc_file]["size"], dsc_files[dsc_file]["md5sum"], dsc_location_id);
773 # FIXME: needs to check for -1/-2 and or handle exception
775 files_id = db_access.set_files_id (filename, dsc_files[dsc_file]["size"], dsc_files[dsc_file]["md5sum"], dsc_location_id);
776 projectB.query("INSERT INTO dsc_files (source, file) VALUES (currval('source_id_seq'), %d)" % (files_id));
778 # Add the .deb files to the DB
779 for file in files.keys():
780 if files[file]["type"] == "deb":
781 package = files[file]["package"]
782 version = files[file]["version"]
783 maintainer = files[file]["maintainer"]
784 maintainer = string.replace(maintainer, "'", "\\'")
785 maintainer_id = db_access.get_or_set_maintainer_id(maintainer);
786 architecture = files[file]["architecture"]
787 architecture_id = db_access.get_architecture_id (architecture);
788 type = files[file]["dbtype"];
789 component = files[file]["component"]
790 source = files[file]["source"]
792 if string.find(source, "(") != -1:
793 m = utils.re_extract_src_version.match(source)
795 source_version = m.group(2)
796 if not source_version:
797 source_version = version
798 filename = files[file]["pool name"] + file;
799 if not files[file]["files id"]:
800 files[file]["files id"] = db_access.set_files_id (filename, files[file]["size"], files[file]["md5sum"], files[file]["location id"])
801 source_id = db_access.get_source_id (source, source_version);
803 projectB.query("INSERT INTO binaries (package, version, maintainer, source, architecture, file, type) VALUES ('%s', '%s', %d, %d, %d, %d, '%s')"
804 % (package, version, maintainer_id, source_id, architecture_id, files[file]["files id"], type));
806 projectB.query("INSERT INTO binaries (package, version, maintainer, architecture, file, type) VALUES ('%s', '%s', %d, %d, %d, '%s')"
807 % (package, version, maintainer_id, architecture_id, files[file]["files id"], type));
808 for suite in changes["distribution"].keys():
809 suite_id = db_access.get_suite_id(suite);
810 projectB.query("INSERT INTO bin_associations (suite, bin) VALUES (%d, currval('binaries_id_seq'))" % (suite_id));
812 # If the .orig.tar.gz is in a legacy directory we need to poolify
813 # it, so that apt-get source (and anything else that goes by the
814 # "Directory:" field in the Sources.gz file) works.
815 if orig_tar_id != None:
816 q = projectB.query("SELECT DISTINCT ON (f.id) l.path, f.filename, f.id as files_id, df.source, df.id as dsc_files_id, f.size, f.md5sum FROM files f, dsc_files df, location l WHERE df.source IN (SELECT source FROM dsc_files WHERE file = %s) AND f.id = df.file AND l.id = f.location AND (l.type = 'legacy' OR l.type = 'legacy-mixed')" % (orig_tar_id));
819 # Is this an old upload superseded by a newer -sa upload? (See check_dsc() for details)
820 if legacy_source_untouchable.has_key(qid["files_id"]):
822 # First move the files to the new location
823 legacy_filename = qid["path"]+qid["filename"];
824 pool_location = utils.poolify (changes["source"], files[file]["component"]);
825 pool_filename = pool_location + os.path.basename(qid["filename"]);
826 destination = Cnf["Dir::PoolDir"] + pool_location
827 utils.move(legacy_filename, destination);
828 # Then Update the DB's files table
829 q = projectB.query("UPDATE files SET filename = '%s', location = '%s' WHERE id = '%s'" % (pool_filename, dsc_location_id, qid["files_id"]));
831 # Install the files into the pool
832 for file in files.keys():
833 if files[file].has_key("byhand"):
835 destination = Cnf["Dir::PoolDir"] + files[file]["pool name"] + file
836 destdir = os.path.dirname(destination)
837 utils.move (file, destination)
838 install_bytes = install_bytes + float(files[file]["size"])
840 # Copy the .changes file across for suite which need it.
841 for suite in changes["distribution"].keys():
842 if Cnf.has_key("Suite::%s::CopyChanges" % (suite)):
843 utils.copy (changes_filename, Cnf["Dir::RootDir"] + Cnf["Suite::%s::CopyChanges" % (suite)]);
845 projectB.query("COMMIT WORK");
847 utils.move (changes_filename, Cnf["Dir::IncomingDir"] + 'DONE/' + os.path.basename(changes_filename))
849 install_count = install_count + 1;
851 if not Cnf["Dinstall::Options::No-Mail"]:
852 mail_message = """Return-Path: %s
855 Bcc: troup@auric.debian.org
856 Subject: %s INSTALLED
862 %s""" % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), reject_message, summary, installed_footer)
863 utils.send_mail (mail_message, "")
864 announce (short_summary, 1)
867 #####################################################################################################################
869 def stable_install (changes_filename, summary, short_summary):
870 global install_count, install_bytes
872 print "Installing to stable."
874 archive = utils.where_am_i();
876 # Begin a transaction; if we bomb out anywhere between here and the COMMIT WORK below, the DB will not be changed.
877 projectB.query("BEGIN WORK");
879 # Add the .dsc file to the DB
880 for file in files.keys():
881 if files[file]["type"] == "dsc":
882 package = dsc["source"]
883 version = dsc["version"] # NB: not files[file]["version"], that has no epoch
884 q = projectB.query("SELECT id FROM source WHERE source = '%s' AND version = '%s'" % (package, version))
887 sys.stderr.write("INTERNAL ERROR: couldn't find '%s' (%s) in source table.\n" % (package, version));
889 source_id = ql[0][0];
890 suite_id = db_access.get_suite_id('proposed-updates');
891 projectB.query("DELETE FROM src_associations WHERE suite = '%s' AND source = '%s'" % (suite_id, source_id));
892 suite_id = db_access.get_suite_id('stable');
893 projectB.query("INSERT INTO src_associations (suite, source) VALUES ('%s', '%s')" % (suite_id, source_id));
895 # Add the .deb files to the DB
896 for file in files.keys():
897 if files[file]["type"] == "deb":
898 package = files[file]["package"]
899 version = files[file]["version"]
900 architecture = files[file]["architecture"]
901 q = projectB.query("SELECT b.id FROM binaries b, architecture a WHERE b.package = '%s' AND b.version = '%s' AND (a.arch_string = '%s' OR a.arch_string = 'all') AND b.architecture = a.id" % (package, version, architecture))
904 sys.stderr.write("INTERNAL ERROR: couldn't find '%s' (%s for %s architecture) in binaries table.\n" % (package, version, architecture));
906 binary_id = ql[0][0];
907 suite_id = db_access.get_suite_id('proposed-updates');
908 projectB.query("DELETE FROM bin_associations WHERE suite = '%s' AND bin = '%s'" % (suite_id, binary_id));
909 suite_id = db_access.get_suite_id('stable');
910 projectB.query("INSERT INTO bin_associations (suite, bin) VALUES ('%s', '%s')" % (suite_id, binary_id));
912 projectB.query("COMMIT WORK");
914 utils.move (changes_filename, Cnf["Rhona::Morgue"] + os.path.basename(changes_filename));
916 # Update the Stable ChangeLog file
918 new_changelog_filename = Cnf["Dir::RootDir"] + Cnf["Suite::Stable::ChangeLogBase"] + ".ChangeLog";
919 changelog_filename = Cnf["Dir::RootDir"] + Cnf["Suite::Stable::ChangeLogBase"] + "ChangeLog";
920 if os.path.exists(new_changelog_filename):
921 os.unlink (new_changelog_filename);
923 new_changelog = utils.open_file(new_changelog_filename, 'w');
924 for file in files.keys():
925 if files[file]["type"] == "deb":
926 new_changelog.write("stable/%s/binary-%s/%s\n" % (files[file]["component"], files[file]["architecture"], file));
927 elif re_issource.match(file) != None:
928 new_changelog.write("stable/%s/source/%s\n" % (files[file]["component"], file));
930 new_changelog.write("%s\n" % (file));
931 chop_changes = re_fdnic.sub("\n", changes["changes"]);
932 new_changelog.write(chop_changes + '\n\n');
933 if os.access(changelog_filename, os.R_OK) != 0:
934 changelog = utils.open_file(changelog_filename, 'r');
935 new_changelog.write(changelog.read());
936 new_changelog.close();
937 if os.access(changelog_filename, os.R_OK) != 0:
938 os.unlink(changelog_filename);
939 utils.move(new_changelog_filename, changelog_filename);
941 install_count = install_count + 1;
943 if not Cnf["Dinstall::Options::No-Mail"]:
944 mail_message = """Return-Path: %s
947 Bcc: troup@auric.debian.org
948 Subject: %s INSTALLED into stable
954 %s""" % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), reject_message, summary, installed_footer)
955 utils.send_mail (mail_message, "")
956 announce (short_summary, 1)
958 #####################################################################################################################
960 def reject (changes_filename, manual_reject_mail_filename):
963 base_changes_filename = os.path.basename(changes_filename);
964 reason_filename = re_changes.sub("reason", base_changes_filename);
965 reject_filename = "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], reason_filename);
967 # Move the .changes files and it's contents into REJECT/ (if we can; errors are ignored)
969 utils.move (changes_filename, "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], base_changes_filename));
970 except utils.cant_overwrite_exc:
971 sys.stderr.write("W: couldn't overwrite existing file '%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], base_changes_filename));
973 for file in files.keys():
974 if os.path.exists(file):
976 utils.move (file, "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], file));
977 except utils.cant_overwrite_exc:
978 sys.stderr.write("W: couldn't overwrite existing file '%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], file));
981 # If this is not a manual rejection generate the .reason file and rejection mail message
982 if manual_reject_mail_filename == "":
983 if os.path.exists(reject_filename):
984 os.unlink(reject_filename);
985 fd = os.open(reject_filename, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
986 os.write(fd, reject_message);
988 reject_mail_message = """From: %s
990 Bcc: troup@auric.debian.org
995 %s""" % (Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), reject_message, reject_footer);
996 else: # Have a manual rejection file to use
997 reject_mail_message = ""; # avoid <undef>'s
999 # Send the rejection mail if appropriate
1000 if not Cnf["Dinstall::Options::No-Mail"]:
1001 utils.send_mail (reject_mail_message, manual_reject_mail_filename);
1003 ##################################################################
1005 def manual_reject (changes_filename):
1006 # Build up the rejection email
1007 user_email_address = string.replace(string.split(pwd.getpwuid(os.getuid())[4],',')[0], '.', '')
1008 user_email_address = user_email_address + " <%s@%s>" % (pwd.getpwuid(os.getuid())[0], Cnf["Dinstall::MyHost"])
1009 manual_reject_message = Cnf.get("Dinstall::Options::Manual-Reject", "")
1011 reject_mail_message = """From: %s
1014 Bcc: troup@auric.debian.org
1015 Subject: %s REJECTED
1020 %s""" % (user_email_address, Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], os.path.basename(changes_filename), manual_reject_message, reject_message, reject_footer)
1022 # Write the rejection email out as the <foo>.reason file
1023 reason_filename = re_changes.sub("reason", os.path.basename(changes_filename));
1024 reject_filename = "%s/REJECT/%s" % (Cnf["Dir::IncomingDir"], reason_filename)
1025 if os.path.exists(reject_filename):
1026 os.unlink(reject_filename);
1027 fd = os.open(reject_filename, os.O_RDWR|os.O_CREAT|os.O_EXCL, 0644);
1028 os.write(fd, reject_mail_message);
1031 # If we weren't given one, spawn an editor so the user can add one in
1032 if manual_reject_message == "":
1033 result = os.system("vi +6 %s" % (reject_file))
1035 sys.stderr.write ("vi invocation failed for `%s'!" % (reject_file))
1038 # Then process it as if it were an automatic rejection
1039 reject (changes_filename, reject_filename)
1041 #####################################################################################################################
1043 def acknowledge_new (changes_filename, summary):
1046 changes_filename = os.path.basename(changes_filename);
1048 new_ack_new[changes_filename] = 1;
1050 if new_ack_old.has_key(changes_filename):
1051 print "Ack already sent.";
1054 print "Sending new ack.";
1055 if not Cnf["Dinstall::Options::No-Mail"]:
1056 new_ack_message = """Return-Path: %s
1059 Bcc: troup@auric.debian.org
1063 %s""" % (Cnf["Dinstall::MyEmailAddress"], Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], changes_filename, summary, new_ack_footer);
1064 utils.send_mail(new_ack_message,"");
1066 #####################################################################################################################
1068 def announce (short_summary, action):
1069 # Only do announcements for source uploads with a recent dpkg-dev installed
1070 if float(changes.get("format", 0)) < 1.6 or not changes["architecture"].has_key("source"):
1076 for dist in changes["distribution"].keys():
1077 list = Cnf.Find("Suite::%s::Announce" % (dist))
1078 if list == None or lists_done.has_key(list):
1080 lists_done[list] = 1
1081 summary = summary + "Announcing to %s\n" % (list)
1084 mail_message = """Return-Path: %s
1087 Bcc: troup@auric.debian.org
1088 Subject: Installed %s %s (%s)
1094 """ % (Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], list, changes["source"], changes["version"], string.join(changes["architecture"].keys(), ' ' ),
1095 changes["filecontents"], short_summary)
1096 utils.send_mail (mail_message, "")
1098 (dsc_rfc822, dsc_name, dsc_email) = utils.fix_maintainer (dsc.get("maintainer",Cnf["Dinstall::MyEmailAddress"]));
1099 bugs = changes["closes"].keys()
1101 if dsc_name == changes["maintainername"]:
1102 summary = summary + "Closing bugs: "
1104 summary = summary + "%s " % (bug)
1106 mail_message = """Return-Path: %s
1108 To: %s-close@bugs.debian.org
1109 Bcc: troup@auric.debian.org
1110 Subject: Bug#%s: fixed in %s %s
1112 We believe that the bug you reported is fixed in the latest version of
1113 %s, which has been installed in the Debian FTP archive:
1115 %s""" % (Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], bug, bug, changes["source"], changes["version"], changes["source"], short_summary)
1117 if changes["distribution"].has_key("stable"):
1118 mail_message = mail_message + """Note that this package is not part of the released stable Debian
1119 distribution. It may have dependencies on other unreleased software,
1120 or other instabilities. Please take care if you wish to install it.
1121 The update will eventually make its way into the next released Debian
1124 mail_message = mail_message + """A summary of the changes between this version and the previous one is
1127 Thank you for reporting the bug, which will now be closed. If you
1128 have further comments please address them to %s@bugs.debian.org,
1129 and the maintainer will reopen the bug report if appropriate.
1131 Debian distribution maintenance software
1133 %s (supplier of updated %s package)
1135 (This message was generated automatically at their request; if you
1136 believe that there is a problem with it please contact the archive
1137 administrators by mailing ftpmaster@debian.org)
1140 %s""" % (bug, changes["maintainer"], changes["source"], changes["filecontents"])
1142 utils.send_mail (mail_message, "")
1144 summary = summary + "Setting bugs to severity fixed: "
1145 control_message = ""
1147 summary = summary + "%s " % (bug)
1148 control_message = control_message + "severity %s fixed\n" % (bug)
1149 if action and control_message != "":
1150 mail_message = """Return-Path: %s
1152 To: control@bugs.debian.org
1153 Bcc: troup@auric.debian.org, %s
1154 Subject: Fixed in NMU of %s %s
1159 This message was generated automatically in response to a
1160 non-maintainer upload. The .changes file follows.
1163 """ % (Cnf["Dinstall::MyEmailAddress"], changes["maintainer822"], changes["maintainer822"], changes["source"], changes["version"], control_message, changes["filecontents"])
1164 utils.send_mail (mail_message, "")
1165 summary = summary + "\n"
1169 ###############################################################################
1171 # reprocess is necessary for the case of foo_1.2-1 and foo_1.2-2 in
1172 # Incoming. -1 will reference the .orig.tar.gz, but -2 will not.
1173 # dsccheckdistrib() can find the .orig.tar.gz but it will not have
1174 # processed it during it's checks of -2. If -1 has been deleted or
1175 # otherwise not checked by da-install, the .orig.tar.gz will not have
1176 # been checked at all. To get round this, we force the .orig.tar.gz
1177 # into the .changes structure and reprocess the .changes file.
1179 def process_it (changes_file):
1180 global reprocess, orig_tar_id, changes, dsc, dsc_files, files;
1184 # Reset some globals
1190 legacy_source_untouchable = {};
1192 # Absolutize the filename to avoid the requirement of being in the
1193 # same directory as the .changes file.
1194 changes_file = os.path.abspath(changes_file);
1196 # And since handling of installs to stable munges with the CWD;
1197 # save and restore it.
1200 check_signature (changes_file);
1201 check_changes (changes_file);
1209 action(changes_file);
1214 ###############################################################################
1217 global Cnf, projectB, reject_message, install_bytes, new_ack_old
1221 Cnf = apt_pkg.newConfiguration();
1222 apt_pkg.ReadConfigFileISC(Cnf,utils.which_conf_file());
1224 Arguments = [('a',"automatic","Dinstall::Options::Automatic"),
1225 ('d',"debug","Dinstall::Options::Debug", "IntVal"),
1226 ('h',"help","Dinstall::Options::Help"),
1227 ('k',"ack-new","Dinstall::Options::Ack-New"),
1228 ('m',"manual-reject","Dinstall::Options::Manual-Reject", "HasArg"),
1229 ('n',"no-action","Dinstall::Options::No-Action"),
1230 ('p',"no-lock", "Dinstall::Options::No-Lock"),
1231 ('r',"no-version-check", "Dinstall::Options::No-Version-Check"),
1232 ('s',"no-mail", "Dinstall::Options::No-Mail"),
1233 ('u',"override-distribution", "Dinstall::Options::Override-Distribution", "HasArg"),
1234 ('v',"version","Dinstall::Options::Version")];
1236 changes_files = apt_pkg.ParseCommandLine(Cnf,Arguments,sys.argv);
1238 if Cnf["Dinstall::Options::Help"]:
1241 if Cnf["Dinstall::Options::Version"]:
1242 print "katie version 0.0000000000";
1245 postgresql_user = None; # Default == Connect as user running program.
1247 # -n/--dry-run invalidates some other options which would involve things happening
1248 if Cnf["Dinstall::Options::No-Action"]:
1249 Cnf["Dinstall::Options::Automatic"] = ""
1250 Cnf["Dinstall::Options::Ack-New"] = ""
1251 postgresql_user = Cnf["DB::ROUser"];
1253 projectB = pg.connect('projectb', Cnf["DB::Host"], int(Cnf["DB::Port"]), None, None, postgresql_user);
1255 db_access.init(Cnf, projectB);
1257 # Check that we aren't going to clash with the daily cron job
1259 if os.path.exists("%s/Archive_Maintenance_In_Progress" % (Cnf["Dir::RootDir"])) and not Cnf["Dinstall::Options::No-Lock"]:
1260 sys.stderr.write("Archive maintenance in progress. Try again later.\n");
1263 # Obtain lock if not in no-action mode
1265 if not Cnf["Dinstall::Options::No-Action"]:
1266 lock_fd = os.open(Cnf["Dinstall::LockFile"], os.O_RDWR);
1267 fcntl.lockf(lock_fd, FCNTL.F_TLOCK);
1269 # Read in the list of already-acknowledged NEW packages
1270 new_ack_list = utils.open_file(Cnf["Dinstall::NewAckList"],'r');
1272 for line in new_ack_list.readlines():
1273 new_ack_old[line[:-1]] = 1;
1274 new_ack_list.close();
1276 # Process the changes files
1277 for changes_file in changes_files:
1279 print "\n" + changes_file;
1280 process_it (changes_file);
1284 if install_count > 1:
1286 sys.stderr.write("Installed %d package %s, %s.\n" % (install_count, sets, utils.size_type(int(install_bytes))));
1288 # Write out the list of already-acknowledged NEW packages
1289 if Cnf["Dinstall::Options::Ack-New"]:
1290 new_ack_list = utils.open_file(Cnf["Dinstall::NewAckList"],'w')
1291 for i in new_ack_new.keys():
1292 new_ack_list.write(i+'\n')
1293 new_ack_list.close()
1296 if __name__ == '__main__':