libalpm
Arch Linux Package Manager Library
|
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: */