libalpm
Arch Linux Package Manager Library
query.c
Go to the documentation of this file.
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: */