This is the mail archive of the
cygwin-patches
mailing list for the Cygwin project.
[PATCH] cygcheck -m, --check-mtimes option
- From: Christian Franke <Christian dot Franke at t-online dot de>
- To: cygwin-patches at cygwin dot com
- Date: Thu, 07 Aug 2014 22:15:25 +0200
- Subject: [PATCH] cygcheck -m, --check-mtimes option
- Authentication-results: sourceware.org; auth=none
Attached is an experimental patch which adds -m,
--check-mtimes[=SECONDS] option to cygcheck. It provides an IMO useful
heuristics to find files possibly modified after installation.
"cygcheck -c -m" prints the number of files with st_mtime >
INSTALL_TIME+SECONDS. INSTALL_TIME is the st_mtime of the
/etc/setup/PACKAGE.lst.gz file.
With -v, the affected path names are printed. The optional parameter
SECONDS defaults to 600 to hide files modified by postinstall scripts.
Documentation update and changelog entry are still missing.
Christian
diff --git a/winsup/utils/cygcheck.cc b/winsup/utils/cygcheck.cc
index 465bc78..7a1b532 100644
--- a/winsup/utils/cygcheck.cc
+++ b/winsup/utils/cygcheck.cc
@@ -61,7 +61,7 @@ typedef __int64 longlong;
#endif
/* In dump_setup.cc */
-void dump_setup (int, char **, bool);
+void dump_setup (int, char **, bool, int);
void package_find (int, char **);
void package_list (int, char **);
/* In bloda.cc */
@@ -2169,7 +2169,7 @@ usage (FILE * stream, int status)
{
fprintf (stream, "\
Usage: cygcheck [-v] [-h] PROGRAM\n\
- cygcheck -c [-d] [PACKAGE]\n\
+ cygcheck -c [-d] [-m] [PACKAGE]\n\
cygcheck -s [-r] [-v] [-h]\n\
cygcheck -k\n\
cygcheck -f FILE [FILE]...\n\
@@ -2188,6 +2188,8 @@ At least one command option or a PROGRAM is required, as shown above.\n\
-c, --check-setup show installed version of PACKAGE and verify integrity\n\
(or for all installed packages if none specified)\n\
-d, --dump-only just list packages, do not verify (with -c)\n\
+ -m, --check-mtimes[=SECONDS]\n\
+ check for files newer than install time (with -c)\n\
-s, --sysinfo produce diagnostic system information (implies -c)\n\
-r, --registry also scan registry for Cygwin settings (with -s)\n\
-k, --keycheck perform a keyboard check session (must be run from a\n\
@@ -2224,6 +2226,7 @@ Note: -c, -f, and -l only report on packages that are currently installed. To\n\
struct option longopts[] = {
{"check-setup", no_argument, NULL, 'c'},
{"dump-only", no_argument, NULL, 'd'},
+ {"check-mtimes", optional_argument, NULL, 'm'},
{"sysinfo", no_argument, NULL, 's'},
{"registry", no_argument, NULL, 'r'},
{"verbose", no_argument, NULL, 'v'},
@@ -2240,7 +2243,7 @@ struct option longopts[] = {
{0, no_argument, NULL, 0}
};
-static char opts[] = "cdsrvkflphV";
+static char opts[] = "cdmsrvkflphV";
static void
print_version ()
@@ -2326,6 +2329,8 @@ main (int argc, char **argv)
bool ok = true;
load_cygwin (argc, argv);
+ int check_mtimes_offset = -1;
+
/* Need POSIX sorting while parsing args, but don't forget the
user's original environment. */
char *posixly = getenv ("POSIXLY_CORRECT");
@@ -2343,6 +2348,9 @@ main (int argc, char **argv)
case 'd':
dump_only = 1;
break;
+ case 'm':
+ check_mtimes_offset = (optarg ? atoi(optarg) : 600);
+ break;
case 'r':
registry = 1;
break;
@@ -2437,7 +2445,7 @@ main (int argc, char **argv)
}
if (check_setup)
- dump_setup (verbose, argv, !dump_only);
+ dump_setup (verbose, argv, !dump_only, check_mtimes_offset);
else if (find_package)
package_find (verbose, argv);
else if (list_package)
@@ -2456,7 +2464,7 @@ main (int argc, char **argv)
if (!check_setup)
{
puts ("");
- dump_setup (verbose, NULL, !dump_only);
+ dump_setup (verbose, NULL, !dump_only, check_mtimes_offset);
}
if (!givehelp)
diff --git a/winsup/utils/dump_setup.cc b/winsup/utils/dump_setup.cc
index 002c91d..0d3b2c5 100644
--- a/winsup/utils/dump_setup.cc
+++ b/winsup/utils/dump_setup.cc
@@ -25,6 +25,12 @@ details. */
#include "path.h"
#include <zlib.h>
+/* From ../cygwin/hires.h: */
+/* 100ns difference between Windows and UNIX timebase. */
+#define FACTOR (0x19db1ded53e8000LL)
+/* # of 100ns intervals per second. */
+#define NSPERSEC 10000000LL
+
static int package_len = 20;
static unsigned int version_len = 10;
@@ -297,6 +303,7 @@ simple_nt_stat (const char *filename, struct stat *st)
{
st->st_mode = (fbi.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
? S_IFDIR : S_IFREG;
+ st->st_mtime = (fbi.LastWriteTime.QuadPart - FACTOR) / NSPERSEC;
return 0;
}
if (status == STATUS_OBJECT_PATH_NOT_FOUND
@@ -331,7 +338,7 @@ directory_exists (int verbose, char *filename, char *package)
}
static bool
-file_exists (int verbose, char *filename, const char *alt, char *package)
+file_exists (int verbose, char *filename, const char *alt, char *package, time_t *mtime = NULL)
{
struct stat status;
if (simple_nt_stat(cygpath("/", filename, NULL), &status) &&
@@ -346,17 +353,27 @@ file_exists (int verbose, char *filename, const char *alt, char *package)
printf ("File type mismatch: /%s from package %s\n", filename, package);
return false;
}
+
+ if (mtime)
+ *mtime = status.st_mtime;
return true;
}
static gzFile
-open_package_list (char *package)
+open_package_list (char *package, time_t *install_time = NULL)
{
char filelist[MAX_PATH + 1] = "/etc/setup/";
strcat (strcat (filelist, package), ".lst.gz");
if (!file_exists (false, filelist + 1, NULL, NULL))
return NULL;
+ if (install_time)
+ {
+ struct stat status;
+ if (!simple_nt_stat (cygpath ("/", filelist, NULL), &status))
+ *install_time = status.st_mtime;
+ }
+
gzFile fp;
#ifndef ZLIB_VERSION
fp = NULL;
@@ -370,9 +387,11 @@ open_package_list (char *package)
}
static bool
-check_package_files (int verbose, char *package)
+check_package_files (int verbose, char *package, int *newcnt, int check_mtimes_offset)
{
- gzFile fp = open_package_list (package);
+ time_t install_time = -1;
+ gzFile fp = open_package_list (package,
+ (check_mtimes_offset >= 0 ? &install_time : NULL));
if (!fp)
{
if (verbose)
@@ -385,6 +404,7 @@ check_package_files (int verbose, char *package)
while (gzgets (fp, buf, MAX_PATH))
{
char *filename = strtok(buf, "\n");
+ time_t mtime = -1;
if (*filename == '/')
++filename;
@@ -398,14 +418,36 @@ check_package_files (int verbose, char *package)
}
else if (!strncmp (filename, "etc/postinstall/", 16))
{
- if (!file_exists (verbose, filename, ".done", package))
+ if (!file_exists (verbose, filename, ".done", package, &mtime))
result = false;
}
else
{
- if (!file_exists (verbose, filename, ".lnk", package))
+ if (!file_exists (verbose, filename, ".lnk", package, &mtime))
result = false;
}
+
+ if (check_mtimes_offset >= 0 && mtime > 0 && install_time > 0
+ && mtime > install_time + check_mtimes_offset)
+ {
+ (*newcnt)++;
+
+ if (verbose)
+ {
+ int diff = (int)(mtime - install_time);
+ const char * unit;
+ if (diff < 60)
+ unit = "second";
+ else if (diff < 60*60)
+ diff /= 60, unit = "minute";
+ else if (diff < 60*60*24)
+ diff /= 60*60, unit = "hour";
+ else
+ diff /= 60*60*24, unit = "day";
+ printf ("Newer file: /%s from package %s (written %d %s%s after installation)\n",
+ filename, package, diff, unit, (diff == 1 ? "" : "s"));
+ }
+ }
}
gzclose (fp);
@@ -481,7 +523,7 @@ get_packages (char **argv)
}
void
-dump_setup (int verbose, char **argv, bool check_files)
+dump_setup (int verbose, char **argv, bool check_files, int check_mtimes_offset)
{
pkgver *packages = get_packages(argv);
@@ -505,10 +547,19 @@ dump_setup (int verbose, char **argv, bool check_files)
for (int i = 0; packages[i].name; i++)
{
if (check_files)
- printf ("%-*s %-*s%s\n", package_len, packages[i].name,
- version_len, packages[i].ver,
- check_package_files (verbose, packages[i].name)
- ? " OK" : " Incomplete");
+ {
+ int newcnt = 0;
+ bool ok = check_package_files (verbose, packages[i].name, &newcnt,
+ check_mtimes_offset);
+
+ char newmsg[32] = "";
+ if (newcnt)
+ snprintf (newmsg, sizeof(newmsg), "(%d newer)", newcnt);
+
+ printf ("%-*s %-*s %-*s%s\n", package_len, packages[i].name,
+ version_len, packages[i].ver, (newmsg[0] ? 12 : 2),
+ (ok ? "OK" : "Incomplete"), newmsg);
+ }
else
printf ("%-*s %s\n", package_len, packages[i].name, packages[i].ver);
fflush(stdout);