[pacman-dev] Database consistency checking

Xavier shiningxc at gmail.com
Fri Jul 20 17:09:55 EDT 2007


On Wed, Jul 18, 2007 at 05:23:50PM +0200, Xavier wrote:
> By calling check_depends(pkg) on all package from the local database, I have
> enough information for computing all requiredby fields. But the problem is I
> can't store these.
> 
> In this part :
> 139       if(deppkg && alpm_depcmp(deppkg, dep)) {
> 140         found = 1; 
> 
> I would need to do something like :
> alpm_list_t *newrqdby = alpm_pkg_get_newrequiredby(deppkg);
> newrqdby = alpm_list_add(newrqdby, pkgname).
> 
> And when everything is done, compare old and new requiredby lists for each
> package. But I don't see any way to do that.
> (that would also make my check_requiredby() function useless).
> 
> Or maybe I could achieve the same (in a less efficient way) with a more
> complicated check_requiredby function.
> But on the algorithm side, this check_requiredby function is stupid because
> everything is done by check_depends..

Well, here is the original testdb file I already posted, plus the above
suggestion implemented (which allows more efficient code + the detection
of wrongly duplicated requiredby).

But that really sucks of course :( I hate C.. There is really no clean way
to do what I want?
I guess I could create a new pkg structure type only for that testdb file,
for storing the new requiredby field, or having a second local pkgcache
(duplicating every package and storing them in a list, and modifying the
 requiredy field there).
But the code is already huge compared to the simple things it does.
It would make it even more huge :p

I think I'll start learning C# so I can hack on Maelstorm when its released
:)
Or maybe the best thing here would be to use libalpm bindings with an higher
level language.

-------------- next part --------------
>From 87a2748822e95d65d358fd81f31825f10e1c256c Mon Sep 17 00:00:00 2001
From: Chantry Xavier <shiningxc at gmail.com>
Date: Tue, 17 Jul 2007 14:21:01 +0200
Subject: [PATCH] Add testdb util for finding inconsistencies in the database.

Signed-off-by: Chantry Xavier <shiningxc at gmail.com>
---
 src/util/.gitignore  |    1 +
 src/util/Makefile.am |    5 +-
 src/util/testdb.c    |  201 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 206 insertions(+), 1 deletions(-)
 create mode 100644 src/util/testdb.c

diff --git a/src/util/.gitignore b/src/util/.gitignore
index 96ef10d..3668880 100644
--- a/src/util/.gitignore
+++ b/src/util/.gitignore
@@ -2,3 +2,4 @@
 .libs
 vercmp
 testpkg
+testdb
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 0c48f10..676b442 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -1,4 +1,4 @@
-bin_PROGRAMS = vercmp testpkg
+bin_PROGRAMS = vercmp testpkg testdb
 
 INCLUDES = -I$(top_srcdir)/lib/libalpm
 
@@ -10,4 +10,7 @@ vercmp_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la
 testpkg_SOURCES = testpkg.c
 testpkg_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la
 
+testdb_SOURCES = testdb.c
+testdb_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la
+
 # vim:set ts=2 sw=2 noet:
diff --git a/src/util/testdb.c b/src/util/testdb.c
new file mode 100644
index 0000000..9d7a8d8
--- /dev/null
+++ b/src/util/testdb.c
@@ -0,0 +1,201 @@
+/*
+ *  testdb.c : Test a pacman local database for validity
+ *
+ *  Copyright (c) 2007 by Aaron Griffin <aaronmgriffin at gmail.com>
+ * 
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
+ *  USA.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <libgen.h>
+
+#include <alpm.h>
+#include <alpm_list.h>
+
+#define PATH_MAX 1024
+
+static void cleanup(int signum) {
+  if(alpm_release() == -1) {
+    fprintf(stderr, "error releasing alpm: %s\n", alpm_strerror(pm_errno));
+  }
+
+  exit(signum);
+}
+
+void output_cb(pmloglevel_t level, char *fmt, va_list args)
+{
+  if(strlen(fmt)) {
+    switch(level) {
+      case PM_LOG_ERROR: printf("error: "); break;
+      case PM_LOG_WARNING: printf("warning: "); break;
+      default: return;
+    }
+    vprintf(fmt, args);
+    printf("\n");
+  }
+}
+
+int db_test(char *dbpath)
+{
+  struct dirent *ent;
+  char path[PATH_MAX];
+  struct stat buf;
+  int ret = 0;
+
+  DIR *dir;
+  
+  if(!(dir = opendir(dbpath))) {
+    fprintf(stderr, "error : %s : %s\n", dbpath, strerror(errno));
+    return(1);
+  }
+
+  while ((ent = readdir(dir)) != NULL) {
+    if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) {
+      continue;
+    }
+    /* check for desc, depends, and files */
+    snprintf(path, PATH_MAX, "%s/%s/desc", dbpath, ent->d_name);
+    if(stat(path, &buf)) {
+      printf("%s: description file is missing\n", ent->d_name);
+      ret++;
+    }
+    snprintf(path, PATH_MAX, "%s/%s/depends", dbpath, ent->d_name);
+    if(stat(path, &buf)) {
+      printf("%s: dependency file is missing\n", ent->d_name);
+      ret++;
+    }
+    snprintf(path, PATH_MAX, "%s/%s/files", dbpath, ent->d_name);
+    if(stat(path, &buf)) {
+      printf("%s: file list is missing\n", ent->d_name);
+      ret++;
+    }
+  }
+  return(ret);
+}
+
+int check_requiredby(pmpkg_t *pkg)
+{
+  int retval = 0;
+  alpm_list_t *reqs, *deps;
+  const char *pkgname = alpm_pkg_get_name(pkg);
+
+  for(reqs = alpm_pkg_get_requiredby(pkg); reqs; reqs = reqs->next) {
+    int found = 0;
+    char *reqname = reqs->data;
+    pmpkg_t *reqpkg = alpm_db_get_pkg(alpm_option_get_localdb(), reqname);
+    for(deps = alpm_pkg_get_depends(reqpkg); deps && !found; deps = deps->next) {
+      pmdepend_t *dep = alpm_splitdep(deps->data);
+      found = alpm_depcmp(pkg, dep);
+      free(dep);
+    }
+    if(!found) {
+      printf("%s : wrong requiredby field %s\n", pkgname, reqname);
+      retval++;
+    }
+  }
+  return(retval);
+}
+
+int check_depends(pmpkg_t *pkg)
+{
+  int retval = 0;
+  alpm_list_t *i, *j;
+  pmdb_t *db = alpm_option_get_localdb();
+
+  const char *pkgname = alpm_pkg_get_name(pkg);
+  alpm_list_t *depends = alpm_pkg_get_depends(pkg);
+
+  for(i = depends; i; i = i->next) {
+    if(!i->data) {
+      continue;
+    }
+    char *depname = i->data;
+    pmdepend_t* dep = alpm_splitdep(depname);
+    if(dep == NULL) {
+      continue;
+    }
+    int found = 0;
+    for(j = alpm_db_getpkgcache(db); j; j = j->next) {
+      pmpkg_t *deppkg = j->data;
+      if(deppkg && alpm_depcmp(deppkg, dep)) {
+        found = 1;
+        alpm_list_t *rqdby = alpm_pkg_get_requiredby(deppkg);
+        const char *deppkgname = alpm_pkg_get_name(deppkg);
+        if(!alpm_list_find_str(rqdby, pkgname)) {
+          printf("%s : missing requiredby field %s\n", deppkgname, pkgname);
+          retval++;
+        }
+      }
+    }
+    if(!found) {
+      printf("%s : missing dependency %s\n", pkgname, depname);
+      retval++;
+    }
+    free(dep);
+  }
+  return(retval);
+}
+
+int main(int argc, char **argv)
+{
+  int retval = 0; /* default = false */
+  pmdb_t *db = NULL;
+  char dbpath[PATH_MAX];
+  alpm_list_t *i;
+
+  if(argc != 2) {
+    fprintf(stderr, "usage: %s <pacman db>\n", basename(argv[0]));
+    return(1);
+  }
+
+  snprintf(dbpath, PATH_MAX, "%s/local", argv[1]);
+
+  retval = db_test(dbpath);
+  if(retval) {
+    exit(retval);
+  }
+
+  if(alpm_initialize() == -1) {
+    fprintf(stderr, "cannot initialize alpm: %s\n", alpm_strerror(pm_errno));
+    return(1);
+  }
+
+  /* let us get log messages from libalpm */
+  alpm_option_set_logcb(output_cb);
+
+  alpm_option_set_dbpath(argv[1]);
+
+  db = alpm_db_register("local");
+  if(db == NULL) {
+    fprintf(stderr, "error: could not register 'local' database (%s)\n",
+        alpm_strerror(pm_errno));
+    cleanup(EXIT_FAILURE);
+  }
+
+  for(i = alpm_db_getpkgcache(db); i; i = alpm_list_next(i)) {
+    pmpkg_t *pkg = alpm_list_getdata(i);
+    retval += check_requiredby(pkg);
+    retval += check_depends(pkg);
+  }
+
+  cleanup(retval);
+}
-- 
1.5.2.4

-------------- next part --------------
>From c0f445808507029391522e7a1524cec82dfc59f0 Mon Sep 17 00:00:00 2001
From: Chantry Xavier <shiningxc at gmail.com>
Date: Fri, 20 Jul 2007 17:02:59 +0200
Subject: [PATCH] testdb : slightly more efficient implementation.

This also detects wrongly duplicated requiredby entries.
---
 lib/libalpm/alpm.h    |    2 +
 lib/libalpm/package.c |   20 +++++++++
 lib/libalpm/package.h |    1 +
 src/util/testdb.c     |  109 +++++++++++++++++++++++++++++++++++++------------
 4 files changed, 105 insertions(+), 27 deletions(-)

diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 9e641f3..cdd6311 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -211,6 +211,8 @@ alpm_list_t *alpm_pkg_get_licenses(pmpkg_t *pkg);
 alpm_list_t *alpm_pkg_get_groups(pmpkg_t *pkg);
 alpm_list_t *alpm_pkg_get_depends(pmpkg_t *pkg);
 alpm_list_t *alpm_pkg_get_requiredby(pmpkg_t *pkg);
+alpm_list_t *alpm_pkg_get_newrequiredby(pmpkg_t *pkg);
+void alpm_pkg_set_newrequiredby(pmpkg_t *pkg, alpm_list_t *newrequiredby);
 alpm_list_t *alpm_pkg_get_conflicts(pmpkg_t *pkg);
 alpm_list_t *alpm_pkg_get_provides(pmpkg_t *pkg);
 alpm_list_t *alpm_pkg_get_replaces(pmpkg_t *pkg);
diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
index d5eca20..437647f 100644
--- a/lib/libalpm/package.c
+++ b/lib/libalpm/package.c
@@ -519,6 +519,25 @@ alpm_list_t SYMEXPORT *alpm_pkg_get_requiredby(pmpkg_t *pkg)
 	return pkg->requiredby;
 }
 
+void SYMEXPORT alpm_pkg_set_newrequiredby(pmpkg_t *pkg, alpm_list_t *newrequiredby)
+{
+	pkg->newrequiredby = newrequiredby;
+}
+
+alpm_list_t SYMEXPORT *alpm_pkg_get_newrequiredby(pmpkg_t *pkg)
+{
+	ALPM_LOG_FUNC;
+
+	/* Sanity checks */
+	ASSERT(handle != NULL, return(NULL));
+	ASSERT(pkg != NULL, return(NULL));
+
+	if(pkg->origin == PKG_FROM_CACHE && !(pkg->infolevel & INFRQ_DEPENDS)) {
+		_alpm_db_read(pkg->data, pkg, INFRQ_DEPENDS);
+	}
+	return pkg->newrequiredby;
+}
+
 alpm_list_t SYMEXPORT *alpm_pkg_get_conflicts(pmpkg_t *pkg)
 {
 	ALPM_LOG_FUNC;
@@ -746,6 +765,7 @@ pmpkg_t *_alpm_pkg_dup(pmpkg_t *pkg)
 	newpkg->licenses    = alpm_list_strdup(alpm_pkg_get_licenses(pkg));
 	/*newpkg->desc_localized = alpm_list_strdup(pkg->desc_localized);*/
 	newpkg->requiredby = alpm_list_strdup(alpm_pkg_get_requiredby(pkg));
+	newpkg->newrequiredby = alpm_list_strdup(alpm_pkg_get_newrequiredby(pkg));
 	newpkg->conflicts  = alpm_list_strdup(alpm_pkg_get_conflicts(pkg));
 	newpkg->files      = alpm_list_strdup(alpm_pkg_get_files(pkg));
 	newpkg->backup     = alpm_list_strdup(alpm_pkg_get_backup(pkg));
diff --git a/lib/libalpm/package.h b/lib/libalpm/package.h
index f704ab9..8c89807 100644
--- a/lib/libalpm/package.h
+++ b/lib/libalpm/package.h
@@ -75,6 +75,7 @@ struct __pmpkg_t {
 	alpm_list_t *backup;
 	alpm_list_t *depends;
 	alpm_list_t *requiredby;
+	alpm_list_t *newrequiredby;
 	alpm_list_t *conflicts;
 	alpm_list_t *provides;
 	/* internal */
diff --git a/src/util/testdb.c b/src/util/testdb.c
index 9d7a8d8..8040894 100644
--- a/src/util/testdb.c
+++ b/src/util/testdb.c
@@ -92,27 +92,17 @@ int db_test(char *dbpath)
   return(ret);
 }
 
-int check_requiredby(pmpkg_t *pkg)
+static void display_list(alpm_list_t *list)
 {
-  int retval = 0;
-  alpm_list_t *reqs, *deps;
-  const char *pkgname = alpm_pkg_get_name(pkg);
-
-  for(reqs = alpm_pkg_get_requiredby(pkg); reqs; reqs = reqs->next) {
-    int found = 0;
-    char *reqname = reqs->data;
-    pmpkg_t *reqpkg = alpm_db_get_pkg(alpm_option_get_localdb(), reqname);
-    for(deps = alpm_pkg_get_depends(reqpkg); deps && !found; deps = deps->next) {
-      pmdepend_t *dep = alpm_splitdep(deps->data);
-      found = alpm_depcmp(pkg, dep);
-      free(dep);
-    }
-    if(!found) {
-      printf("%s : wrong requiredby field %s\n", pkgname, reqname);
-      retval++;
+  alpm_list_t *i;
+  for(i = list; i; i = i->next) {
+    char *s = i->data;
+    if(i->next) {
+      printf("%s, ", s);
+    } else {
+      printf("%s\n", s);
     }
   }
-  return(retval);
 }
 
 int check_depends(pmpkg_t *pkg)
@@ -123,6 +113,7 @@ int check_depends(pmpkg_t *pkg)
 
   const char *pkgname = alpm_pkg_get_name(pkg);
   alpm_list_t *depends = alpm_pkg_get_depends(pkg);
+  alpm_list_t *missing_deps = NULL;
 
   for(i = depends; i; i = i->next) {
     if(!i->data) {
@@ -138,23 +129,66 @@ int check_depends(pmpkg_t *pkg)
       pmpkg_t *deppkg = j->data;
       if(deppkg && alpm_depcmp(deppkg, dep)) {
         found = 1;
-        alpm_list_t *rqdby = alpm_pkg_get_requiredby(deppkg);
-        const char *deppkgname = alpm_pkg_get_name(deppkg);
-        if(!alpm_list_find_str(rqdby, pkgname)) {
-          printf("%s : missing requiredby field %s\n", deppkgname, pkgname);
-          retval++;
-        }
+        alpm_list_t *newrqdby = alpm_pkg_get_newrequiredby(deppkg);
+        newrqdby = alpm_list_add(newrqdby, (char *)pkgname);
+        alpm_pkg_set_newrequiredby(deppkg, newrqdby);
       }
     }
     if(!found) {
-      printf("%s : missing dependency %s\n", pkgname, depname);
-      retval++;
+      missing_deps = alpm_list_add(missing_deps, depname);
     }
     free(dep);
   }
+  if(missing_deps) {
+    retval++;
+    printf("missing deps for %s : ", pkgname);
+    display_list(missing_deps);
+  }
   return(retval);
 }
 
+int _alpm_str_cmp(const void *s1, const void *s2)
+{
+	return(strcmp(s1, s2));
+}
+
+static void diff(alpm_list_t *list1, alpm_list_t *list2,
+    alpm_list_t **result1, alpm_list_t **result2)
+{
+  list1 = alpm_list_msort(list1, alpm_list_count(list1), _alpm_str_cmp);
+  list2 = alpm_list_msort(list2, alpm_list_count(list2), _alpm_str_cmp);
+
+  alpm_list_t *i = list1;
+  alpm_list_t *j = list2;
+
+  while(i || j) {
+    char *s1 = NULL;
+    char *s2 = NULL;
+    int n;
+    if(i && !j) {
+      n = -1;
+    } else if(!i && j) {
+      n = 1;
+    } else {
+      s1 = i->data;
+      s2 = j->data;
+      n = strcmp(s1, s2);
+    }
+    if(n < 0) {
+      s1 = i->data;
+      *result1 = alpm_list_add(*result1, s1);
+      i = i->next;
+    } else if (n > 0) {
+      s2 = j->data;
+      *result2 = alpm_list_add(*result2, s2);
+      j = j->next;
+    } else {
+      i = i->next;
+      j = j->next;
+    }
+  }
+}
+
 int main(int argc, char **argv)
 {
   int retval = 0; /* default = false */
@@ -191,11 +225,32 @@ int main(int argc, char **argv)
     cleanup(EXIT_FAILURE);
   }
 
+  printf("CHECKING DEPENDENCIES\n\n");
   for(i = alpm_db_getpkgcache(db); i; i = alpm_list_next(i)) {
     pmpkg_t *pkg = alpm_list_getdata(i);
-    retval += check_requiredby(pkg);
     retval += check_depends(pkg);
   }
 
+  printf("\nCHECKING REQUIREDBY\n\n");
+  for(i = alpm_db_getpkgcache(db); i; i = alpm_list_next(i)) {
+    alpm_list_t *extra = NULL, *missing = NULL;
+    pmpkg_t *pkg = alpm_list_getdata(i);
+    alpm_list_t *rqdby = alpm_pkg_get_requiredby(pkg);
+    alpm_list_t *newrqdby = alpm_pkg_get_newrequiredby(pkg);
+    diff(rqdby, newrqdby, &extra, &missing);
+    if(extra || missing) {
+      retval++;
+      const char *pkgname = alpm_pkg_get_name(pkg);
+      if(extra) {
+        printf("wrong requiredby for %s : ", pkgname);
+        display_list(extra);
+      }
+      if(missing) {
+        printf("missing requiredby for %s : ", pkgname);
+        display_list(missing);
+      }
+    }
+  }
+
   cleanup(retval);
 }
-- 
1.5.2.4



More information about the pacman-dev mailing list