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 <[email protected]>, thanks
for your work!

Signed-off-by: Dan McGee <[email protected]>
---

Some additional notes and example output:

dmc...@galway ~/projects/pacman (master)
$ ./src/pacman/pacman -Qkq
wpa_supplicant /etc/wpa_supplicant.conf

dmc...@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 <[email protected]>
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

_______________________________________________
pacman-dev mailing list
[email protected]
http://www.archlinux.org/mailman/listinfo/pacman-dev

Reply via email to