+2007-08-07 Ian Jackson <iwj@ubuntu.com>
+
+ * man/deb-control.5: Document Breaks field.
+ * man/dpkg-query.1: Document Breaks as a recognized field.
+ * man/dpkg.1: Add description of '--force-breaks'.
+ * scripts/controllib.pl (@pkg_dep_fields): Add 'Breaks'.
+ * scripts/dpkg-genchanges.pl: Ignore Breaks field.
+ * scripts/dpkg-source.pl: Likewise.
+ * src/archives.c (try_remove_can): Rename to ...
+ (try_deconfigure_can): ... this. Generalize dependency force check
+ by taking a function as argument. Store the possible package removal
+ which caused the deconfiguration in the xinfo member of the package
+ to be deconfigured.
+ (try_remove_can): New function.
+ (check_breaks): Likewise.
+ * src/archives.h (check_breaks): New prototype.
+ * src/cleanup.c (cu_prermdeconfigure): Handle case when argv[1]
+ might be 0, if deconfigure was due to Breaks.
+ * src/configure.c (deferred_configure): Call breakses_ok.
+ * src/depcon.c (depisok): Add Breaks support.
+ * src/help.c (force_breaks): New function.
+ * src/main.c (fc_breaks): New variable.
+ (forceinfo): Add 'breaks' as a supported option for '--force-...'.
+ * src/main.h (struct perpackagestate): Add xinfo member.
+ (fc_breaks): New variable definition.
+ (breakses_ok): New prototype.
+ (force_depends): Likewise.
+ * src/packages.c (breaks_check_one): New function.
+ (breaks_check_target): Likewise.
+ (breakses_ok): Likewise.
+ * src/processarc.c (process_archive): Check for Breaks dependencies,
+ instead of bailing out if field found. Distinguish between deconfigure
+ due to a removal due to Conflicts or Depends, and deconfigure due to
+ an installation due to Breaks. Run the deconfiguration of each
+ package to be deconfigured once, instead of once per each conflicting
+ package being removed.
+
2007-07-31 Ian Jackson <iwj@ubuntu.com>
* src/archives.c (quote_filename): Change formatting to match the
* Move variables automatically modified at build time for the perl scripts
to a new style perl module (Dpkg) and make all programs use it.
* Switch 'dpkg-gettext.pl' to a new style perl module (Dpkg::Gettext).
+ * Implement support for Breaks field. Closes: #379140
+ Thanks to Ian Jackson.
+ * Run the deconfiguration of each package to be deconfigured once, instead
+ of once per each conflicting package being removed. Closes: #378003
+ Thanks to Ian Jackson.
[ Updated scripts translations ]
* French (Frédéric Bothamy, Christian Perrier).
"<<" for less than, ">=" for greater than or equal to, "<=" for less than
or equal to, and "=" for equal to.
.TP
+.BR Breaks: " <package list>"
+Lists packages that this one breaks, for example by exposing bugs
+when the named packages rely on this one. The package maintenance
+software will not allow broken packages to be configured; generally
+the resolution is to upgrade the packages named in a
+.B Breaks
+field.
+.TP
.BR Conflicts: " <package list>"
Lists packages that conflict with this one, for example by containing
files with the same names. The package maintenance software will not
\fBConffiles\fP
\fBConfig\-Version\fP
\fBConflicts\fP
+ \fBBreaks\fP
\fBDepends\fP
\fBDescription\fP
\fBEnhances\fP
\fBdepends\-version\fP:
Don't care about versions when checking dependencies.
+\fBbreaks\fP:
+Install, even if this would break another package.
+
\fBconflicts\fP:
Install, even if it conflicts with another package. This is dangerous,
for it will usually cause overwriting of some files.
my $parsechangelog = 'dpkg-parsechangelog';
our @pkg_dep_fields = qw(Pre-Depends Depends Recommends Suggests Enhances
- Conflicts Replaces Provides);
+ Conflicts Breaks Replaces Provides);
our @src_dep_fields = qw(Build-Depends Build-Depends-Indep
Build-Conflicts Build-Conflicts-Indep);
}
push(@archvalues,$v) unless !$v || $archadded{$v}++;
} elsif (m/^(Package|Essential|Pre-Depends|Depends|Provides)$/ ||
- m/^(Recommends|Suggests|Enhances|Optional|Conflicts|Replaces)$/ ||
+ m/^(Recommends|Suggests|Enhances|Optional|Conflicts|Breaks|Replaces)$/ ||
m/^X[CS]+-/i) {
} else {
&unknown(_g("package's section of control info file"));
$f{$_}= $v;
} elsif (m/^(Package|Essential|Pre-Depends|Depends|Provides)$/i ||
m/^(Recommends|Suggests|Optional|Conflicts|Replaces)$/i ||
- m/^(Enhances|Description|Section|Priority)$/i ||
+ m/^(Breaks|Enhances|Description|Section|Priority)$/i ||
m/^X[BC]+-/i) {
} else {
&unknown(_g("package's section of control info file"));
return 0;
}
-static int try_remove_can(struct deppossi *pdep,
- struct pkginfo *fixbyrm,
+static int try_deconfigure_can(int (*force_p)(struct deppossi*),
+ struct pkginfo *pkg,
+ struct deppossi *pdep,
+ const char *action,
+ struct pkginfo *removal,
const char *why) {
+ /* Also checks whether the pdep is forced, first, according to force_p.
+ * force_p may be 0 in which case nothing is considered forced.
+ *
+ * Action is a string describing the action which causes the
+ * deconfiguration:
+ * removal of <package> (due to Conflicts+Depends removal!=0)
+ * installation of <package> (due to Breaks removal==0)
+ *
+ * Return values: 2: forced (no deconfiguration needed, why is printed)
+ * 1: deconfiguration queued ok (no message printed)
+ * 0: not possible (why is printed)
+ */
struct packageinlist *newdeconf;
- if (force_depends(pdep)) {
+ if (force_p && force_p(pdep)) {
fprintf(stderr, _("dpkg: warning - "
- "ignoring dependency problem with removal of %s:\n%s"),
- fixbyrm->name, why);
- return 1;
+ "ignoring dependency problem with %s:\n%s"),
+ action, why);
+ return 2;
} else if (f_autodeconf) {
- if (pdep->up->up->installed.essential) {
+ if (pkg->installed.essential) {
if (fc_removeessential) {
fprintf(stderr, _("dpkg: warning - considering deconfiguration of essential\n"
- " package %s, to enable removal of %s.\n"),
- pdep->up->up->name,fixbyrm->name);
+ " package %s, to enable %s.\n"),
+ pkg->name, action);
} else {
fprintf(stderr, _("dpkg: no, %s is essential, will not deconfigure\n"
- " it in order to enable removal of %s.\n"),
- pdep->up->up->name,fixbyrm->name);
+ " it in order to enable %s.\n"),
+ pkg->name, action);
return 0;
}
}
- pdep->up->up->clientdata->istobe= itb_deconfigure;
+ pkg->clientdata->istobe= itb_deconfigure;
newdeconf= malloc(sizeof(struct packageinlist));
newdeconf->next= deconfigure;
- newdeconf->pkg= pdep->up->up;
+ newdeconf->pkg= pkg;
+ newdeconf->xinfo= removal;
deconfigure= newdeconf;
return 1;
} else {
- fprintf(stderr, _("dpkg: no, cannot remove %s (--auto-deconfigure will help):\n%s"),
- fixbyrm->name, why);
+ fprintf(stderr, _("dpkg: no, cannot proceed with %s (--auto-deconfigure will help):\n%s"),
+ action, why);
return 0;
}
}
+static int try_remove_can(struct deppossi *pdep,
+ struct pkginfo *fixbyrm,
+ const char *why) {
+ char action[512];
+ sprintf(action, _("removal of %.250s"), fixbyrm->name);
+ return try_deconfigure_can(force_depends, pdep->up->up, pdep,
+ action, fixbyrm, why);
+}
+
+void check_breaks(struct dependency *dep, struct pkginfo *pkg,
+ const char *pfilename) {
+ struct pkginfo *fixbydeconf;
+ struct varbuf why;
+ int ok;
+
+ varbufinit(&why);
+
+ fixbydeconf= 0;
+ if (depisok(dep, &why, &fixbydeconf, 0)) {
+ varbuffree(&why);
+ return;
+ }
+
+ varbufaddc(&why, 0);
+
+ if (fixbydeconf && f_autodeconf) {
+ char action[512];
+
+ ensure_package_clientdata(fixbydeconf);
+ assert(fixbydeconf->clientdata->istobe == itb_normal);
+
+ sprintf(action, _("installation of %.250s"), pkg->name);
+ fprintf(stderr, _("dpkg: considering deconfiguration of %s,"
+ " which would be broken by %s ...\n"),
+ fixbydeconf->name, action);
+
+ ok= try_deconfigure_can(force_breaks, fixbydeconf, dep->list,
+ action, 0, why.buf);
+ if (ok == 1) {
+ fprintf(stderr, _("dpkg: yes, will deconfigure %s (broken by %s).\n"),
+ fixbydeconf->name, pkg->name);
+ }
+ } else {
+ fprintf(stderr, _("dpkg: regarding %s containing %s:\n%s"),
+ pfilename, pkg->name, why.buf);
+ ok= 0;
+ }
+ varbuffree(&why);
+ if (ok > 0) return;
+
+ if (force_breaks(dep->list)) {
+ fprintf(stderr, _("dpkg: warning - ignoring breakage,"
+ " may proceed anyway !\n"));
+ return;
+ }
+
+ if (fixbydeconf && !f_autodeconf) {
+ ohshit(_("installing %.250s would break %.250s, and\n"
+ " deconfiguration is not permitted (--auto-deconfigure might help)"),
+ pkg->name, fixbydeconf->name);
+ } else {
+ ohshit(_("installing %.250s would break existing software"),
+ pkg->name);
+ }
+}
+
void check_conflict(struct dependency *dep, struct pkginfo *pkg,
const char *pfilename) {
struct pkginfo *fixbyrm;
void check_conflict(struct dependency *dep, struct pkginfo *pkg,
const char *pfilename);
+void check_breaks(struct dependency *dep, struct pkginfo *pkg,
+ const char *pfilename);
struct fileinlist *addfiletolist(struct tarcontext *tc,
struct filenamenode *namenode);
void ok_prermdeconfigure(int argc, void **argv) {
struct pkginfo *deconf= (struct pkginfo*)argv[0];
- /* also has conflictor in argv[1] and infavour in argv[2] */
+ /* also has conflictor in argv[1] and infavour in argv[2].
+ * conflictor may be 0 if deconfigure was due to Breaks */
if (cipaction->arg == act_install)
add_to_queue(deconf);
void cu_prermdeconfigure(int argc, void **argv) {
struct pkginfo *deconf= (struct pkginfo*)argv[0];
- struct pkginfo *conflictor= (struct pkginfo*)argv[1];
+ struct pkginfo *conflictor= (struct pkginfo*)argv[1]; /* may be 0 */
struct pkginfo *infavour= (struct pkginfo*)argv[2];
maintainer_script_installed(deconf,POSTINSTFILE,"post-installation",
"abort-deconfigure", "in-favour", infavour->name,
versiondescribe(&infavour->available.version,
vdew_nonambig),
- "removing", conflictor->name,
- versiondescribe(&conflictor->installed.version,
- vdew_nonambig),
+ conflictor ? "removing" : (char*)0,
+ conflictor ? conflictor->name : (char*)0,
+ conflictor ? versiondescribe(&conflictor->installed.version,
+ vdew_nonambig) : (char*)0,
(char*)0);
deconf->status= stat_installed;
modstatdb_note(deconf);
pkg->clientdata->istobe= itb_installnew;
add_to_queue(pkg);
return;
- } else if (ok == 0) {
+ }
+ ok = breakses_ok(pkg, &aemsgs) ? ok : 0;
+ if (ok == 0) {
sincenothing= 0;
varbufaddc(&aemsgs,0);
fprintf(stderr,
* before a package is unpacked, when it is sufficient for the package
* to be unpacked provided that both the unpacked and previously-configured
* versions are acceptable.
+ * On 0 return (`not OK'), *canfixbyremove refers to a package which
+ * if removed (dep_conflicts) or deconfigured (dep_breaks) will fix
+ * the problem. Caller may pass 0 for canfixbyremove and need not
+ * initialise *canfixbyremove.
*/
struct deppossi *possi;
struct deppossi *provider;
dep->type == dep_recommends || dep->type == dep_suggests ||
dep->type == dep_enhances);
+ if (canfixbyremove) *canfixbyremove= 0;
+
/* The dependency is always OK if we're trying to remove the depend*ing*
* package.
*/
}
}
- if (canfixbyremove) *canfixbyremove= 0;
return 0;
} else {
- /* It's a conflict. There's only one main alternative,
+ /* It's conflicts or breaks. There's only one main alternative,
* but we also have to consider Providers. We return `0' as soon
* as we find something that matches the conflict, and only describe
* it then. If we get to the end without finding anything we return `1'.
nconflicts= 0;
if (possi->ed != possi->up->up) {
- /* If the package conflicts with itself it must mean that it conflicts
- * with other packages which provide the same virtual name. We therefore
- * don't look at the real package and go on to the virtual ones.
+ /* If the package conflicts with or breaks itself it must mean
+ * other packages which provide the same virtual name. We
+ * therefore don't look at the real package and go on to the
+ * virtual ones.
*/
switch (possi->ed->clientdata->istobe) {
nconflicts++;
*canfixbyremove= possi->ed;
break;
- case itb_normal: case itb_deconfigure: case itb_preinstall:
+ case itb_deconfigure:
+ if (dep->type == dep_breaks) break; /* already deconfiguring this */
+ /* fall through */
+ case itb_normal: case itb_preinstall:
switch (possi->ed->status) {
case stat_notinstalled: case stat_configfiles:
break;
- default:
+ case stat_halfinstalled: case stat_unpacked:
+ case stat_halfconfigured:
+ if (dep->type == dep_breaks) break; /* no problem */
+ case stat_installed:
if (!versionsatisfied(&possi->ed->installed, possi)) break;
sprintf(linebuf, _(" %.250s (version %.250s) is %s.\n"),
possi->ed->name,
continue;
case itb_remove:
continue;
- case itb_normal: case itb_deconfigure: case itb_preinstall:
+ case itb_deconfigure:
+ if (dep->type == dep_breaks) continue; /* already deconfiguring */
+ case itb_normal: case itb_preinstall:
switch (provider->up->up->status) {
case stat_notinstalled: case stat_configfiles:
continue;
- default:
+ case stat_halfinstalled: case stat_unpacked:
+ case stat_halfconfigured:
+ if (dep->type == dep_breaks) break; /* no problem */
+ case stat_installed:
sprintf(linebuf, _(" %.250s provides %.250s and is %s.\n"),
provider->up->up->name, possi->ed->name,
gettext(statusstrings[provider->up->up->status]));
ignore_depends(possi->up->up);
}
+int force_breaks(struct deppossi *possi) {
+ return fc_breaks ||
+ ignore_depends(possi->ed) ||
+ ignore_depends(possi->up->up);
+}
+
int force_conflicts(struct deppossi *possi) {
return fc_conflicts;
}
/* Change fc_overwrite to 1 to enable force-overwrite by default */
int fc_downgrade=1, fc_configureany=0, fc_hold=0, fc_removereinstreq=0, fc_overwrite=0;
int fc_removeessential=0, fc_conflicts=0, fc_depends=0, fc_dependsversion=0;
-int fc_badpath=0, fc_overwritediverted=0, fc_architecture=0;
+int fc_breaks=0, fc_badpath=0, fc_overwritediverted=0, fc_architecture=0;
int fc_nonroot=0, fc_overwritedir=0, fc_conff_new=0, fc_conff_miss=0;
int fc_conff_old=0, fc_conff_def=0;
int fc_badverify = 0;
{ "confmiss", &fc_conff_miss },
{ "depends", &fc_depends },
{ "depends-version", &fc_dependsversion },
+ { "breaks", &fc_breaks },
{ "bad-path", &fc_badpath },
{ "not-root", &fc_nonroot },
{ "overwrite", &fc_overwrite },
struct packageinlist {
struct packageinlist *next;
struct pkginfo *pkg;
+ void *xinfo;
};
enum action { act_unset, act_install, act_unpack, act_avail, act_configure,
extern unsigned long f_debug;
extern int fc_downgrade, fc_configureany, fc_hold, fc_removereinstreq, fc_overwrite;
extern int fc_removeessential, fc_conflicts, fc_depends, fc_dependsversion;
-extern int fc_badpath, fc_overwritediverted, fc_architecture;
+extern int fc_breaks, fc_badpath, fc_overwritediverted, fc_architecture;
extern int fc_nonroot, fc_overwritedir, fc_conff_new, fc_conff_miss;
extern int fc_conff_old, fc_conff_def;
extern int fc_badverify;
void removal_bulk(struct pkginfo *pkg);
int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in);
int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing,
- struct varbuf *aemsgs);
+ struct varbuf *aemsgs); /* checks [Pre]-Depends only */
+int breakses_ok(struct pkginfo *pkg, struct varbuf *aemsgs);
void deferred_remove(struct pkginfo *pkg);
void deferred_configure(struct pkginfo *pkg);
void cu_closefd(int argc, void **argv);
int ignore_depends(struct pkginfo *pkg);
+int force_breaks(struct deppossi *possi);
int force_depends(struct deppossi *possi);
int force_conff_new(struct deppossi *possi);
int force_conff_miss(struct deppossi *possi);
}
}
+static void breaks_check_one(struct varbuf *aemsgs, int *ok,
+ struct deppossi *breaks,
+ struct pkginfo *broken,
+ struct pkginfo *breaker,
+ struct pkginfo *virtbroken) {
+ struct varbuf depmsg;
+
+ debug(dbg_depcondetail, " checking breaker %s virtbroken %s",
+ breaker->name, virtbroken ? virtbroken->name : "<none>");
+
+ if (breaker->status == stat_notinstalled ||
+ breaker->status == stat_configfiles) return;
+ if (broken == breaker) return;
+ if (!versionsatisfied(&broken->installed, breaks)) return;
+ if (ignore_depends(breaker)) return;
+ if (virtbroken && ignore_depends(virtbroken)) return;
+
+ varbufinit(&depmsg);
+ varbufdependency(&depmsg, breaks->up);
+ varbufaddc(&depmsg, 0);
+ varbufprintf(aemsgs, _(" %s (%s) breaks %s and is %s.\n"),
+ breaker->name,
+ versiondescribe(&breaker->installed.version, vdew_nonambig),
+ depmsg.buf,
+ gettext(statusstrings[breaker->status]));
+ varbuffree(&depmsg);
+
+ if (virtbroken) {
+ varbufprintf(aemsgs, _(" %s (%s) provides %s.\n"),
+ broken->name,
+ versiondescribe(&broken->installed.version, vdew_nonambig),
+ virtbroken->name);
+ } else if (breaks->verrel != dvr_none) {
+ varbufprintf(aemsgs, _(" Version of %s to be configured is %s.\n"),
+ broken->name,
+ versiondescribe(&broken->installed.version, vdew_nonambig));
+ if (fc_dependsversion) return;
+ }
+ if (force_breaks(breaks)) return;
+ *ok= 0;
+}
+
+void breaks_check_target(struct varbuf *aemsgs, int *ok,
+ struct pkginfo *broken,
+ struct pkginfo *target,
+ struct pkginfo *virtbroken) {
+ struct deppossi *possi;
+
+ for (possi= target->installed.depended; possi; possi= possi->nextrev) {
+ if (possi->up->type != dep_breaks) continue;
+ if (virtbroken && possi->verrel != dvr_none) continue;
+ breaks_check_one(aemsgs, ok, possi, broken, possi->up->up, virtbroken);
+ }
+}
+
+int breakses_ok(struct pkginfo *pkg, struct varbuf *aemsgs) {
+ struct dependency *dep;
+ struct pkginfo *virtbroken;
+ int ok= 2;
+
+ debug(dbg_depcon, " checking Breaks");
+
+ breaks_check_target(aemsgs, &ok, pkg, pkg, 0);
+
+ for (dep= pkg->installed.depends; dep; dep= dep->next) {
+ if (dep->type != dep_provides) continue;
+ virtbroken= dep->list->ed;
+ debug(dbg_depcondetail, " checking virtbroken %s", virtbroken->name);
+ breaks_check_target(aemsgs, &ok, pkg, virtbroken, virtbroken);
+ }
+ return ok;
+}
+
int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing,
struct varbuf *aemsgs) {
int ok, matched, found, thisf, interestingwarnings;
/* Look for things we conflict with. */
check_conflict(dsearch, pkg, pfilename);
break;
+ case dep_breaks:
+ /* Look for things we break. */
+ check_breaks(dsearch, pkg, pfilename);
+ break;
case dep_provides:
/* Look for things that conflict with what we provide. */
if (dsearch->list->ed->installed.valid) {
}
}
break;
- case dep_breaks:
- fprintf(stderr, _("dpkg: regarding %s containing %s:\n"
- " package uses Breaks; not supported in this dpkg\n"),
- pfilename, pkg->name);
- if (!force_depends(dsearch->list))
- ohshit(_("unsupported dependency problem - not installing %.250s"),
- pkg->name);
- fprintf(stderr, _("dpkg: warning - ignoring Breaks !\n"));
- /* FIXME: implement Breaks */
- break;
case dep_suggests:
case dep_recommends:
case dep_depends:
modstatdb_note(pkg);
}
- for (i = 0 ; i < cflict_index ; i++) {
- if (!(conflictor[i]->status == stat_halfconfigured ||
- conflictor[i]->status == stat_installed)) continue;
for (deconpil= deconfigure; deconpil; deconpil= deconpil->next) {
- printf(_("De-configuring %s, so that we can remove %s ...\n"),
- deconpil->pkg->name, conflictor[i]->name);
+ struct pkginfo *removing= deconpil->xinfo;
+
+ printf(removing ?
+ _("De-configuring %s, to allow removal of %s ...\n") :
+ _("De-configuring %s ...\n"),
+ deconpil->pkg->name, removing ? removing->name : 0);
+
deconpil->pkg->status= stat_halfconfigured;
modstatdb_note(deconpil->pkg);
/* This means that we *either* go and run postinst abort-deconfigure,
push_cleanup(cu_prermdeconfigure,~ehflag_normaltidy,
ok_prermdeconfigure,ehflag_normaltidy,
3,(void*)deconpil->pkg,
- (void*)conflictor[i],(void*)pkg);
+ (void*)removing, (void*)pkg);
maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal",
"deconfigure", "in-favour", pkg->name,
versiondescribe(&pkg->available.version,
vdew_nonambig),
- "removing", conflictor[i]->name,
- versiondescribe(&conflictor[i]->installed.version,
- vdew_nonambig),
+ removing ? "removing" : (char*)0,
+ removing ? removing->name : (char*)0,
+ removing ? versiondescribe(&removing->installed.version,
+ vdew_nonambig) : (char*)0,
(char*)0);
}
+
+ for (i = 0 ; i < cflict_index; i++) {
+ if (!(conflictor[i]->status == stat_halfconfigured ||
+ conflictor[i]->status == stat_installed)) continue;
conflictor[i]->status= stat_halfconfigured;
modstatdb_note(conflictor[i]);
push_cleanup(cu_prerminfavour,~ehflag_normaltidy, 0,0,