blob: f482145a33d9fec0ad31fd153b4c0a9d893ce8d3 [file] [log] [blame]
Darin Petkov65b01462010-04-14 13:32:20 -07001// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/*
6 * metrics_library.cc
7 *
8 * Created on: Dec 1, 2009
9 * Author: sosa
10 */
11
12#include "metrics_library.h"
13
14#include <errno.h>
15#include <sys/file.h>
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070016
17#include <cstdarg>
18#include <cstdio>
19#include <cstring>
Darin Petkov65b01462010-04-14 13:32:20 -070020
21#define READ_WRITE_ALL_FILE_FLAGS \
22 (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
23
24static const char kAutotestPath[] = "/tmp/.chromeos-metrics-autotest";
25static const char kChromePath[] = "/tmp/.chromeos-metrics";
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070026static const int32_t kBufferSize = 1024;
Darin Petkov65b01462010-04-14 13:32:20 -070027
28using namespace std;
29
30// TODO(sosa@chromium.org) - use Chromium logger instead of stderr
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070031static void PrintError(const char *message, const char *file,
32 int code) {
Darin Petkov65b01462010-04-14 13:32:20 -070033 const char *kProgramName = "metrics_library";
34 if (code == 0) {
35 fprintf(stderr, "%s: %s\n", kProgramName, message);
36 } else if (file == NULL) {
37 fprintf(stderr, "%s: ", kProgramName);
38 perror(message);
39 } else {
40 fprintf(stderr, "%s: %s: ", kProgramName, file);
41 perror(message);
42 }
43}
44
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070045// Sends message of size length to Chrome and returns true on success.
46static bool SendMessageToChrome(int32_t length, const char *message) {
Darin Petkov65b01462010-04-14 13:32:20 -070047 int chrome_fd = open(kChromePath,
48 O_WRONLY | O_APPEND | O_CREAT,
49 READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070050 // If we failed to open it, return.
Darin Petkov65b01462010-04-14 13:32:20 -070051 if (chrome_fd < 0) {
52 PrintError("open", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070053 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070054 }
55
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070056 // Need to chmod because open flags are anded with umask. Ignore the
57 // exit code -- a chronos process may fail chmoding because the file
58 // has been created by a root process but that should be OK.
59 fchmod(chrome_fd, READ_WRITE_ALL_FILE_FLAGS);
Darin Petkov65b01462010-04-14 13:32:20 -070060
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070061 // Grab an exclusive lock to protect Chrome from truncating
62 // underneath us. Keep the file locked as briefly as possible.
Darin Petkov65b01462010-04-14 13:32:20 -070063 if (flock(chrome_fd, LOCK_EX) < 0) {
64 PrintError("flock", kChromePath, errno);
65 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070066 return false;
Darin Petkov65b01462010-04-14 13:32:20 -070067 }
68
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070069 bool success = true;
70 if (write(chrome_fd, message, length) != length) {
Darin Petkov65b01462010-04-14 13:32:20 -070071 PrintError("write", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070072 success = false;
73 }
Darin Petkov65b01462010-04-14 13:32:20 -070074
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070075 // Release the file lock and close file.
76 if (flock(chrome_fd, LOCK_UN) < 0) {
Darin Petkov65b01462010-04-14 13:32:20 -070077 PrintError("unlock", kChromePath, errno);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070078 success = false;
79 }
Darin Petkov65b01462010-04-14 13:32:20 -070080 close(chrome_fd);
Darin Petkov4fcb2ac2010-04-15 16:40:23 -070081 return success;
82}
83
84// Formats a name/value message for Chrome in buffer and returns the
85// length of the message or a negative value on error.
86//
87// Message format is: | LENGTH(binary) | NAME | \0 | VALUE | \0 |
88//
89// The arbitrary format argument covers the non-LENGTH portion of the
90// message. The caller is responsible to store the \0 character
91// between NAME and VALUE (e.g. "%s%c%d", name, '\0', value).
92static int32_t FormatChromeMessage(int32_t buffer_size, char *buffer,
93 const char *format, ...) {
94 int32_t message_length;
95 size_t len_size = sizeof(message_length);
96
97 // Format the non-LENGTH contents in the buffer by leaving space for
98 // LENGTH at the start of the buffer.
99 va_list args;
100 va_start(args, format);
101 message_length = vsnprintf(&buffer[len_size], buffer_size - len_size,
102 format, args);
103 va_end(args);
104
105 if (message_length < 0) {
106 PrintError("chrome message format error", NULL, 0);
107 return -1;
108 }
109
110 // +1 to account for the trailing \0.
111 message_length += len_size + 1;
112 if (message_length > buffer_size) {
113 PrintError("chrome message too long", NULL, 0);
114 return -1;
115 }
116
117 // Prepend LENGTH to the message.
118 memcpy(buffer, &message_length, len_size);
119 return message_length;
120}
121
122bool MetricsLibrary::SendToAutotest(string name, int value) {
123 FILE *autotest_file = fopen(kAutotestPath, "a+");
124 if (autotest_file == NULL) {
125 PrintError("fopen", kAutotestPath, errno);
126 return false;
127 }
128
129 fprintf(autotest_file, "%s=%d\n", name.c_str(), value);
130 fclose(autotest_file);
131 return true;
132}
133
134bool MetricsLibrary::SendToChrome(string name, int value) {
135 // Format the message.
136 char message[kBufferSize];
137 int32_t message_length =
138 FormatChromeMessage(kBufferSize, message,
139 "%s%c%d", name.c_str(), '\0', value);
140
141 if (message_length < 0)
142 return false;
143
144 // Send the message.
145 return SendMessageToChrome(message_length, message);
Darin Petkov65b01462010-04-14 13:32:20 -0700146}