| OLD | NEW |
| (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/syslogs_provider.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/memory/scoped_ptr.h" |
| 12 #include "base/memory/singleton.h" |
| 13 #include "base/string_util.h" |
| 14 #include "chrome/common/chrome_switches.h" |
| 15 #include "content/browser/browser_thread.h" |
| 16 |
| 17 namespace chromeos { |
| 18 namespace system { |
| 19 namespace { |
| 20 |
| 21 const char kSysLogsScript[] = |
| 22 "/usr/share/userfeedback/scripts/sysinfo_script_runner"; |
| 23 const char kBzip2Command[] = |
| 24 "/bin/bzip2"; |
| 25 const char kMultilineQuote[] = "\"\"\""; |
| 26 const char kNewLineChars[] = "\r\n"; |
| 27 const char kInvalidLogEntry[] = "<invalid characters in log entry>"; |
| 28 const char kEmptyLogEntry[] = "<no value>"; |
| 29 |
| 30 const char kContextFeedback[] = "feedback"; |
| 31 const char kContextSysInfo[] = "sysinfo"; |
| 32 const char kContextNetwork[] = "network"; |
| 33 |
| 34 // Reads a key from the input string erasing the read values + delimiters read |
| 35 // from the initial string |
| 36 std::string ReadKey(std::string* data) { |
| 37 size_t equal_sign = data->find("="); |
| 38 if (equal_sign == std::string::npos) |
| 39 return std::string(""); |
| 40 std::string key = data->substr(0, equal_sign); |
| 41 data->erase(0, equal_sign); |
| 42 if (data->size() > 0) { |
| 43 // erase the equal to sign also |
| 44 data->erase(0,1); |
| 45 return key; |
| 46 } |
| 47 return std::string(); |
| 48 } |
| 49 |
| 50 // Reads a value from the input string; erasing the read values from |
| 51 // the initial string; detects if the value is multiline and reads |
| 52 // accordingly |
| 53 std::string ReadValue(std::string* data) { |
| 54 // Trim the leading spaces and tabs. In order to use a multi-line |
| 55 // value, you have to place the multi-line quote on the same line as |
| 56 // the equal sign. |
| 57 // |
| 58 // Why not use TrimWhitespace? Consider the following input: |
| 59 // |
| 60 // KEY1= |
| 61 // KEY2=VALUE |
| 62 // |
| 63 // If we use TrimWhitespace, we will incorrectly trim the new line |
| 64 // and assume that KEY1's value is "KEY2=VALUE" rather than empty. |
| 65 TrimString(*data, " \t", data); |
| 66 |
| 67 // If multiline value |
| 68 if (StartsWithASCII(*data, std::string(kMultilineQuote), false)) { |
| 69 data->erase(0, strlen(kMultilineQuote)); |
| 70 size_t next_multi = data->find(kMultilineQuote); |
| 71 if (next_multi == std::string::npos) { |
| 72 // Error condition, clear data to stop further processing |
| 73 data->erase(); |
| 74 return std::string(); |
| 75 } |
| 76 std::string value = data->substr(0, next_multi); |
| 77 data->erase(0, next_multi + 3); |
| 78 return value; |
| 79 } else { // single line value |
| 80 size_t endl_pos = data->find_first_of(kNewLineChars); |
| 81 // if we don't find a new line, we just return the rest of the data |
| 82 std::string value = data->substr(0, endl_pos); |
| 83 data->erase(0, endl_pos); |
| 84 return value; |
| 85 } |
| 86 } |
| 87 |
| 88 // Returns a map of system log keys and values. |
| 89 // |
| 90 // Parameters: |
| 91 // temp_filename: This is an out parameter that holds the name of a file in |
| 92 // Reads a value from the input string; erasing the read values from |
| 93 // the initial string; detects if the value is multiline and reads |
| 94 // accordingly |
| 95 // /tmp that contains the system logs in a KEY=VALUE format. |
| 96 // If this parameter is NULL, system logs are not retained on |
| 97 // the filesystem after this call completes. |
| 98 // context: This is an in parameter specifying what context should be |
| 99 // passed to the syslog collection script; currently valid |
| 100 // values are "sysinfo" or "feedback"; in case of an invalid |
| 101 // value, the script will currently default to "sysinfo" |
| 102 |
| 103 LogDictionaryType* GetSystemLogs(FilePath* zip_file_name, |
| 104 const std::string& context) { |
| 105 // Create the temp file, logs will go here |
| 106 FilePath temp_filename; |
| 107 |
| 108 if (!file_util::CreateTemporaryFile(&temp_filename)) |
| 109 return NULL; |
| 110 |
| 111 std::string cmd = std::string(kSysLogsScript) + " " + context + " >> " + |
| 112 temp_filename.value(); |
| 113 |
| 114 // Ignore the return value - if the script execution didn't work |
| 115 // stderr won't go into the output file anyway. |
| 116 if (::system(cmd.c_str()) == -1) |
| 117 LOG(WARNING) << "Command " << cmd << " failed to run"; |
| 118 |
| 119 // Compress the logs file if requested. |
| 120 if (zip_file_name) { |
| 121 cmd = std::string(kBzip2Command) + " -c " + temp_filename.value() + " > " + |
| 122 zip_file_name->value(); |
| 123 if (::system(cmd.c_str()) == -1) |
| 124 LOG(WARNING) << "Command " << cmd << " failed to run"; |
| 125 } |
| 126 // Read logs from the temp file |
| 127 std::string data; |
| 128 bool read_success = file_util::ReadFileToString(temp_filename, |
| 129 &data); |
| 130 // if we were using an internal temp file, the user does not need the |
| 131 // logs to stay past the ReadFile call - delete the file |
| 132 file_util::Delete(temp_filename, false); |
| 133 |
| 134 if (!read_success) |
| 135 return NULL; |
| 136 |
| 137 // Parse the return data into a dictionary |
| 138 LogDictionaryType* logs = new LogDictionaryType(); |
| 139 while (data.length() > 0) { |
| 140 std::string key = ReadKey(&data); |
| 141 TrimWhitespaceASCII(key, TRIM_ALL, &key); |
| 142 if (!key.empty()) { |
| 143 std::string value = ReadValue(&data); |
| 144 if (IsStringUTF8(value)) { |
| 145 TrimWhitespaceASCII(value, TRIM_ALL, &value); |
| 146 if (value.empty()) |
| 147 (*logs)[key] = kEmptyLogEntry; |
| 148 else |
| 149 (*logs)[key] = value; |
| 150 } else { |
| 151 LOG(WARNING) << "Invalid characters in system log entry: " << key; |
| 152 (*logs)[key] = kInvalidLogEntry; |
| 153 } |
| 154 } else { |
| 155 // no more keys, we're done |
| 156 break; |
| 157 } |
| 158 } |
| 159 |
| 160 return logs; |
| 161 } |
| 162 |
| 163 } // namespace |
| 164 |
| 165 class SyslogsProviderImpl : public SyslogsProvider { |
| 166 public: |
| 167 // SyslogsProvider implementation: |
| 168 virtual Handle RequestSyslogs( |
| 169 bool compress_logs, |
| 170 SyslogsContext context, |
| 171 CancelableRequestConsumerBase* consumer, |
| 172 ReadCompleteCallback* callback); |
| 173 |
| 174 // Reads system logs, compresses content if requested. |
| 175 // Called from FILE thread. |
| 176 void ReadSyslogs( |
| 177 scoped_refptr<CancelableRequest<ReadCompleteCallback> > request, |
| 178 bool compress_logs, |
| 179 SyslogsContext context); |
| 180 |
| 181 // Loads compressed logs and writes into |zip_content|. |
| 182 void LoadCompressedLogs(const FilePath& zip_file, |
| 183 std::string* zip_content); |
| 184 |
| 185 static SyslogsProviderImpl* GetInstance(); |
| 186 |
| 187 private: |
| 188 friend struct DefaultSingletonTraits<SyslogsProviderImpl>; |
| 189 |
| 190 SyslogsProviderImpl(); |
| 191 |
| 192 // Gets syslogs context string from the enum value. |
| 193 const char* GetSyslogsContextString(SyslogsContext context); |
| 194 |
| 195 DISALLOW_COPY_AND_ASSIGN(SyslogsProviderImpl); |
| 196 }; |
| 197 |
| 198 SyslogsProviderImpl::SyslogsProviderImpl() { |
| 199 } |
| 200 |
| 201 CancelableRequestProvider::Handle SyslogsProviderImpl::RequestSyslogs( |
| 202 bool compress_logs, |
| 203 SyslogsContext context, |
| 204 CancelableRequestConsumerBase* consumer, |
| 205 ReadCompleteCallback* callback) { |
| 206 // Register the callback request. |
| 207 scoped_refptr<CancelableRequest<ReadCompleteCallback> > request( |
| 208 new CancelableRequest<ReadCompleteCallback>(callback)); |
| 209 AddRequest(request, consumer); |
| 210 |
| 211 // Schedule a task on the FILE thread which will then trigger a request |
| 212 // callback on the calling thread (e.g. UI) when complete. |
| 213 BrowserThread::PostTask( |
| 214 BrowserThread::FILE, FROM_HERE, |
| 215 NewRunnableMethod( |
| 216 this, &SyslogsProviderImpl::ReadSyslogs, request, |
| 217 compress_logs, context)); |
| 218 |
| 219 return request->handle(); |
| 220 } |
| 221 |
| 222 // Called from FILE thread. |
| 223 void SyslogsProviderImpl::ReadSyslogs( |
| 224 scoped_refptr<CancelableRequest<ReadCompleteCallback> > request, |
| 225 bool compress_logs, |
| 226 SyslogsContext context) { |
| 227 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 228 |
| 229 if (request->canceled()) |
| 230 return; |
| 231 |
| 232 if (compress_logs && !CommandLine::ForCurrentProcess()->HasSwitch( |
| 233 switches::kCompressSystemFeedback)) |
| 234 compress_logs = false; |
| 235 |
| 236 // Create temp file. |
| 237 FilePath zip_file; |
| 238 if (compress_logs && !file_util::CreateTemporaryFile(&zip_file)) { |
| 239 LOG(ERROR) << "Cannot create temp file"; |
| 240 compress_logs = false; |
| 241 } |
| 242 |
| 243 LogDictionaryType* logs = NULL; |
| 244 logs = GetSystemLogs( |
| 245 compress_logs ? &zip_file : NULL, |
| 246 GetSyslogsContextString(context)); |
| 247 |
| 248 std::string* zip_content = NULL; |
| 249 if (compress_logs) { |
| 250 // Load compressed logs. |
| 251 zip_content = new std::string(); |
| 252 LoadCompressedLogs(zip_file, zip_content); |
| 253 file_util::Delete(zip_file, false); |
| 254 } |
| 255 |
| 256 // Will call the callback on the calling thread. |
| 257 request->ForwardResult(Tuple2<LogDictionaryType*, |
| 258 std::string*>(logs, zip_content)); |
| 259 } |
| 260 |
| 261 |
| 262 void SyslogsProviderImpl::LoadCompressedLogs(const FilePath& zip_file, |
| 263 std::string* zip_content) { |
| 264 DCHECK(zip_content); |
| 265 if (!file_util::ReadFileToString(zip_file, zip_content)) { |
| 266 LOG(ERROR) << "Cannot read compressed logs file from " << |
| 267 zip_file.value().c_str(); |
| 268 } |
| 269 } |
| 270 |
| 271 const char* SyslogsProviderImpl::GetSyslogsContextString( |
| 272 SyslogsContext context) { |
| 273 switch (context) { |
| 274 case(SYSLOGS_FEEDBACK): |
| 275 return kContextFeedback; |
| 276 case(SYSLOGS_SYSINFO): |
| 277 return kContextSysInfo; |
| 278 case(SYSLOGS_NETWORK): |
| 279 return kContextNetwork; |
| 280 case(SYSLOGS_DEFAULT): |
| 281 return kContextSysInfo; |
| 282 default: |
| 283 NOTREACHED(); |
| 284 return ""; |
| 285 } |
| 286 } |
| 287 |
| 288 SyslogsProviderImpl* SyslogsProviderImpl::GetInstance() { |
| 289 return Singleton<SyslogsProviderImpl, |
| 290 DefaultSingletonTraits<SyslogsProviderImpl> >::get(); |
| 291 } |
| 292 |
| 293 SyslogsProvider* SyslogsProvider::GetInstance() { |
| 294 return SyslogsProviderImpl::GetInstance(); |
| 295 } |
| 296 |
| 297 } // namespace system |
| 298 } // namespace chromeos |
| 299 |
| 300 // Allows InvokeLater without adding refcounting. SyslogsProviderImpl is a |
| 301 // Singleton and won't be deleted until it's last InvokeLater is run. |
| 302 DISABLE_RUNNABLE_METHOD_REFCOUNT(chromeos::system::SyslogsProviderImpl); |
| OLD | NEW |