Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 15 Mar 2001 02:10:51 +0200 (EET)
From:      Maxim Sobolev <sobomax@freebsd.org>
To:        jkh@freebsd.org
Cc:        ports@freebsd.org, asami@freebsd.org, kris@freebsd.org
Subject:   Serious misbehaviour of ``pkg_add -r'' [patch]
Message-ID:  <200103150010.f2F0AqK53921@vic.sabbo.net>

next in thread | raw e-mail | index | archive | help

--%--multipart-mixed-boundary-1.53716.984615051--%
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

Hi folks,

As Kris pointed me out, a serious bug exists in our "remote fetch" pkg_add(1)
mode, i.e. the mode when using supplied name pkg_add automatically fetches
and installs requested package and all its dependencies.

This bug affects most of the cases when user tries to install package that
has multi-level dependencies, i.e. ``pkg_add -r (A)'', while (A) depends on
package (B) which in turn depends on package (C) and so on.

Unfortunately pkg_add(1) in this case doesn't apply any efforts to ensure
proper installation order, so in many cases it will try to install (B)
before (C), obviously failing miserably due to missed dependency and user
will end up with only (C) installed.

Good example of such misbehaviour is tkcvs-6.4 package. You can verify
this bug by deinstalling tcl/tk any to doing ``pkg_add -r tkcvs-6.4''.
After several error messages you will end with only tcl installed.

It appears that pkg_add(1) tries to install dependencies exactly in the
order in which they were specifies in the +CONTENTS file, which is a
reversed alphabetical order (bsd.ports.mk sorts them alphabetically and
passes to pkg_create, which reverses it then). For the tkcvs package
relevant part of +CONTENTS looks like:

@cwd /usr/local
@pkgdep tk-8.3.2
@pkgdep tcl-8.3.2
@pkgdep XFree86-libraries-4.0.1_2
@comment ORIGIN:devel/tkcvs

Thus, after fetching and unpacking tkcvs pkg_add(1) fetches and tries to
install tk, but fails because of tcl and XFree packages are missed. After
that it fetches/installs tcl and XFree, but fails to install tkcvs itself
due to missed tk package.

The first approach to that problem that came to my mind was to enhance
pkg_add(1) to analyse dependencies between packages after fetching each
particular package, but it is not very clear and easy solution because
of the following reasons:

1. In many cases this require that several packages to be fetched before
   pkg_add would be able to install something (as you can see from the
   tkcvs example, pkg_add have to fetch tkcvs, tk and tcl and only then
   it will be able to install tcl);
2. as a result of 1 pkg_add have to store unpacked but have not installed
   yet packages on the user's disk;
3. it is not very clear how to handle interrupted transmission (i.e. what
   to do with already downloaded but not yet installed packages).

After thinking it over I came to a conclusion that there is at least one
much more easier approach. Instead of adding complexity into pkg_add(1)
we can enhance pkg_create to sort dependency list in such a way that it
will list packages in the order in which they could be subsequently added.
When creating package pkg_create already have all dependencies installed,
so it is easy for it to do proper sorting. Another advantage is that the
users will not have to upgrade their pkg_* tools to get correct functionality.

With this message I'm attaching patch, which adds reordering into pkg_create.
Please note that it is not completely finished (I'm planning to add some more
errors checking), but it worksfor me. I would like to get your comments on
it, because we have to move quickly as the problem described above should
be resolved *before* 4.3 ports freeze.

Thanks!

-Maxim

--%--multipart-mixed-boundary-1.53716.984615051--%
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Description: ASCII C program text
Content-Disposition: attachment; filename="pi.diff"

Index: create/perform.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/create/perform.c,v
retrieving revision 1.53
diff -d -u -r1.53 perform.c
--- create/perform.c	2001/01/22 12:01:54	1.53
+++ create/perform.c	2001/03/14 22:35:51
@@ -97,15 +97,37 @@
 
     /* Stick the dependencies, if any, at the top */
     if (Pkgdeps) {
+	char **deps;
+	char *tmp;
+	int i, ndeps;
+
 	if (Verbose && !PlistOnly)
 	    printf("Registering depends:");
-	while (Pkgdeps) {
+
+	/* Count number of dependencies */
+	tmp = strdup(Pkgdeps);
+	ndeps = 0;
+	while (tmp) {
+	    cp = strsep(&tmp, " \t\n");
+	    if (*cp)
+		ndeps++;
+	}
+	free(tmp);
+
+	/* Create easy to use NULL-terminated list */
+	deps = alloca(sizeof(*deps) * ndeps + 1);
+	for (i = 0; Pkgdeps; i++) {
 	    cp = strsep(&Pkgdeps, " \t\n");
-	    if (*cp) {
-		add_plist_top(&plist, PLIST_PKGDEP, cp);
-		if (Verbose && !PlistOnly)
-		    printf(" %s", cp);
-	    }
+	    if (*cp)
+		deps[i] = cp;
+	}
+	deps[ndeps] = NULL;
+
+	sortdeps(deps);
+	for (i = 0; i < ndeps; i++) {
+	    add_plist_top(&plist, PLIST_PKGDEP, deps[i]);
+	    if (Verbose && !PlistOnly)
+		printf(" %s", deps[i]);
 	}
 	if (Verbose && !PlistOnly)
 	    printf(".\n");
Index: delete/perform.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/delete/perform.c,v
retrieving revision 1.28
diff -d -u -r1.28 perform.c
--- delete/perform.c	2001/03/04 17:38:38	1.28
+++ delete/perform.c	2001/03/14 22:35:51
@@ -30,7 +30,6 @@
 static int pkg_do(char *);
 static void sanity_check(char *);
 static void undepend(PackingList, char *);
-static int chkifdepends(char *pkgname1, char *pkgname2);
 static char LogDir[FILENAME_MAX];
 
 
@@ -38,10 +37,9 @@
 pkg_perform(char **pkgs)
 {
     char **matched;
-    char *tmp;
-    int i, j;
+    int i;
     int err_cnt = 0;
-    int loop_cnt, errcode;
+    int errcode;
 
     if (MatchType != MATCH_EXACT) {
 	matched = matchinstalled(MatchType, pkgs, &errcode);
@@ -65,36 +63,8 @@
 	}
     }
 
+    err_cnt = sortdeps(pkgs);
     for (i = 0; pkgs[i]; i++) {
-	/*
-	 * Check to see if any other package in pkgs[i+1:] depends
-	 * on pkgs[i] and deffer removal of pkgs[i] if so.
-	 */
-	loop_cnt = 0;
-	for (j = i + 1; pkgs[j]; j++) {
-	    if (chkifdepends(pkgs[j], pkgs[i]) == 1) {
-		/*
-		 * Try to avoid deadlock if package A depends on B which in
-		 * turn depends on C and C due to an error depends on A.
-		 * Use ugly but simple method, becase it Should Never
-		 * Happen[tm] in the real life anyway.
-		 */
-		if (loop_cnt > 4096) {
-		    warnx("dependency loop detected for package %s", pkgs[j]);
-		    err_cnt++;
-		    break;
-		}
-		loop_cnt++;
-		tmp = pkgs[i];
-		pkgs[i] = pkgs[j];
-		pkgs[j] = tmp;
-		/*
-		 * Another iteration requred to check if new pkgs[i]
-		 * itself has any packages that depend on it
-		 */
-		j = i + 1;
-	    }
-	}
 	err_cnt += pkg_do(pkgs[i]);
     }
 
@@ -364,38 +334,3 @@
      return;
 }
 
-/*
- * Check to see if pkgname1 depends on pkgname2.
- * Returns 1 if depends, 0 if not, and -1 if error occured.
- */ 
-static int
-chkifdepends(char *pkgname1, char *pkgname2)
-{
-    FILE *fp;
-    char fname[FILENAME_MAX];
-    char fbuf[FILENAME_MAX];
-    char *tmp;
-    int retval;
-
-    sprintf(fname, "%s/%s/%s",
-	    (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
-	    pkgname2, REQUIRED_BY_FNAME);
-    fp = fopen(fname, "r");
-    if (fp == NULL) {
-	/* Probably pkgname2 doesn't have any packages that depend on it */
-	return 0;
-    }
-
-    retval = 0;
-    while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
-	if (fbuf[strlen(fbuf)-1] == '\n')
-	    fbuf[strlen(fbuf)-1] = '\0';
-	if (strcmp(fbuf, pkgname1) == 0) {	/* match */
-	    retval = 1;
-	    break;
-	}
-    }
-
-    fclose(fp);
-    return retval;
-}
Index: lib/Makefile
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/lib/Makefile,v
retrieving revision 1.7
diff -d -u -r1.7 Makefile
--- lib/Makefile	2001/02/27 09:00:18	1.7
+++ lib/Makefile	2001/03/14 22:35:51
@@ -1,7 +1,8 @@
 # $FreeBSD$
 
 LIB=	install
-SRCS=	file.c msg.c plist.c str.c exec.c global.c pen.c match.c
+SRCS=	file.c msg.c plist.c str.c exec.c global.c pen.c match.c \
+	deps.c
 CFLAGS+= ${DEBUG}
 NOPROFILE= yes
 NOPIC=	yes
Index: lib/deps.c
===================================================================
RCS file: deps.c
diff -N deps.c
--- /dev/null	Wed Mar 14 14:35:25 2001
+++ deps.c	Wed Mar 14 14:35:52 2001
@@ -0,0 +1,107 @@
+#ifndef lint
+static const char rcsid[] =
+  "$FreeBSD$";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 14 March 2001
+ *
+ * Routines used to do various operations with dependencies
+ * among installed packages.
+ *
+ */
+
+#include "lib.h"
+
+#include <err.h>
+#include <stdio.h>
+
+int
+sortdeps(char **pkgs)
+{
+    char *tmp;
+    int i, j, loop_cnt;
+    int err_cnt = 0;
+
+    for (i = 0; pkgs[i]; i++) {
+	/*
+	 * Check to see if any other package in pkgs[i+1:] depends
+	 * on pkgs[i] and swap those two packages if so.
+	 */
+	loop_cnt = 0;
+	for (j = i + 1; pkgs[j]; j++) {
+	    if (chkifdepends(pkgs[j], pkgs[i]) == 1) {
+		/*
+                 * Try to avoid deadlock if package A depends on B which in
+                 * turn depends on C and C due to an error depends on A.
+                 * Use ugly but simple method, becase it Should Never
+                 * Happen[tm] in the real life anyway.
+                 */
+		if (loop_cnt > 4096) {
+		    warnx("dependency loop detected for package %s", pkgs[j]);
+		    err_cnt++;
+		    break;
+		}
+		loop_cnt++;
+		tmp = pkgs[i];
+		pkgs[i] = pkgs[j];
+		pkgs[j] = tmp;
+		/*
+                 * Another iteration requred to check if new pkgs[i]
+                 * itself has any packages that depend on it
+                 */
+                j = i + 1;
+	    }
+	}
+    }
+    return err_cnt;
+}
+
+/*
+ * Check to see if pkgname1 depends on pkgname2.
+ * Returns 1 if depends, 0 if not, and -1 if error occured.
+ */ 
+int
+chkifdepends(char *pkgname1, char *pkgname2)
+{
+    FILE *fp;
+    char fname[FILENAME_MAX];
+    char fbuf[FILENAME_MAX];
+    char *tmp;
+    int retval;
+
+    sprintf(fname, "%s/%s/%s",
+	    (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+	    pkgname2, REQUIRED_BY_FNAME);
+    fp = fopen(fname, "r");
+    if (fp == NULL) {
+	/* Probably pkgname2 doesn't have any packages that depend on it */
+	return 0;
+    }
+
+    retval = 0;
+    while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
+	if (fbuf[strlen(fbuf)-1] == '\n')
+	    fbuf[strlen(fbuf)-1] = '\0';
+	if (strcmp(fbuf, pkgname1) == 0) {	/* match */
+	    retval = 1;
+	    break;
+	}
+    }
+
+    fclose(fp);
+    return retval;
+}
Index: lib/lib.h
===================================================================
RCS file: /home/ncvs/src/usr.sbin/pkg_install/lib/lib.h,v
retrieving revision 1.32
diff -d -u -r1.32 lib.h
--- lib/lib.h	2001/02/27 09:00:18	1.32
+++ lib/lib.h	2001/03/14 22:35:52
@@ -177,6 +177,10 @@
 /* Query installed packages */
 char		**matchinstalled(match_t, char **, int *);
 
+/* Dependencies */
+int		sortdeps(char **);
+int		chkifdepends(char *, char *);
+
 /* Externs */
 extern Boolean	Verbose;
 extern Boolean	Fake;

--%--multipart-mixed-boundary-1.53716.984615051--%--

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-ports" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200103150010.f2F0AqK53921>