[pacman-dev] Database consistency checking

Xavier shiningxc at gmail.com
Tue Jul 17 05:59:25 EDT 2007


On Mon, Jul 16, 2007 at 04:47:12PM +0200, Xavier wrote:
> Yes indeed, that's what I was thinking about.
> Maybe it could also look for broken dependencies also (mostly breakage caused
> by --nodeps).
> The problem is I'm not sure it fits perfectly in libalpm. Maybe it should
> rather be an external tool, using libalpm (in src/util/).
> That should be enough at least for checking, but for writing to the database
> in order to correct inconsistencies, I'm not sure..

Finally, I think that isn't needed, errors can be corrected by just
reinstalling the packages.
So just displaying the missing dependencies, and extra / missing
requiredby fields should be enough.

I tried putting the code in an external tool (starting from the existing
testpkg.c one), but that's stupid, because that code is already in libalpm.
The dependencies / requiredby handling functions should be refactored a bit
so that it could be used in this case, and then either they should be
exported so that it could be used in an external tool, or there should be a
little function in libalpm for checking for inconsistencies.

I'll still attach the prog as a reference, because I think it might be interesting.
But I really hate the code : copy / pasting is the lazy way, and imo that's one 
of the reason the current code base is so ugly and messy.


/*
 *  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 = opendir(dbpath);

  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);
    }
    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++;
    }
  }
  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);
}




More information about the pacman-dev mailing list