metrics: Add guest mode detection to metrics library and client
Change-Id: I2c27bd999330395ba3568820ea76198b202bd7f4
BUG=7203
TEST=Verify metrics_client -c and -g toggling consent and guest mode.
Review URL: http://codereview.chromium.org/3571009
diff --git a/metrics/metrics_client.cc b/metrics/metrics_client.cc
index e85c4d4..ce22e98 100644
--- a/metrics/metrics_client.cc
+++ b/metrics/metrics_client.cc
@@ -7,63 +7,29 @@
#include "metrics_library.h"
-int main(int argc, char** argv) {
- bool send_to_autotest = false;
- bool send_to_chrome = true;
- bool send_enum = false;
- bool secs_to_msecs = false;
- int name_index = 1;
- bool print_usage = false;
+void ShowUsage() {
+ fprintf(stderr,
+ "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n"
+ " metrics_client [-ab] -e name sample max\n"
+ " metrics_client [-cg]\n"
+ "\n"
+ " default: send metric with integer values to Chrome only\n"
+ " |min| > 0, |min| <= sample < |max|\n"
+ " -a: send metric (name/sample) to Autotest only\n"
+ " -b: send metric to both Chrome and Autotest\n"
+ " -c: return exit status 0 if user consents to stats, 1 otherwise\n"
+ " -e: send linear/enumeration histogram data\n"
+ " -g: return exit status 0 if machine in guest mode, 1 otherwise\n"
+ " -t: convert sample from double seconds to int milliseconds\n");
+ exit(1);
+}
- if (argc >= 3) {
- // Parse arguments
- int flag;
- while ((flag = getopt(argc, argv, "abet")) != -1) {
- switch (flag) {
- case 'a':
- send_to_autotest = true;
- send_to_chrome = false;
- break;
- case 'b':
- send_to_chrome = true;
- send_to_autotest = true;
- break;
- case 'e':
- send_enum = true;
- break;
- case 't':
- secs_to_msecs = true;
- break;
- default:
- print_usage = true;
- break;
- }
- }
- name_index = optind;
- } else {
- print_usage = true;
- }
-
- int num_args = send_enum ? 3 : 5;
- if ((name_index + num_args) != argc ||
- (send_enum && secs_to_msecs)) {
- print_usage = true;
- }
-
- if (print_usage) {
- fprintf(stderr,
- "Usage: metrics_client [-ab] [-t] name sample min max nbuckets\n"
- " metrics_client [-ab] -e name sample max\n"
- "\n"
- " default: send metric with integer values to Chrome only\n"
- " |min| > 0, |min| <= sample < |max|\n"
- " -a: send metric (name/sample) to Autotest only\n"
- " -b: send metric to both Chrome and Autotest\n"
- " -e: send linear/enumeration histogram data\n"
- " -t: convert sample from double seconds to int milliseconds\n");
- return 1;
- }
-
+static int SendStats(char* argv[],
+ int name_index,
+ bool send_enum,
+ bool secs_to_msecs,
+ bool send_to_autotest,
+ bool send_to_chrome) {
const char* name = argv[name_index];
int sample;
if (secs_to_msecs) {
@@ -92,3 +58,89 @@
}
return 0;
}
+
+static int HasConsent() {
+ MetricsLibrary metrics_lib;
+ metrics_lib.Init();
+ return metrics_lib.AreMetricsEnabled() ? 0 : 1;
+}
+
+static int IsGuestMode() {
+ MetricsLibrary metrics_lib;
+ metrics_lib.Init();
+ return metrics_lib.IsGuestMode() ? 0 : 1;
+}
+
+int main(int argc, char** argv) {
+ enum Mode {
+ kModeSendStats,
+ kModeHasConsent,
+ kModeIsGuestMode
+ } mode = kModeSendStats;
+ bool send_to_autotest = false;
+ bool send_to_chrome = true;
+ bool send_enum = false;
+ bool secs_to_msecs = false;
+ bool print_usage = false;
+
+ // Parse arguments
+ int flag;
+ while ((flag = getopt(argc, argv, "abcegt")) != -1) {
+ switch (flag) {
+ case 'a':
+ mode = kModeSendStats;
+ send_to_autotest = true;
+ send_to_chrome = false;
+ break;
+ case 'b':
+ mode = kModeSendStats;
+ send_to_chrome = true;
+ send_to_autotest = true;
+ break;
+ case 'c':
+ mode = kModeHasConsent;
+ break;
+ case 'e':
+ send_enum = true;
+ break;
+ case 'g':
+ mode = kModeIsGuestMode;
+ break;
+ case 't':
+ secs_to_msecs = true;
+ break;
+ default:
+ print_usage = true;
+ break;
+ }
+ }
+ int name_index = optind;
+
+ int expected_args = 0;
+ if (mode == kModeSendStats)
+ expected_args = send_enum ? 3 : 5;
+
+ if ((name_index + expected_args) != argc) {
+ ShowUsage();
+ }
+
+ switch(mode) {
+ case kModeSendStats:
+ if (send_enum && secs_to_msecs) {
+ ShowUsage();
+ }
+ return SendStats(argv,
+ name_index,
+ send_enum,
+ secs_to_msecs,
+ send_to_autotest,
+ send_to_chrome);
+ case kModeHasConsent:
+ return HasConsent();
+ case kModeIsGuestMode:
+ return IsGuestMode();
+ default:
+ ShowUsage();
+ return 0;
+ }
+}
diff --git a/metrics/metrics_daemon.cc b/metrics/metrics_daemon.cc
index 2f62330..a5059d9 100644
--- a/metrics/metrics_daemon.cc
+++ b/metrics/metrics_daemon.cc
@@ -210,7 +210,6 @@
DeleteFrequencyCounters();
ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
- ConfigureCrashFrequencyReporter(kMetricAnyCrashesDailyName);
ConfigureCrashFrequencyReporter(kMetricAnyCrashesWeeklyName);
ConfigureCrashFrequencyReporter(kMetricKernelCrashesDailyName);
ConfigureCrashFrequencyReporter(kMetricKernelCrashesWeeklyName);
diff --git a/metrics/metrics_library.cc b/metrics/metrics_library.cc
index 3e3987d..f720972 100644
--- a/metrics/metrics_library.cc
+++ b/metrics/metrics_library.cc
@@ -44,13 +44,84 @@
: uma_events_file_(NULL),
consent_file_(kConsentFile) {}
+// We take buffer and buffer_size as parameters in order to simplify testing
+// of various alignments of the |device_name| with |buffer_size|.
+bool MetricsLibrary::IsDeviceMounted(const char* device_name,
+ const char* mounts_file,
+ char* buffer,
+ int buffer_size,
+ bool* result) {
+ if (buffer == NULL || buffer_size < 1)
+ return false;
+ int mounts_fd = open(mounts_file, O_RDONLY);
+ if (mounts_fd < 0)
+ return false;
+ // match_offset describes:
+ // -1 -- not beginning of line
+ // 0..strlen(device_name)-1 -- this offset in device_name is next to match
+ // strlen(device_name) -- matched full name, just need a space.
+ int match_offset = 0;
+ bool match = false;
+ while (!match) {
+ int read_size = read(mounts_fd, buffer, buffer_size);
+ if (read_size <= 0) {
+ if (errno == -EINTR)
+ continue;
+ break;
+ }
+ for (int i = 0; i < read_size; ++i) {
+ if (buffer[i] == '\n') {
+ match_offset = 0;
+ continue;
+ }
+ if (match_offset < 0) {
+ continue;
+ }
+ if (device_name[match_offset] == '\0') {
+ if (buffer[i] == ' ') {
+ match = true;
+ break;
+ }
+ match_offset = -1;
+ continue;
+ }
+
+ if (buffer[i] == device_name[match_offset]) {
+ ++match_offset;
+ } else {
+ match_offset = -1;
+ }
+ }
+ }
+ close(mounts_fd);
+ *result = match;
+ return true;
+}
+
+bool MetricsLibrary::IsGuestMode() {
+ char buffer[256];
+ bool result = false;
+ if (!IsDeviceMounted("guestfs",
+ "/proc/mounts",
+ buffer,
+ sizeof(buffer),
+ &result)) {
+ return false;
+ }
+ return result;
+}
+
bool MetricsLibrary::AreMetricsEnabled() {
static struct stat stat_buffer;
time_t this_check_time = time(NULL);
if (this_check_time != cached_enabled_time_) {
cached_enabled_time_ = this_check_time;
- cached_enabled_ = (stat(consent_file_, &stat_buffer) >= 0);
+ if (stat(consent_file_, &stat_buffer) >= 0 &&
+ !IsGuestMode())
+ cached_enabled_ = true;
+ else
+ cached_enabled_ = false;
}
return cached_enabled_;
}
diff --git a/metrics/metrics_library.h b/metrics/metrics_library.h
index 52da94d..fb31c20 100644
--- a/metrics/metrics_library.h
+++ b/metrics/metrics_library.h
@@ -27,6 +27,9 @@
// Initializes the library.
void Init();
+ // Returns whether or not the machine is running in guest mode.
+ bool IsGuestMode();
+
// Returns whether or not metrics collection is enabled.
bool AreMetricsEnabled();
@@ -75,9 +78,18 @@
FRIEND_TEST(MetricsLibraryTest, AreMetricsEnabled);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessage);
FRIEND_TEST(MetricsLibraryTest, FormatChromeMessageTooLong);
+ FRIEND_TEST(MetricsLibraryTest, IsDeviceMounted);
FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
+ // Sets |*result| to whether or not the |mounts_file| indicates that
+ // the |device_name| is currently mounted. Uses |buffer| of
+ // |buffer_size| to read the file. Returns false if any error.
+ bool IsDeviceMounted(const char* device_name,
+ const char* mounts_file,
+ char* buffer, int buffer_size,
+ bool* result);
+
// Sends message of size |length| to Chrome for transport to UMA and
// returns true on success.
bool SendMessageToChrome(int32_t length, const char* message);
diff --git a/metrics/metrics_library_test.cc b/metrics/metrics_library_test.cc
index cbf4cce..0cd695b 100644
--- a/metrics/metrics_library_test.cc
+++ b/metrics/metrics_library_test.cc
@@ -11,8 +11,8 @@
#include "metrics_library.h"
static const FilePath kTestUMAEventsFile("test-uma-events");
-
static const char kTestConsent[] = "test-consent";
+static const char kTestMounts[] = "test-mounts";
static void SetMetricsEnabled(bool enabled) {
if (enabled)
@@ -35,6 +35,8 @@
}
virtual void TearDown() {
+ file_util::Delete(FilePath(kTestConsent), false);
+ file_util::Delete(FilePath(kTestMounts), false);
file_util::Delete(kTestUMAEventsFile, false);
}
@@ -44,6 +46,65 @@
MetricsLibrary lib_;
};
+TEST_F(MetricsLibraryTest, IsDeviceMounted) {
+ static const char kTestContents[] =
+ "0123456789abcde 0123456789abcde\nguestfs foo bar\n";
+ char buffer[1024];
+ int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 };
+ bool result;
+ EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
+ "nonexistent",
+ buffer,
+ 1,
+ &result));
+ ASSERT_TRUE(file_util::WriteFile(FilePath(kTestMounts),
+ kTestContents,
+ strlen(kTestContents)));
+ EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
+ kTestMounts,
+ buffer,
+ 0,
+ &result));
+ for (size_t i = 0; i < arraysize(block_sizes); ++i) {
+ EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde",
+ kTestMounts,
+ buffer,
+ block_sizes[i],
+ &result));
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(lib_.IsDeviceMounted("guestfs",
+ kTestMounts,
+ buffer,
+ block_sizes[i],
+ &result));
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(lib_.IsDeviceMounted("0123456",
+ kTestMounts,
+ buffer,
+ block_sizes[i],
+ &result));
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(lib_.IsDeviceMounted("9abcde",
+ kTestMounts,
+ buffer,
+ block_sizes[i],
+ &result));
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(lib_.IsDeviceMounted("foo",
+ kTestMounts,
+ buffer,
+ block_sizes[i],
+ &result));
+ EXPECT_FALSE(result);
+ EXPECT_TRUE(lib_.IsDeviceMounted("bar",
+ kTestMounts,
+ buffer,
+ block_sizes[i],
+ &result));
+ EXPECT_FALSE(result);
+ }
+}
+
TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
SetMetricsEnabled(false);
EXPECT_FALSE(lib_.AreMetricsEnabled());