Index: src/platform/metrics/metrics_library.cc |
diff --git a/src/platform/metrics/metrics_library.cc b/src/platform/metrics/metrics_library.cc |
index 99ad61640af785026fd1f64307e1495a8320d3ae..f482145a33d9fec0ad31fd153b4c0a9d893ce8d3 100644 |
--- a/src/platform/metrics/metrics_library.cc |
+++ b/src/platform/metrics/metrics_library.cc |
@@ -13,21 +13,23 @@ |
#include <errno.h> |
#include <sys/file.h> |
-#include <string.h> |
-#include <stdio.h> |
+ |
+#include <cstdarg> |
+#include <cstdio> |
+#include <cstring> |
#define READ_WRITE_ALL_FILE_FLAGS \ |
(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) |
static const char kAutotestPath[] = "/tmp/.chromeos-metrics-autotest"; |
static const char kChromePath[] = "/tmp/.chromeos-metrics"; |
-static const int kBufferSize = 4096; |
+static const int32_t kBufferSize = 1024; |
using namespace std; |
// TODO(sosa@chromium.org) - use Chromium logger instead of stderr |
-void MetricsLibrary::PrintError(const char *message, const char *file, |
- int code) { |
+static void PrintError(const char *message, const char *file, |
+ int code) { |
const char *kProgramName = "metrics_library"; |
if (code == 0) { |
fprintf(stderr, "%s: %s\n", kProgramName, message); |
@@ -40,61 +42,105 @@ void MetricsLibrary::PrintError(const char *message, const char *file, |
} |
} |
-void MetricsLibrary::SendToAutotest(string name, string value) { |
- FILE *autotest_file = fopen(kAutotestPath, "a+"); |
- if (autotest_file == NULL) { |
- PrintError("fopen", kAutotestPath, errno); |
- return; |
- } |
- |
- fprintf(autotest_file, "%s=%s\n", name.c_str(), value.c_str()); |
- fclose(autotest_file); |
-} |
- |
-void MetricsLibrary::SendToChrome(string name, string value) { |
+// Sends message of size length to Chrome and returns true on success. |
+static bool SendMessageToChrome(int32_t length, const char *message) { |
int chrome_fd = open(kChromePath, |
O_WRONLY | O_APPEND | O_CREAT, |
READ_WRITE_ALL_FILE_FLAGS); |
- // If we failed to open it, return |
+ // If we failed to open it, return. |
if (chrome_fd < 0) { |
PrintError("open", kChromePath, errno); |
- return; |
+ return false; |
} |
- // Need to chmod because open flags are anded with umask. |
- if (fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS) < 0) { |
- PrintError("fchmod", kChromePath, errno); |
- close(chrome_fd); |
- return; |
- } |
+ // Need to chmod because open flags are anded with umask. Ignore the |
+ // exit code -- a chronos process may fail chmoding because the file |
+ // has been created by a root process but that should be OK. |
+ fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS); |
- // Grab an exclusive lock to protect Chrome from truncating underneath us |
+ // Grab an exclusive lock to protect Chrome from truncating |
+ // underneath us. Keep the file locked as briefly as possible. |
if (flock(chrome_fd, LOCK_EX) < 0) { |
PrintError("flock", kChromePath, errno); |
close(chrome_fd); |
- return; |
+ return false; |
} |
- // Message format is: LENGTH (binary), NAME, VALUE |
- char message[kBufferSize]; |
- char *curr_ptr = message; |
- int32_t message_length = |
- name.length() + value.length() + 2 + sizeof(message_length); |
- if (message_length > static_cast<int32_t>(sizeof(message))) |
- PrintError("name/value too long", NULL, 0); |
- |
- // Make sure buffer is blanked |
- memset(message, 0, sizeof(message)); |
- memcpy(curr_ptr, &message_length, sizeof(message_length)); |
- curr_ptr += sizeof(message_length); |
- strncpy(curr_ptr, name.c_str(), name.length()); |
- curr_ptr += name.length() + 1; |
- strncpy(curr_ptr, value.c_str(), value.length()); |
- if (write(chrome_fd, message, message_length) != message_length) |
+ bool success = true; |
+ if (write(chrome_fd, message, length) != length) { |
PrintError("write", kChromePath, errno); |
+ success = false; |
+ } |
- // Release the file lock and close file |
- if (flock(chrome_fd, LOCK_UN) < 0) |
+ // Release the file lock and close file. |
+ if (flock(chrome_fd, LOCK_UN) < 0) { |
PrintError("unlock", kChromePath, errno); |
+ success = false; |
+ } |
close(chrome_fd); |
+ return success; |
+} |
+ |
+// Formats a name/value message for Chrome in buffer and returns the |
+// length of the message or a negative value on error. |
+// |
+// Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 | |
+// |
+// The arbitrary format argument covers the non-LENGTH portion of the |
+// message. The caller is responsible to store the \0 character |
+// between NAME and VALUE (e.g. "%s%c%d", name, '\0', value). |
+static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer, |
+ const char *format, ...) { |
+ int32_t message_length; |
+ size_t len_size = sizeof(message_length); |
+ |
+ // Format the non-LENGTH contents in the buffer by leaving space for |
+ // LENGTH at the start of the buffer. |
+ va_list args; |
+ va_start(args, format); |
+ message_length = vsnprintf(&buffer[len_size], buffer_size - len_size, |
+ format, args); |
+ va_end(args); |
+ |
+ if (message_length < 0) { |
+ PrintError("chrome message format error", NULL, 0); |
+ return -1; |
+ } |
+ |
+ // +1 to account for the trailing \0. |
+ message_length += len_size + 1; |
+ if (message_length > buffer_size) { |
+ PrintError("chrome message too long", NULL, 0); |
+ return -1; |
+ } |
+ |
+ // Prepend LENGTH to the message. |
+ memcpy(buffer, &message_length, len_size); |
+ return message_length; |
+} |
+ |
+bool MetricsLibrary::SendToAutotest(string name, int value) { |
+ FILE *autotest_file = fopen(kAutotestPath, "a+"); |
+ if (autotest_file == NULL) { |
+ PrintError("fopen", kAutotestPath, errno); |
+ return false; |
+ } |
+ |
+ fprintf(autotest_file, "%s=%d\n", name.c_str(), value); |
+ fclose(autotest_file); |
+ return true; |
+} |
+ |
+bool MetricsLibrary::SendToChrome(string name, int value) { |
+ // Format the message. |
+ char message[kBufferSize]; |
+ int32_t message_length = |
+ FormatChromeMessage(kBufferSize, message, |
+ "%s%c%d", name.c_str(), '\0', value); |
+ |
+ if (message_length < 0) |
+ return false; |
+ |
+ // Send the message. |
+ return SendMessageToChrome(message_length, message); |
} |