This implements FS#13877. Add a new option "-Qk" which checks if all of the files for a given package (or packages) are really on the system (i.e. not accidentally deleted). This can be combined with filters and other display options. It also respects both the --quiet and --verbose flags to give varying levels of output. Based on the original patch by Charly Coste <changaco@laposte.net>, thanks for your work! Signed-off-by: Dan McGee <dan@archlinux.org> --- Some additional notes and example output: dmcgee@galway ~/projects/pacman (master) $ ./src/pacman/pacman -Qkq wpa_supplicant /etc/wpa_supplicant.conf dmcgee@galway ~/projects/pacman (master) $ ./src/pacman/pacman -Qk wpa_supplicant: missing /etc/wpa_supplicant.conf (No such file or directory) wpa_supplicant: 22 total, 1 missing file(s) $ ./src/pacman/pacman -Qkmv Root : / Conf File : /etc/pacman.conf DB Path : /var/lib/pacman/ Cache Dirs: /var/cache/pacman/pkg/ /home/makepkg/packages/ Lock File : /var/lib/pacman/db.lck Log File : /var/log/pacman.log Targets : None bjfilter: 5 total, 0 missing file(s) clearlooks: 51 total, 0 missing file(s) ebtables: 37 total, 0 missing file(s) icc: 1729 total, 0 missing file(s) intel-compilers-common: 104 total, 0 missing file(s) jre_beta: 825 total, 0 missing file(s) kcachegrind: 60 total, 0 missing file(s) libcnbj: 36 total, 0 missing file(s) metasploit3: 16330 total, 0 missing file(s) mixxx: 2072 total, 0 missing file(s) munin-node: 159 total, 0 missing file(s) picasa-beta: 1133 total, 0 missing file(s) pstocanonbj: 24 total, 0 missing file(s) python-markdown: 65 total, 0 missing file(s) rng-tools: 15 total, 0 missing file(s) tkinfo: 9 total, 0 missing file(s) weka: 11 total, 0 missing file(s) OK, that last one looks a bit silly with the paths at the top, doesn't it. Any ideas? I'd be fine with showing the 0 errors lines all the time, it would just require some grep foo for people to screen those out. That way, you can do things like this (note that the output is slightly edited from what this patch will produce, it is showing the output even with 0 missing files): ''''' $ ./src/pacman/pacman -Qiik pacman-git Name : pacman-git Version : 20090715-1 URL : http://www.archlinux.org/pacman/ Licenses : GPL Groups : None Provides : pacman=3.2.2 Depends On : gcc-libs bash libarchive>=2.6.0 libfetch pacman-mirrorlist Optional Deps : fakeroot: for makepkg usage as normal user python: for rankmirrors script usage Required By : pacman-contrib pkgstats Conflicts With : pacman Replaces : None Installed Size : 2036.00 K Packager : Dan McGee <dan@archlinux.org> Architecture : x86_64 Build Date : Wed 15 Jul 2009 09:15:00 PM CDT Install Date : Wed 15 Jul 2009 11:40:07 PM CDT Install Reason : Explicitly installed Install Script : No Description : A library-based package manager with dependency support Backup Files: MODIFIED /etc/pacman.conf MODIFIED /etc/makepkg.conf pacman-git: 114 total, 0 missing file(s) ''''' -Dan doc/pacman.8.txt | 4 ++ src/pacman/conf.h | 1 + src/pacman/pacman.c | 7 ++++- src/pacman/query.c | 83 ++++++++++++++++++++++++++++++++++++++++++++------- 4 files changed, 83 insertions(+), 12 deletions(-) diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt index af85a15..ccff167 100644 --- a/doc/pacman.8.txt +++ b/doc/pacman.8.txt @@ -196,6 +196,10 @@ Query Options[[QO]] '\--info' or '-i' flags will also display the list of backup files and their modification states. +*-k \--check*:: + Check that all files owned by the given package(s) are present on the + system. If packages are not specified, check all installed packages. + *-l, \--list*:: List all files owned by a given package. Multiple packages can be specified on the command line. diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 39802ca..6523d49 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -51,6 +51,7 @@ typedef struct __config_t { unsigned short op_q_search; unsigned short op_q_changelog; unsigned short op_q_upgrade; + unsigned short op_q_check; unsigned short op_s_clean; unsigned short op_s_downloadonly; diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 7f86489..48d45ad 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -108,6 +108,7 @@ static void usage(int op, const char * const myname) printf(_(" -e, --explicit list packages explicitly installed [filter]\n")); printf(_(" -g, --groups view all members of a package group\n")); printf(_(" -i, --info view package information (-ii for backup files)\n")); + printf(_(" -k, --check check that the files owned by the package(s) are present\n")); printf(_(" -l, --list list the contents of the queried package\n")); printf(_(" -m, --foreign list installed packages not found in sync db(s) [filter]\n")); printf(_(" -o, --owns <file> query the package that owns <file>\n")); @@ -345,6 +346,7 @@ static int parseargs(int argc, char *argv[]) {"help", no_argument, 0, 'h'}, {"info", no_argument, 0, 'i'}, {"dbonly", no_argument, 0, 'k'}, + {"check", no_argument, 0, 'k'}, {"list", no_argument, 0, 'l'}, {"foreign", no_argument, 0, 'm'}, {"nosave", no_argument, 0, 'n'}, @@ -473,7 +475,10 @@ static int parseargs(int argc, char *argv[]) case 'g': (config->group)++; break; case 'h': config->help = 1; break; case 'i': (config->op_q_info)++; (config->op_s_info)++; break; - case 'k': config->flags |= PM_TRANS_FLAG_DBONLY; break; + case 'k': + config->flags |= PM_TRANS_FLAG_DBONLY; + config->op_q_check = 1; + break; case 'l': config->op_q_list = 1; break; case 'm': config->op_q_foreign = 1; break; case 'n': config->flags |= PM_TRANS_FLAG_NOSAVE; break; diff --git a/src/pacman/query.c b/src/pacman/query.c index 4997202..b70c713 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -309,8 +309,58 @@ static int filter(pmpkg_t *pkg) return(1); } -static void display(pmpkg_t *pkg) +/* Loop through the packages. For each package, + * loop through files to check if they exist. */ +static int check(pmpkg_t *pkg) { + alpm_list_t *i; + const char *root; + int allfiles = 0, errors = 0; + size_t rootlen; + char f[PATH_MAX]; + + root = alpm_option_get_root(); + rootlen = strlen(root); + if(rootlen + 1 > PATH_MAX) { + /* we are in trouble here */ + pm_printf(PM_LOG_ERROR, _("root path too long\n")); + return(1); + } + strcpy(f, root); + + const char *pkgname = alpm_pkg_get_name(pkg); + for(i = alpm_pkg_get_files(pkg); i; i = alpm_list_next(i)) { + struct stat st; + const char *path = alpm_list_getdata(i); + + if(rootlen + 1 + strlen(path) > PATH_MAX) { + pm_printf(PM_LOG_WARNING, _("file path too long\n")); + continue; + } + strcpy(f + rootlen, path); + allfiles++; + /* use lstat to prevent errors from symlinks */ + if(lstat(f, &st) != 0) { + if(config->quiet) { + fprintf(stderr, "%s %s\n", pkgname, f); + } else { + fprintf(stderr, "%s: missing %s (%s)\n", pkgname, f, strerror(errno)); + } + errors++; + } + } + + if((errors > 0 && !config->quiet) || config->verbose) { + printf("%s: %d total, %d missing file(s)\n", pkgname, allfiles, errors); + } + + return(errors != 0 ? 1 : 0); +} + +static int display(pmpkg_t *pkg) +{ + int ret = 0; + if(config->op_q_info) { if(config->op_q_isfile) { /* omit info that isn't applicable for a file package */ @@ -325,19 +375,25 @@ static void display(pmpkg_t *pkg) if(config->op_q_changelog) { dump_pkg_changelog(pkg); } - if(!config->op_q_info && !config->op_q_list && !config->op_q_changelog) { + if(config->op_q_check) { + ret = check(pkg); + } + if(!config->op_q_info && !config->op_q_list + && !config->op_q_changelog && !config->op_q_check) { if (!config->quiet) { printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); } else { printf("%s\n", alpm_pkg_get_name(pkg)); } } + return(ret); } int pacman_query(alpm_list_t *targets) { int ret = 0; alpm_list_t *i; + pmpkg_t *pkg = NULL; /* First: operations that do not require targets */ @@ -358,12 +414,12 @@ int pacman_query(alpm_list_t *targets) alpm_list_t *sync_dbs = alpm_option_get_syncdbs(); if(sync_dbs == NULL || alpm_list_count(sync_dbs) == 0) { pm_printf(PM_LOG_ERROR, _("no usable package repositories configured.\n")); - return(-1); + return(1); } } /* operations on all packages in the local DB - * valid: no-op (plain -Q), list, info + * valid: no-op (plain -Q), list, info, check * invalid: isfile, owns */ if(targets == NULL) { if(config->op_q_isfile || config->op_q_owns) { @@ -372,12 +428,15 @@ int pacman_query(alpm_list_t *targets) } for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) { - pmpkg_t *pkg = alpm_list_getdata(i); + pkg = alpm_list_getdata(i); if(filter(pkg)) { - display(pkg); + int value = display(pkg); + if(value != 0) { + ret = 1; + } } } - return(0); + return(ret); } /* Second: operations that require target(s) */ @@ -389,10 +448,9 @@ int pacman_query(alpm_list_t *targets) } /* operations on named packages in the local DB - * valid: no-op (plain -Q), list, info */ + * valid: no-op (plain -Q), list, info, check */ for(i = targets; i; i = alpm_list_next(i)) { char *strname = alpm_list_getdata(i); - pmpkg_t *pkg = NULL; if(config->op_q_isfile) { alpm_pkg_load(strname, 1, &pkg); @@ -402,12 +460,15 @@ int pacman_query(alpm_list_t *targets) if(pkg == NULL) { pm_fprintf(stderr, PM_LOG_ERROR, _("package \"%s\" not found\n"), strname); - ret++; + ret = 1; continue; } if(filter(pkg)) { - display(pkg); + int value = display(pkg); + if(value != 0) { + ret = 1; + } } if(config->op_q_isfile) { -- 1.6.3.3