Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(199)

Side by Side Diff: chrome/browser/chromeos/system_access.cc

Issue 7324017: Split SystemAccess into TimezoneSettings, StatisticsProvider, and SyslogsProvider. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: address comments Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium 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 #include "chrome/browser/chromeos/system_access.h"
6
7 #include "base/command_line.h"
8 #include "base/file_path.h"
9 #include "base/file_util.h"
10 #include "base/logging.h"
11 #include "base/observer_list.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/singleton.h"
14 #include "base/string_util.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/chromeos/name_value_pairs_parser.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "content/browser/browser_thread.h"
19
20 namespace chromeos { // NOLINT
21
22 namespace { // NOLINT
23
24 // The filepath to the timezone file that symlinks to the actual timezone file.
25 const char kTimezoneSymlink[] = "/var/lib/timezone/localtime";
26 const char kTimezoneSymlink2[] = "/var/lib/timezone/localtime2";
27
28 // The directory that contains all the timezone files. So for timezone
29 // "US/Pacific", the actual timezone file is: "/usr/share/zoneinfo/US/Pacific"
30 const char kTimezoneFilesDir[] = "/usr/share/zoneinfo/";
31
32 // The system command that returns the hardware class.
33 const char kHardwareClassKey[] = "hardware_class";
34 const char* kHardwareClassTool[] = { "crossystem", "hwid" };
35 const char kUnknownHardwareClass[] = "unknown";
36
37 // Command to get machine hardware info and key/value delimiters.
38 // /tmp/machine-info is generated by platform/init/chromeos_startup.
39 const char* kMachineHardwareInfoTool[] = { "cat", "/tmp/machine-info" };
40 const char kMachineHardwareInfoEq[] = "=";
41 const char kMachineHardwareInfoDelim[] = " \n";
42
43 // Command to get machine OS info and key/value delimiters.
44 const char* kMachineOSInfoTool[] = { "cat", "/etc/lsb-release" };
45 const char kMachineOSInfoEq[] = "=";
46 const char kMachineOSInfoDelim[] = "\n";
47
48 // Command to get VPD info and key/value delimiters.
49 const char* kVpdTool[] = { "cat", "/var/log/vpd_2.0.txt" };
50 const char kVpdEq[] = "=";
51 const char kVpdDelim[] = "\n";
52
53 // Fallback time zone ID used in case of an unexpected error.
54 const char kFallbackTimeZoneId[] = "America/Los_Angeles";
55
56 const char kSysLogsScript[] =
57 "/usr/share/userfeedback/scripts/sysinfo_script_runner";
58 const char kBzip2Command[] =
59 "/bin/bzip2";
60 const char kMultilineQuote[] = "\"\"\"";
61 const char kNewLineChars[] = "\r\n";
62 const char kInvalidLogEntry[] = "<invalid characters in log entry>";
63 const char kEmptyLogEntry[] = "<no value>";
64
65 const char kContextFeedback[] = "feedback";
66 const char kContextSysInfo[] = "sysinfo";
67 const char kContextNetwork[] = "network";
68
69 // Reads a key from the input string erasing the read values + delimiters read
70 // from the initial string
71 std::string ReadKey(std::string* data) {
72 size_t equal_sign = data->find("=");
73 if (equal_sign == std::string::npos)
74 return std::string("");
75 std::string key = data->substr(0, equal_sign);
76 data->erase(0, equal_sign);
77 if (data->size() > 0) {
78 // erase the equal to sign also
79 data->erase(0,1);
80 return key;
81 }
82 return std::string();
83 }
84
85 // Reads a value from the input string; erasing the read values from
86 // the initial string; detects if the value is multiline and reads
87 // accordingly
88 std::string ReadValue(std::string* data) {
89 // Trim the leading spaces and tabs. In order to use a multi-line
90 // value, you have to place the multi-line quote on the same line as
91 // the equal sign.
92 //
93 // Why not use TrimWhitespace? Consider the following input:
94 //
95 // KEY1=
96 // KEY2=VALUE
97 //
98 // If we use TrimWhitespace, we will incorrectly trim the new line
99 // and assume that KEY1's value is "KEY2=VALUE" rather than empty.
100 TrimString(*data, " \t", data);
101
102 // If multiline value
103 if (StartsWithASCII(*data, std::string(kMultilineQuote), false)) {
104 data->erase(0, strlen(kMultilineQuote));
105 size_t next_multi = data->find(kMultilineQuote);
106 if (next_multi == std::string::npos) {
107 // Error condition, clear data to stop further processing
108 data->erase();
109 return std::string();
110 }
111 std::string value = data->substr(0, next_multi);
112 data->erase(0, next_multi + 3);
113 return value;
114 } else { // single line value
115 size_t endl_pos = data->find_first_of(kNewLineChars);
116 // if we don't find a new line, we just return the rest of the data
117 std::string value = data->substr(0, endl_pos);
118 data->erase(0, endl_pos);
119 return value;
120 }
121 }
122
123 // Returns a map of system log keys and values.
124 //
125 // Parameters:
126 // temp_filename: This is an out parameter that holds the name of a file in
127 // /tmp that contains the system logs in a KEY=VALUE format.
128 // If this parameter is NULL, system logs are not retained on
129 // the filesystem after this call completes.
130 // context: This is an in parameter specifying what context should be
131 // passed to the syslog collection script; currently valid
132 // values are "sysinfo" or "feedback"; in case of an invalid
133 // value, the script will currently default to "sysinfo"
134
135 LogDictionaryType* GetSystemLogs(FilePath* zip_file_name,
136 const std::string& context) {
137 // Create the temp file, logs will go here
138 FilePath temp_filename;
139
140 if (!file_util::CreateTemporaryFile(&temp_filename))
141 return NULL;
142
143 std::string cmd = std::string(kSysLogsScript) + " " + context + " >> " +
144 temp_filename.value();
145
146 // Ignore the return value - if the script execution didn't work
147 // stderr won't go into the output file anyway.
148 if (system(cmd.c_str()) == -1)
149 LOG(WARNING) << "Command " << cmd << " failed to run";
150
151 // Compress the logs file if requested.
152 if (zip_file_name) {
153 cmd = std::string(kBzip2Command) + " -c " + temp_filename.value() + " > " +
154 zip_file_name->value();
155 if (system(cmd.c_str()) == -1)
156 LOG(WARNING) << "Command " << cmd << " failed to run";
157 }
158 // Read logs from the temp file
159 std::string data;
160 bool read_success = file_util::ReadFileToString(temp_filename,
161 &data);
162 // if we were using an internal temp file, the user does not need the
163 // logs to stay past the ReadFile call - delete the file
164 file_util::Delete(temp_filename, false);
165
166 if (!read_success)
167 return NULL;
168
169 // Parse the return data into a dictionary
170 LogDictionaryType* logs = new LogDictionaryType();
171 while (data.length() > 0) {
172 std::string key = ReadKey(&data);
173 TrimWhitespaceASCII(key, TRIM_ALL, &key);
174 if (!key.empty()) {
175 std::string value = ReadValue(&data);
176 if (IsStringUTF8(value)) {
177 TrimWhitespaceASCII(value, TRIM_ALL, &value);
178 if (value.empty())
179 (*logs)[key] = kEmptyLogEntry;
180 else
181 (*logs)[key] = value;
182 } else {
183 LOG(WARNING) << "Invalid characters in system log entry: " << key;
184 (*logs)[key] = kInvalidLogEntry;
185 }
186 } else {
187 // no more keys, we're done
188 break;
189 }
190 }
191
192 return logs;
193 }
194
195 class SystemAccessImpl : public SystemAccess {
196 public:
197 // SystemAccess.implementation:
198 virtual const icu::TimeZone& GetTimezone();
199 virtual void SetTimezone(const icu::TimeZone& timezone);
200 virtual bool GetMachineStatistic(const std::string& name,
201 std::string* result);
202 virtual void AddObserver(Observer* observer);
203 virtual void RemoveObserver(Observer* observer);
204
205 virtual Handle RequestSyslogs(
206 bool compress_logs,
207 SyslogsContext context,
208 CancelableRequestConsumerBase* consumer,
209 ReadCompleteCallback* callback);
210
211 // Reads system logs, compresses content if requested.
212 // Called from FILE thread.
213 void ReadSyslogs(
214 scoped_refptr<CancelableRequest<ReadCompleteCallback> > request,
215 bool compress_logs,
216 SyslogsContext context);
217
218 // Loads compressed logs and writes into |zip_content|.
219 void LoadCompressedLogs(const FilePath& zip_file,
220 std::string* zip_content);
221
222 static SystemAccessImpl* GetInstance();
223
224 private:
225 friend struct DefaultSingletonTraits<SystemAccessImpl>;
226
227 SystemAccessImpl();
228
229 // Updates the machine statistcs by examining the system.
230 void UpdateMachineStatistics();
231
232 // Gets syslogs context string from the enum value.
233 const char* GetSyslogsContextString(SyslogsContext context);
234
235 scoped_ptr<icu::TimeZone> timezone_;
236 ObserverList<Observer> observers_;
237 NameValuePairsParser::NameValueMap machine_info_;
238
239 DISALLOW_COPY_AND_ASSIGN(SystemAccessImpl);
240 };
241
242 std::string GetTimezoneIDAsString() {
243 // Look at kTimezoneSymlink, see which timezone we are symlinked to.
244 char buf[256];
245 const ssize_t len = readlink(kTimezoneSymlink, buf,
246 sizeof(buf)-1);
247 if (len == -1) {
248 LOG(ERROR) << "GetTimezoneID: Cannot read timezone symlink "
249 << kTimezoneSymlink;
250 return std::string();
251 }
252
253 std::string timezone(buf, len);
254 // Remove kTimezoneFilesDir from the beginning.
255 if (timezone.find(kTimezoneFilesDir) != 0) {
256 LOG(ERROR) << "GetTimezoneID: Timezone symlink is wrong "
257 << timezone;
258 return std::string();
259 }
260
261 return timezone.substr(strlen(kTimezoneFilesDir));
262 }
263
264 void SetTimezoneIDFromString(const std::string& id) {
265 // Change the kTimezoneSymlink symlink to the path for this timezone.
266 // We want to do this in an atomic way. So we are going to create the symlink
267 // at kTimezoneSymlink2 and then move it to kTimezoneSymlink
268
269 FilePath timezone_symlink(kTimezoneSymlink);
270 FilePath timezone_symlink2(kTimezoneSymlink2);
271 FilePath timezone_file(kTimezoneFilesDir + id);
272
273 // Make sure timezone_file exists.
274 if (!file_util::PathExists(timezone_file)) {
275 LOG(ERROR) << "SetTimezoneID: Cannot find timezone file "
276 << timezone_file.value();
277 return;
278 }
279
280 // Delete old symlink2 if it exists.
281 file_util::Delete(timezone_symlink2, false);
282
283 // Create new symlink2.
284 if (symlink(timezone_file.value().c_str(),
285 timezone_symlink2.value().c_str()) == -1) {
286 LOG(ERROR) << "SetTimezoneID: Unable to create symlink "
287 << timezone_symlink2.value() << " to " << timezone_file.value();
288 return;
289 }
290
291 // Move symlink2 to symlink.
292 if (!file_util::ReplaceFile(timezone_symlink2, timezone_symlink)) {
293 LOG(ERROR) << "SetTimezoneID: Unable to move symlink "
294 << timezone_symlink2.value() << " to "
295 << timezone_symlink.value();
296 }
297 }
298
299 const icu::TimeZone& SystemAccessImpl::GetTimezone() {
300 return *timezone_.get();
301 }
302
303 void SystemAccessImpl::SetTimezone(const icu::TimeZone& timezone) {
304 timezone_.reset(timezone.clone());
305 icu::UnicodeString unicode;
306 timezone.getID(unicode);
307 std::string id;
308 UTF16ToUTF8(unicode.getBuffer(), unicode.length(), &id);
309 VLOG(1) << "Setting timezone to " << id;
310 chromeos::SetTimezoneIDFromString(id);
311 icu::TimeZone::setDefault(timezone);
312 FOR_EACH_OBSERVER(Observer, observers_, TimezoneChanged(timezone));
313 }
314
315 bool SystemAccessImpl::GetMachineStatistic(
316 const std::string& name, std::string* result) {
317 NameValuePairsParser::NameValueMap::iterator iter = machine_info_.find(name);
318 if (iter != machine_info_.end()) {
319 *result = iter->second;
320 return true;
321 }
322 return false;
323 }
324
325 void SystemAccessImpl::AddObserver(Observer* observer) {
326 observers_.AddObserver(observer);
327 }
328
329 void SystemAccessImpl::RemoveObserver(Observer* observer) {
330 observers_.RemoveObserver(observer);
331 }
332
333 SystemAccessImpl::SystemAccessImpl() {
334 // Get Statistics
335 UpdateMachineStatistics();
336 // Get Timezone
337 std::string id = GetTimezoneIDAsString();
338 if (id.empty()) {
339 id = kFallbackTimeZoneId;
340 LOG(ERROR) << "Got an empty string for timezone, default to " << id;
341 }
342 icu::TimeZone* timezone =
343 icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(id));
344 timezone_.reset(timezone);
345 icu::TimeZone::setDefault(*timezone);
346 VLOG(1) << "Timezone is " << id;
347 }
348
349 void SystemAccessImpl::UpdateMachineStatistics() {
350 NameValuePairsParser parser(&machine_info_);
351 if (!parser.GetSingleValueFromTool(arraysize(kHardwareClassTool),
352 kHardwareClassTool,
353 kHardwareClassKey)) {
354 // Use kUnknownHardwareClass if the hardware class command fails.
355 parser.AddNameValuePair(kHardwareClassKey, kUnknownHardwareClass);
356 }
357 parser.ParseNameValuePairsFromTool(arraysize(kMachineHardwareInfoTool),
358 kMachineHardwareInfoTool,
359 kMachineHardwareInfoEq,
360 kMachineHardwareInfoDelim);
361 parser.ParseNameValuePairsFromTool(arraysize(kMachineOSInfoTool),
362 kMachineOSInfoTool,
363 kMachineOSInfoEq,
364 kMachineOSInfoDelim);
365 parser.ParseNameValuePairsFromTool(
366 arraysize(kVpdTool), kVpdTool, kVpdEq, kVpdDelim);
367 }
368
369 CancelableRequestProvider::Handle SystemAccessImpl::RequestSyslogs(
370 bool compress_logs,
371 SyslogsContext context,
372 CancelableRequestConsumerBase* consumer,
373 ReadCompleteCallback* callback) {
374 // Register the callback request.
375 scoped_refptr<CancelableRequest<ReadCompleteCallback> > request(
376 new CancelableRequest<ReadCompleteCallback>(callback));
377 AddRequest(request, consumer);
378
379 // Schedule a task on the FILE thread which will then trigger a request
380 // callback on the calling thread (e.g. UI) when complete.
381 BrowserThread::PostTask(
382 BrowserThread::FILE, FROM_HERE,
383 NewRunnableMethod(
384 this, &SystemAccessImpl::ReadSyslogs, request,
385 compress_logs, context));
386
387 return request->handle();
388 }
389
390 // Called from FILE thread.
391 void SystemAccessImpl::ReadSyslogs(
392 scoped_refptr<CancelableRequest<ReadCompleteCallback> > request,
393 bool compress_logs,
394 SyslogsContext context) {
395 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
396
397 if (request->canceled())
398 return;
399
400 if (compress_logs && !CommandLine::ForCurrentProcess()->HasSwitch(
401 switches::kCompressSystemFeedback))
402 compress_logs = false;
403
404 // Create temp file.
405 FilePath zip_file;
406 if (compress_logs && !file_util::CreateTemporaryFile(&zip_file)) {
407 LOG(ERROR) << "Cannot create temp file";
408 compress_logs = false;
409 }
410
411 LogDictionaryType* logs = NULL;
412 logs = chromeos::GetSystemLogs(
413 compress_logs ? &zip_file : NULL,
414 GetSyslogsContextString(context));
415
416 std::string* zip_content = NULL;
417 if (compress_logs) {
418 // Load compressed logs.
419 zip_content = new std::string();
420 LoadCompressedLogs(zip_file, zip_content);
421 file_util::Delete(zip_file, false);
422 }
423
424 // Will call the callback on the calling thread.
425 request->ForwardResult(Tuple2<LogDictionaryType*,
426 std::string*>(logs, zip_content));
427 }
428
429
430 void SystemAccessImpl::LoadCompressedLogs(const FilePath& zip_file,
431 std::string* zip_content) {
432 DCHECK(zip_content);
433 if (!file_util::ReadFileToString(zip_file, zip_content)) {
434 LOG(ERROR) << "Cannot read compressed logs file from " <<
435 zip_file.value().c_str();
436 }
437 }
438
439 const char* SystemAccessImpl::GetSyslogsContextString(SyslogsContext context) {
440 switch (context) {
441 case(SYSLOGS_FEEDBACK):
442 return kContextFeedback;
443 case(SYSLOGS_SYSINFO):
444 return kContextSysInfo;
445 case(SYSLOGS_NETWORK):
446 return kContextNetwork;
447 case(SYSLOGS_DEFAULT):
448 return kContextSysInfo;
449 default:
450 NOTREACHED();
451 return "";
452 }
453 }
454
455 SystemAccessImpl* SystemAccessImpl::GetInstance() {
456 return Singleton<SystemAccessImpl,
457 DefaultSingletonTraits<SystemAccessImpl> >::get();
458 }
459
460 } // namespace
461
462 SystemAccess* SystemAccess::GetInstance() {
463 return SystemAccessImpl::GetInstance();
464 }
465
466 } // namespace chromeos
467
468 // Allows InvokeLater without adding refcounting. SystemAccessImpl is a
469 // Singleton and won't be deleted until it's last InvokeLater is run.
470 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::SystemAccessImpl);
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/system_access.h ('k') | chrome/browser/extensions/extension_info_private_api_chromeos.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698