libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * query.c 00003 * 00004 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include <stdlib.h> 00022 #include <stdio.h> 00023 #include <stdint.h> 00024 #include <limits.h> 00025 #include <string.h> 00026 #include <sys/stat.h> 00027 #include <unistd.h> 00028 #include <errno.h> 00029 00030 #include <alpm.h> 00031 #include <alpm_list.h> 00032 00033 /* pacman */ 00034 #include "pacman.h" 00035 #include "package.h" 00036 #include "conf.h" 00037 #include "util.h" 00038 00039 static char *resolve_path(const char *file) 00040 { 00041 char *str = NULL; 00042 00043 str = calloc(PATH_MAX, sizeof(char)); 00044 if(!str) { 00045 return NULL; 00046 } 00047 00048 if(!realpath(file, str)) { 00049 free(str); 00050 return NULL; 00051 } 00052 00053 return str; 00054 } 00055 00056 /* check if filename exists in PATH */ 00057 static int search_path(char **filename, struct stat *bufptr) 00058 { 00059 char *envpath, *envpathsplit, *path, *fullname; 00060 size_t flen; 00061 00062 if((envpath = getenv("PATH")) == NULL) { 00063 return -1; 00064 } 00065 if((envpath = envpathsplit = strdup(envpath)) == NULL) { 00066 return -1; 00067 } 00068 00069 flen = strlen(*filename); 00070 00071 while((path = strsep(&envpathsplit, ":")) != NULL) { 00072 size_t plen = strlen(path); 00073 00074 /* strip the trailing slash if one exists */ 00075 while(path[plen - 1] == '/') { 00076 path[--plen] = '\0'; 00077 } 00078 00079 fullname = malloc(plen + flen + 2); 00080 if(!fullname) { 00081 free(envpath); 00082 return -1; 00083 } 00084 sprintf(fullname, "%s/%s", path, *filename); 00085 00086 if(lstat(fullname, bufptr) == 0) { 00087 free(*filename); 00088 *filename = fullname; 00089 free(envpath); 00090 return 0; 00091 } 00092 free(fullname); 00093 } 00094 free(envpath); 00095 return -1; 00096 } 00097 00098 static void print_query_fileowner(const char *filename, alpm_pkg_t *info) 00099 { 00100 if(!config->quiet) { 00101 printf(_("%s is owned by %s %s\n"), filename, 00102 alpm_pkg_get_name(info), alpm_pkg_get_version(info)); 00103 } else { 00104 printf("%s\n", alpm_pkg_get_name(info)); 00105 } 00106 } 00107 00108 static int query_fileowner(alpm_list_t *targets) 00109 { 00110 int ret = 0; 00111 char path[PATH_MAX]; 00112 const char *root; 00113 size_t rootlen; 00114 alpm_list_t *t; 00115 alpm_db_t *db_local; 00116 00117 /* This code is here for safety only */ 00118 if(targets == NULL) { 00119 pm_printf(ALPM_LOG_ERROR, _("no file was specified for --owns\n")); 00120 return 1; 00121 } 00122 00123 /* Set up our root path buffer. We only need to copy the location of root in 00124 * once, then we can just overwrite whatever file was there on the previous 00125 * iteration. */ 00126 root = alpm_option_get_root(config->handle); 00127 rootlen = strlen(root); 00128 if(rootlen + 1 > PATH_MAX) { 00129 /* we are in trouble here */ 00130 pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); 00131 return 1; 00132 } 00133 strcpy(path, root); 00134 00135 db_local = alpm_option_get_localdb(config->handle); 00136 00137 for(t = targets; t; t = alpm_list_next(t)) { 00138 char *filename, *dname, *rpath; 00139 const char *bname; 00140 struct stat buf; 00141 alpm_list_t *i; 00142 int found = 0; 00143 00144 filename = strdup(t->data); 00145 00146 if(lstat(filename, &buf) == -1) { 00147 /* if it is not a path but a program name, then check in PATH */ 00148 if(strchr(filename, '/') == NULL) { 00149 if(search_path(&filename, &buf) == -1) { 00150 pm_printf(ALPM_LOG_ERROR, _("failed to find '%s' in PATH: %s\n"), 00151 filename, strerror(errno)); 00152 ret++; 00153 free(filename); 00154 continue; 00155 } 00156 } else { 00157 pm_printf(ALPM_LOG_ERROR, _("failed to read file '%s': %s\n"), 00158 filename, strerror(errno)); 00159 ret++; 00160 free(filename); 00161 continue; 00162 } 00163 } 00164 00165 if(S_ISDIR(buf.st_mode)) { 00166 pm_printf(ALPM_LOG_ERROR, 00167 _("cannot determine ownership of directory '%s'\n"), filename); 00168 ret++; 00169 free(filename); 00170 continue; 00171 } 00172 00173 bname = mbasename(filename); 00174 dname = mdirname(filename); 00175 /* for files in '/', there is no directory name to match */ 00176 if(strcmp(dname, "") == 0) { 00177 rpath = NULL; 00178 } else { 00179 rpath = resolve_path(dname); 00180 00181 if(!rpath) { 00182 pm_printf(ALPM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), 00183 filename, strerror(errno)); 00184 free(filename); 00185 free(dname); 00186 free(rpath); 00187 ret++; 00188 continue; 00189 } 00190 } 00191 free(dname); 00192 00193 for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) { 00194 alpm_pkg_t *info = i->data; 00195 alpm_filelist_t *filelist = alpm_pkg_get_files(info); 00196 size_t j; 00197 00198 for(j = 0; j < filelist->count; j++) { 00199 const alpm_file_t *file = filelist->files + j; 00200 char *ppath, *pdname; 00201 const char *pkgfile = file->name; 00202 00203 /* avoid the costly resolve_path usage if the basenames don't match */ 00204 if(strcmp(mbasename(pkgfile), bname) != 0) { 00205 continue; 00206 } 00207 00208 /* for files in '/', there is no directory name to match */ 00209 if(!rpath) { 00210 print_query_fileowner(filename, info); 00211 found = 1; 00212 continue; 00213 } 00214 00215 if(rootlen + 1 + strlen(pkgfile) > PATH_MAX) { 00216 pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, pkgfile); 00217 } 00218 /* concatenate our file and the root path */ 00219 strcpy(path + rootlen, pkgfile); 00220 00221 pdname = mdirname(path); 00222 ppath = resolve_path(pdname); 00223 free(pdname); 00224 00225 if(ppath && strcmp(ppath, rpath) == 0) { 00226 print_query_fileowner(filename, info); 00227 found = 1; 00228 } 00229 free(ppath); 00230 } 00231 } 00232 if(!found) { 00233 pm_printf(ALPM_LOG_ERROR, _("No package owns %s\n"), filename); 00234 ret++; 00235 } 00236 free(filename); 00237 free(rpath); 00238 } 00239 00240 return ret; 00241 } 00242 00243 /* search the local database for a matching package */ 00244 static int query_search(alpm_list_t *targets) 00245 { 00246 alpm_list_t *i, *searchlist; 00247 int freelist; 00248 alpm_db_t *db_local = alpm_option_get_localdb(config->handle); 00249 00250 /* if we have a targets list, search for packages matching it */ 00251 if(targets) { 00252 searchlist = alpm_db_search(db_local, targets); 00253 freelist = 1; 00254 } else { 00255 searchlist = alpm_db_get_pkgcache(db_local); 00256 freelist = 0; 00257 } 00258 if(searchlist == NULL) { 00259 return 1; 00260 } 00261 00262 for(i = searchlist; i; i = alpm_list_next(i)) { 00263 alpm_list_t *grp; 00264 alpm_pkg_t *pkg = i->data; 00265 00266 if(!config->quiet) { 00267 printf("local/%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); 00268 } else { 00269 fputs(alpm_pkg_get_name(pkg), stdout); 00270 } 00271 00272 00273 if(!config->quiet) { 00274 if((grp = alpm_pkg_get_groups(pkg)) != NULL) { 00275 alpm_list_t *k; 00276 fputs(" (", stdout); 00277 for(k = grp; k; k = alpm_list_next(k)) { 00278 const char *group = k->data; 00279 fputs(group, stdout); 00280 if(alpm_list_next(k)) { 00281 /* only print a spacer if there are more groups */ 00282 putchar(' '); 00283 } 00284 } 00285 putchar(')'); 00286 } 00287 00288 /* we need a newline and initial indent first */ 00289 printf("\n "); 00290 indentprint(alpm_pkg_get_desc(pkg), 4); 00291 } 00292 printf("\n"); 00293 } 00294 00295 /* we only want to free if the list was a search list */ 00296 if(freelist) { 00297 alpm_list_free(searchlist); 00298 } 00299 return 0; 00300 } 00301 00302 static int query_group(alpm_list_t *targets) 00303 { 00304 alpm_list_t *i, *j; 00305 const char *grpname = NULL; 00306 int ret = 0; 00307 alpm_db_t *db_local = alpm_option_get_localdb(config->handle); 00308 00309 if(targets == NULL) { 00310 for(j = alpm_db_get_groupcache(db_local); j; j = alpm_list_next(j)) { 00311 alpm_group_t *grp = j->data; 00312 const alpm_list_t *p; 00313 00314 for(p = grp->packages; p; p = alpm_list_next(p)) { 00315 alpm_pkg_t *pkg = p->data; 00316 printf("%s %s\n", grp->name, alpm_pkg_get_name(pkg)); 00317 } 00318 } 00319 } else { 00320 for(i = targets; i; i = alpm_list_next(i)) { 00321 alpm_group_t *grp; 00322 grpname = i->data; 00323 grp = alpm_db_readgroup(db_local, grpname); 00324 if(grp) { 00325 const alpm_list_t *p; 00326 for(p = grp->packages; p; p = alpm_list_next(p)) { 00327 if(!config->quiet) { 00328 printf("%s %s\n", grpname, 00329 alpm_pkg_get_name(p->data)); 00330 } else { 00331 printf("%s\n", alpm_pkg_get_name(p->data)); 00332 } 00333 } 00334 } else { 00335 pm_printf(ALPM_LOG_ERROR, _("group '%s' was not found\n"), grpname); 00336 ret++; 00337 } 00338 } 00339 } 00340 return ret; 00341 } 00342 00343 static int is_foreign(alpm_pkg_t *pkg) 00344 { 00345 const char *pkgname = alpm_pkg_get_name(pkg); 00346 alpm_list_t *j; 00347 alpm_list_t *sync_dbs = alpm_option_get_syncdbs(config->handle); 00348 00349 int match = 0; 00350 for(j = sync_dbs; j; j = alpm_list_next(j)) { 00351 alpm_db_t *db = j->data; 00352 alpm_pkg_t *findpkg = alpm_db_get_pkg(db, pkgname); 00353 if(findpkg) { 00354 match = 1; 00355 break; 00356 } 00357 } 00358 if(match == 0) { 00359 return 1; 00360 } 00361 return 0; 00362 } 00363 00364 static int is_unrequired(alpm_pkg_t *pkg) 00365 { 00366 alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg); 00367 if(requiredby == NULL) { 00368 return 1; 00369 } 00370 FREELIST(requiredby); 00371 return 0; 00372 } 00373 00374 static int filter(alpm_pkg_t *pkg) 00375 { 00376 /* check if this package was explicitly installed */ 00377 if(config->op_q_explicit && 00378 alpm_pkg_get_reason(pkg) != ALPM_PKG_REASON_EXPLICIT) { 00379 return 0; 00380 } 00381 /* check if this package was installed as a dependency */ 00382 if(config->op_q_deps && 00383 alpm_pkg_get_reason(pkg) != ALPM_PKG_REASON_DEPEND) { 00384 return 0; 00385 } 00386 /* check if this pkg isn't in a sync DB */ 00387 if(config->op_q_foreign && !is_foreign(pkg)) { 00388 return 0; 00389 } 00390 /* check if this pkg is unrequired */ 00391 if(config->op_q_unrequired && !is_unrequired(pkg)) { 00392 return 0; 00393 } 00394 /* check if this pkg is outdated */ 00395 if(config->op_q_upgrade && (alpm_sync_newversion(pkg, 00396 alpm_option_get_syncdbs(config->handle)) == NULL)) { 00397 return 0; 00398 } 00399 return 1; 00400 } 00401 00402 /* Loop through the packages. For each package, 00403 * loop through files to check if they exist. */ 00404 static int check(alpm_pkg_t *pkg) 00405 { 00406 const char *root, *pkgname; 00407 size_t errors = 0; 00408 size_t rootlen; 00409 char f[PATH_MAX]; 00410 alpm_filelist_t *filelist; 00411 size_t i; 00412 00413 root = alpm_option_get_root(config->handle); 00414 rootlen = strlen(root); 00415 if(rootlen + 1 > PATH_MAX) { 00416 /* we are in trouble here */ 00417 pm_printf(ALPM_LOG_ERROR, _("path too long: %s%s\n"), root, ""); 00418 return 1; 00419 } 00420 strcpy(f, root); 00421 00422 pkgname = alpm_pkg_get_name(pkg); 00423 filelist = alpm_pkg_get_files(pkg); 00424 for(i = 0; i < filelist->count; i++) { 00425 const alpm_file_t *file = filelist->files + i; 00426 struct stat st; 00427 const char *path = file->name; 00428 00429 if(rootlen + 1 + strlen(path) > PATH_MAX) { 00430 pm_printf(ALPM_LOG_WARNING, _("path too long: %s%s\n"), root, path); 00431 continue; 00432 } 00433 strcpy(f + rootlen, path); 00434 /* use lstat to prevent errors from symlinks */ 00435 if(lstat(f, &st) != 0) { 00436 if(config->quiet) { 00437 printf("%s %s\n", pkgname, f); 00438 } else { 00439 pm_printf(ALPM_LOG_WARNING, "%s: %s (%s)\n", 00440 pkgname, f, strerror(errno)); 00441 } 00442 errors++; 00443 } 00444 } 00445 00446 if(!config->quiet) { 00447 printf(_n("%s: %jd total file, ", "%s: %jd total files, ", 00448 (unsigned long)filelist->count), pkgname, (intmax_t)filelist->count); 00449 printf(_n("%jd missing file\n", "%jd missing files\n", 00450 (unsigned long)errors), (intmax_t)errors); 00451 } 00452 00453 return (errors != 0 ? 1 : 0); 00454 } 00455 00456 static int display(alpm_pkg_t *pkg) 00457 { 00458 int ret = 0; 00459 00460 if(config->op_q_info) { 00461 if(config->op_q_isfile) { 00462 dump_pkg_full(pkg, 0); 00463 } else { 00464 dump_pkg_full(pkg, config->op_q_info > 1); 00465 } 00466 } 00467 if(config->op_q_list) { 00468 dump_pkg_files(pkg, config->quiet); 00469 } 00470 if(config->op_q_changelog) { 00471 dump_pkg_changelog(pkg); 00472 } 00473 if(config->op_q_check) { 00474 ret = check(pkg); 00475 } 00476 if(!config->op_q_info && !config->op_q_list 00477 && !config->op_q_changelog && !config->op_q_check) { 00478 if(!config->quiet) { 00479 printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); 00480 } else { 00481 printf("%s\n", alpm_pkg_get_name(pkg)); 00482 } 00483 } 00484 return ret; 00485 } 00486 00487 int pacman_query(alpm_list_t *targets) 00488 { 00489 int ret = 0; 00490 int match = 0; 00491 alpm_list_t *i; 00492 alpm_pkg_t *pkg = NULL; 00493 alpm_db_t *db_local; 00494 00495 /* First: operations that do not require targets */ 00496 00497 /* search for a package */ 00498 if(config->op_q_search) { 00499 ret = query_search(targets); 00500 return ret; 00501 } 00502 00503 /* looking for groups */ 00504 if(config->group) { 00505 ret = query_group(targets); 00506 return ret; 00507 } 00508 00509 if(config->op_q_foreign || config->op_q_upgrade) { 00510 if(check_syncdbs(1, 1)) { 00511 return 1; 00512 } 00513 } 00514 00515 db_local = alpm_option_get_localdb(config->handle); 00516 00517 /* operations on all packages in the local DB 00518 * valid: no-op (plain -Q), list, info, check 00519 * invalid: isfile, owns */ 00520 if(targets == NULL) { 00521 if(config->op_q_isfile || config->op_q_owns) { 00522 pm_printf(ALPM_LOG_ERROR, _("no targets specified (use -h for help)\n")); 00523 return 1; 00524 } 00525 00526 for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) { 00527 pkg = i->data; 00528 if(filter(pkg)) { 00529 int value = display(pkg); 00530 if(value != 0) { 00531 ret = 1; 00532 } 00533 match = 1; 00534 } 00535 } 00536 if(!match) { 00537 ret = 1; 00538 } 00539 return ret; 00540 } 00541 00542 /* Second: operations that require target(s) */ 00543 00544 /* determine the owner of a file */ 00545 if(config->op_q_owns) { 00546 ret = query_fileowner(targets); 00547 return ret; 00548 } 00549 00550 /* operations on named packages in the local DB 00551 * valid: no-op (plain -Q), list, info, check */ 00552 for(i = targets; i; i = alpm_list_next(i)) { 00553 const char *strname = i->data; 00554 00555 if(config->op_q_isfile) { 00556 alpm_pkg_load(config->handle, strname, 1, 0, &pkg); 00557 } else { 00558 pkg = alpm_db_get_pkg(db_local, strname); 00559 } 00560 00561 if(pkg == NULL) { 00562 switch(alpm_errno(config->handle)) { 00563 case ALPM_ERR_PKG_NOT_FOUND: 00564 pm_printf(ALPM_LOG_ERROR, 00565 _("package '%s' was not found\n"), strname); 00566 if(!config->op_q_isfile && access(strname, R_OK) == 0) { 00567 pm_printf(ALPM_LOG_WARNING, 00568 _("'%s' is a file, you might want to use %s.\n"), 00569 strname, "-p/--file"); 00570 } 00571 break; 00572 default: 00573 pm_printf(ALPM_LOG_ERROR, 00574 _("could not load package '%s': %s\n"), strname, 00575 alpm_strerror(alpm_errno(config->handle))); 00576 break; 00577 } 00578 ret = 1; 00579 continue; 00580 } 00581 00582 if(filter(pkg)) { 00583 int value = display(pkg); 00584 if(value != 0) { 00585 ret = 1; 00586 } 00587 match = 1; 00588 } 00589 00590 if(config->op_q_isfile) { 00591 alpm_pkg_free(pkg); 00592 pkg = NULL; 00593 } 00594 } 00595 00596 if(!match) { 00597 ret = 1; 00598 } 00599 00600 return ret; 00601 } 00602 00603 /* vim: set ts=2 sw=2 noet: */