pkgsrc-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: pkg/45077: pkgtools/pkg_install catch circular dependencies in add/perform.c
The following reply was made to PR pkg/45077; it has been noted by GNATS.
From: Thomas Cort <tcort%minix3.org@localhost>
To: gnats-bugs%netbsd.org@localhost
Cc:
Subject: Re: pkg/45077: pkgtools/pkg_install catch circular dependencies in
add/perform.c
Date: Mon, 1 Aug 2011 21:02:33 -0400
Update: the patch originally attached to this PR has been in use in Minix
for a little while now. One of our power users, Jan Wieck, was able to
exercise the infinite recursion check. If Jan wipes binutils and
gcc, then adds binutils and gcc, he gets an infinite recursion which
results in an EMFILE error.
# pkgin -y rm binutils
# pkg_add /usr/pkgsrc/packages/3.2.0/i386/All/gcc44-4.4.5*
Jan tracked down the issue to a circular dependency between devel/gmp and
devel/gcc44. The patch originally attached to this PR doesn't handle
this well. Jan's analysis: "What happens is that pkg_do() and
check_dependencies() call each other, but one of them opens some file or
dir before calling the other. The error happens at depth=250, which
makes total sense given that Minix has a 255 open file limit.
Thomas defined the max depth as 1024, which is never reached in my case."
Jan re-wrote the circular dependency checking patch to use a stack.
The patch is attached. It allows the packages in the above example to be
installed.
diff --git a/pkgtools/pkg_install/files/add/perform.c
b/pkgtools/pkg_install/files/add/perform.c
index aa3dff3..ae71fe2 100644
--- a/pkgtools/pkg_install/files/add/perform.c
+++ b/pkgtools/pkg_install/files/add/perform.c
@@ -102,6 +102,12 @@ struct pkg_task {
char **dependencies;
};
+struct dependency_chain {
+ const char *pkgpath;
+ struct dependency_chain *next;
+};
+static struct dependency_chain *dependency_chain = NULL;
+
static const struct pkg_meta_desc {
size_t entry_offset;
const char *entry_filename;
@@ -128,6 +134,9 @@ static const struct pkg_meta_desc {
static int pkg_do(const char *, int, int);
+static int dependency_push(const char *);
+static void dependency_pop(void);
+
static int
end_of_version(const char *opsys, const char *version_end)
{
@@ -1378,6 +1387,10 @@ pkg_do(const char *pkgpath, int mark_automatic, int
top_level)
char *archive_name;
int status, invalid_sig;
struct pkg_task *pkg;
+ int rc;
+
+ if ((rc = dependency_push(pkgpath)) != 1)
+ return rc;
pkg = xcalloc(1, sizeof(*pkg));
@@ -1572,6 +1585,7 @@ clean_memory:
free(pkg->pkgname);
clean_find_archive:
free(pkg);
+ dependency_pop();
return status;
}
@@ -1588,5 +1602,77 @@ pkg_perform(lpkg_head_t *pkgs)
free_lpkg(lpp);
}
+ if (dependency_chain != NULL) { /* ensure stack is empty */
+ warnx("leaving pkg_perform with a non-empty stack.");
+ ++errors;
+ }
+
return errors;
}
+
+
+/*
+ * Add an element to the dependency chain and check for circular dependency
+ * while at it.
+ *
+ * returns 1 on success, 0 on circular dep detected, -1 on error.
+ */
+static int
+dependency_push(const char *pkgpath)
+{
+ struct dependency_chain *dep;
+ struct dependency_chain **last_pdep;
+
+ /* Check if that package is already in the chain. */
+ last_pdep = &dependency_chain;
+ for (dep = *last_pdep; dep; last_pdep = &(dep->next), dep = dep->next) {
+ if (strcmp(dep->pkgpath, pkgpath) == 0) {
+ /* Found it - that means we have a circular dependency
*/
+ warnx("ignoring circular dependency:");
+ while (dep != NULL) {
+ fprintf(stderr, "- %s requires\n",
dep->pkgpath);
+ dep = dep->next;
+ }
+ fprintf(stderr, "- %s\n", pkgpath);
+ return 0;
+ }
+ }
+
+ /* Not found. Add an entry to the end of the chain */
+ dep = (struct dependency_chain *)malloc(sizeof(struct
dependency_chain));
+ if (dep == NULL) {
+ fprintf(stderr, "Out of memory in dependency_push for %s\n",
+ pkgpath);
+ return -1;
+ }
+
+ dep->pkgpath = pkgpath;
+ dep->next = NULL;
+ *last_pdep = dep;
+ return 1;
+}
+
+
+/*
+ * Remove the last entry from the dependency chain.
+ */
+static void
+dependency_pop(void)
+{
+ struct dependency_chain *dep = dependency_chain;
+ struct dependency_chain **pdep = &dependency_chain;
+
+ /* This should never happen */
+ if (dep == NULL) {
+ warnx("empty dependency chain on pop");
+ return;
+ }
+
+ while (dep->next != NULL) {
+ pdep = &(dep->next);
+ dep = dep->next;
+ }
+
+ free(dep);
+ *pdep = NULL;
+}
Home |
Main Index |
Thread Index |
Old Index