libalpm
Arch Linux Package Manager Library
testdb.c
Go to the documentation of this file.
00001 /*
00002  *  testdb.c : Test a pacman local database for validity
00003  *
00004  *  Copyright (c) 2007 by Aaron Griffin <aaronmgriffin@gmail.com>
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00018  */
00019 
00020 #include <unistd.h>
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <errno.h>
00024 #include <string.h>
00025 #include <dirent.h>
00026 
00027 #include <alpm.h>
00028 #include <alpm_list.h>
00029 
00030 #define BASENAME "testdb"
00031 
00032 alpm_handle_t *handle = NULL;
00033 
00034 static void cleanup(int signum)
00035 {
00036     if(handle && alpm_release(handle) == -1) {
00037         fprintf(stderr, "error releasing alpm\n");
00038     }
00039 
00040     exit(signum);
00041 }
00042 
00043 static void output_cb(alpm_loglevel_t level, const char *fmt, va_list args)
00044 {
00045     if(strlen(fmt)) {
00046         switch(level) {
00047             case ALPM_LOG_ERROR: printf("error: "); break;
00048             case ALPM_LOG_WARNING: printf("warning: "); break;
00049             default: return;
00050         }
00051         vprintf(fmt, args);
00052     }
00053 }
00054 
00055 static int check_localdb_files(void)
00056 {
00057     struct dirent *ent;
00058     const char *dbpath;
00059     char path[4096];
00060     int ret = 0;
00061     DIR *dir;
00062 
00063     dbpath = alpm_option_get_dbpath(handle);
00064     snprintf(path, sizeof(path), "%slocal", dbpath);
00065     if(!(dir = opendir(path))) {
00066         fprintf(stderr, "error : %s : %s\n", path, strerror(errno));
00067         return 1;
00068     }
00069 
00070     while((ent = readdir(dir)) != NULL) {
00071         if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0
00072                 || ent->d_name[0] == '.') {
00073             continue;
00074         }
00075         /* check for known db files in local database */
00076         snprintf(path, sizeof(path), "%slocal/%s/desc", dbpath, ent->d_name);
00077         if(access(path, F_OK)) {
00078             printf("%s: description file is missing\n", ent->d_name);
00079             ret++;
00080         }
00081         snprintf(path, sizeof(path), "%slocal/%s/files", dbpath, ent->d_name);
00082         if(access(path, F_OK)) {
00083             printf("%s: file list is missing\n", ent->d_name);
00084             ret++;
00085         }
00086     }
00087     if(closedir(dir)) {
00088         fprintf(stderr, "error closing dbpath : %s\n", strerror(errno));
00089         return 1;
00090     }
00091 
00092     return ret;
00093 }
00094 
00095 static int check_deps(alpm_list_t *pkglist)
00096 {
00097     alpm_list_t *data, *i;
00098     int ret = 0;
00099     /* check dependencies */
00100     data = alpm_checkdeps(handle, pkglist, NULL, pkglist, 0);
00101     for(i = data; i; i = alpm_list_next(i)) {
00102         alpm_depmissing_t *miss = i->data;
00103         char *depstring = alpm_dep_compute_string(miss->depend);
00104         printf("missing %s dependency for %s\n", depstring, miss->target);
00105         free(depstring);
00106         ret++;
00107     }
00108     FREELIST(data);
00109     return ret;
00110 }
00111 
00112 static int check_conflicts(alpm_list_t *pkglist)
00113 {
00114     alpm_list_t *data, *i;
00115     int ret = 0;
00116     /* check conflicts */
00117     data = alpm_checkconflicts(handle, pkglist);
00118     for(i = data; i; i = i->next) {
00119         alpm_conflict_t *conflict = i->data;
00120         printf("%s conflicts with %s\n",
00121                 conflict->package1, conflict->package2);
00122         ret++;
00123     }
00124     FREELIST(data);
00125     return ret;
00126 }
00127 
00128 struct fileitem {
00129     alpm_file_t *file;
00130     alpm_pkg_t *pkg;
00131 };
00132 
00133 static int fileitem_cmp(const void *p1, const void *p2)
00134 {
00135     const struct fileitem * fi1 = p1;
00136     const struct fileitem * fi2 = p2;
00137     return strcmp(fi1->file->name, fi2->file->name);
00138 }
00139 
00140 static int check_filelists(alpm_list_t *pkglist)
00141 {
00142     alpm_list_t *i;
00143     int ret = 0;
00144     size_t list_size = 4096;
00145     size_t offset = 0, j;
00146     struct fileitem *all_files;
00147     struct fileitem *prev_fileitem = NULL;
00148 
00149     all_files = malloc(list_size * sizeof(struct fileitem));
00150 
00151     for(i = pkglist; i; i = i->next) {
00152         alpm_pkg_t *pkg = i->data;
00153         alpm_filelist_t *filelist = alpm_pkg_get_files(pkg);
00154         for(j = 0; j < filelist->count; j++) {
00155             alpm_file_t *file = filelist->files + j;
00156             /* only add files, not directories, to our big list */
00157             if(file->name[strlen(file->name) - 1] == '/') {
00158                 continue;
00159             }
00160 
00161             /* do we need to reallocate and grow our array? */
00162             if(offset >= list_size) {
00163                 struct fileitem *new_files;
00164                 new_files = realloc(all_files, list_size * 2 * sizeof(struct fileitem));
00165                 if(!new_files) {
00166                     free(all_files);
00167                     return 1;
00168                 }
00169                 all_files = new_files;
00170                 list_size *= 2;
00171             }
00172 
00173             /* we can finally add it to the list */
00174             all_files[offset].file = file;
00175             all_files[offset].pkg = pkg;
00176             offset++;
00177         }
00178     }
00179 
00180     /* now sort the list so we can find duplicates */
00181     qsort(all_files, offset, sizeof(struct fileitem), fileitem_cmp);
00182 
00183     /* do a 'uniq' style check on the list */
00184     for(j = 0; j < offset; j++) {
00185         struct fileitem *fileitem = all_files + j;
00186         if(prev_fileitem && fileitem_cmp(prev_fileitem, fileitem) == 0) {
00187             printf("file owned by %s and %s: %s\n",
00188                     alpm_pkg_get_name(prev_fileitem->pkg),
00189                     alpm_pkg_get_name(fileitem->pkg),
00190                     fileitem->file->name);
00191         }
00192         prev_fileitem = fileitem;
00193     }
00194 
00195     free(all_files);
00196     return ret;
00197 }
00198 
00199 static int check_localdb(void)
00200 {
00201     int ret = 0;
00202     alpm_db_t *db = NULL;
00203     alpm_list_t *pkglist;
00204 
00205     ret = check_localdb_files();
00206     if(ret) {
00207         return ret;
00208     }
00209 
00210     db = alpm_option_get_localdb(handle);
00211     pkglist = alpm_db_get_pkgcache(db);
00212     ret += check_deps(pkglist);
00213     ret += check_conflicts(pkglist);
00214     ret += check_filelists(pkglist);
00215     return ret;
00216 }
00217 
00218 static int check_syncdbs(alpm_list_t *dbnames)
00219 {
00220     int ret = 0;
00221     alpm_db_t *db = NULL;
00222     alpm_list_t *i, *pkglist, *syncpkglist = NULL;
00223     const alpm_siglevel_t level = ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
00224 
00225     for(i = dbnames; i; i = alpm_list_next(i)) {
00226         const char *dbname = i->data;
00227         db = alpm_db_register_sync(handle, dbname, level);
00228         if(db == NULL) {
00229             fprintf(stderr, "error: could not register sync database (%s)\n",
00230                     alpm_strerror(alpm_errno(handle)));
00231             ret = 1;
00232             goto cleanup;
00233         }
00234         pkglist = alpm_db_get_pkgcache(db);
00235         syncpkglist = alpm_list_join(syncpkglist, alpm_list_copy(pkglist));
00236     }
00237     ret += check_deps(syncpkglist);
00238 
00239 cleanup:
00240     alpm_list_free(syncpkglist);
00241     return ret;
00242 }
00243 
00244 static void usage(void)
00245 {
00246     fprintf(stderr, "usage:\n");
00247     fprintf(stderr,
00248             "\t%s [-b <pacman db>]                : check the local database\n", BASENAME);
00249     fprintf(stderr,
00250             "\t%s [-b <pacman db>] core extra ... : check the listed sync databases\n", BASENAME);
00251     exit(1);
00252 }
00253 
00254 int main(int argc, char *argv[])
00255 {
00256     int ret = 0;
00257     alpm_errno_t err;
00258     const char *dbpath = DBPATH;
00259     int a = 1;
00260     alpm_list_t *dbnames = NULL;
00261 
00262     while(a < argc) {
00263         if(strcmp(argv[a], "-b") == 0) {
00264             if(++a < argc) {
00265                 dbpath = argv[a];
00266             } else {
00267                 usage();
00268             }
00269         }   else if(strcmp(argv[a], "-h") == 0 ||
00270                 strcmp(argv[a], "--help") == 0 ) {
00271             usage();
00272         } else {
00273             dbnames = alpm_list_add(dbnames, argv[a]);
00274         }
00275         a++;
00276     }
00277 
00278     handle = alpm_initialize(ROOTDIR, dbpath, &err);
00279     if(!handle) {
00280         fprintf(stderr, "cannot initialize alpm: %s\n", alpm_strerror(err));
00281         return EXIT_FAILURE;
00282     }
00283 
00284     /* let us get log messages from libalpm */
00285     alpm_option_set_logcb(handle, output_cb);
00286 
00287     if(!dbnames) {
00288         ret = check_localdb();
00289     } else {
00290         ret = check_syncdbs(dbnames);
00291         alpm_list_free(dbnames);
00292     }
00293 
00294     cleanup(ret);
00295 }
00296 
00297 /* vim: set ts=2 sw=2 noet: */