Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / log_private / log_private_api_chromeos.cc
bloba25bbf97967837dcce933fe5967910a3bb680c49
1 // Copyright 2013 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.
5 #include "chrome/browser/extensions/api/log_private/log_private_api.h"
7 #include <string>
8 #include <vector>
10 #include "base/command_line.h"
11 #include "base/files/file_util.h"
12 #include "base/json/json_writer.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/download/download_prefs.h"
19 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
20 #include "chrome/browser/extensions/api/log_private/filter_handler.h"
21 #include "chrome/browser/extensions/api/log_private/log_parser.h"
22 #include "chrome/browser/extensions/api/log_private/syslog_parser.h"
23 #include "chrome/browser/feedback/system_logs/scrubbed_system_logs_fetcher.h"
24 #include "chrome/browser/io_thread.h"
25 #include "chrome/browser/net/chrome_net_log.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/common/extensions/api/log_private.h"
29 #include "chrome/common/logging_chrome.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "extensions/browser/event_router.h"
33 #include "extensions/browser/extension_function.h"
34 #include "extensions/browser/extension_registry.h"
35 #include "extensions/browser/granted_file_entry.h"
37 #if defined(OS_CHROMEOS)
38 #include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
39 #endif
41 using content::BrowserThread;
43 namespace events {
44 const char kOnCapturedEvents[] = "logPrivate.onCapturedEvents";
45 } // namespace events
47 namespace extensions {
48 namespace {
50 const char kAppLogsSubdir[] = "apps";
51 const char kLogDumpsSubdir[] = "log_dumps";
52 const char kLogFileNameBase[] = "net-internals";
53 const int kNetLogEventDelayMilliseconds = 100;
55 // Gets sequenced task runner for file specific calls within this API.
56 scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() {
57 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
58 return pool->GetSequencedTaskRunnerWithShutdownBehavior(
59 pool->GetNamedSequenceToken(FileResource::kSequenceToken),
60 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
63 // Checks if we are running on sequenced task runner thread.
64 bool IsRunningOnSequenceThread() {
65 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
66 return pool->IsRunningSequenceOnCurrentThread(
67 pool->GetNamedSequenceToken(FileResource::kSequenceToken));
70 scoped_ptr<LogParser> CreateLogParser(const std::string& log_type) {
71 if (log_type == "syslog")
72 return scoped_ptr<LogParser>(new SyslogParser());
73 // TODO(shinfan): Add more parser here
75 NOTREACHED() << "Invalid log type: " << log_type;
76 return scoped_ptr<LogParser>();
79 void CollectLogInfo(
80 FilterHandler* filter_handler,
81 system_logs::SystemLogsResponse* logs,
82 std::vector<linked_ptr<api::log_private::LogEntry> >* output) {
83 for (system_logs::SystemLogsResponse::const_iterator
84 request_it = logs->begin(); request_it != logs->end(); ++request_it) {
85 if (!filter_handler->IsValidSource(request_it->first)) {
86 continue;
88 scoped_ptr<LogParser> parser(CreateLogParser(request_it->first));
89 if (parser) {
90 parser->Parse(request_it->second, output, filter_handler);
95 // Returns directory location of app-specific logs that are initiated with
96 // logPrivate.startEventRecorder() calls - /home/chronos/user/log/apps
97 base::FilePath GetAppLogDirectory() {
98 return logging::GetSessionLogDir(*base::CommandLine::ForCurrentProcess())
99 .Append(kAppLogsSubdir);
102 // Returns directory location where logs dumps initiated with chrome.dumpLogs
103 // will be stored - /home/chronos/<user_profile_dir>/Downloads/log_dumps
104 base::FilePath GetLogDumpDirectory(content::BrowserContext* context) {
105 const DownloadPrefs* const prefs = DownloadPrefs::FromBrowserContext(context);
106 return prefs->DownloadPath().Append(kLogDumpsSubdir);
109 // Removes direcotry content of |logs_dumps| and |app_logs_dir| (only for the
110 // primary profile).
111 void CleanUpLeftoverLogs(bool is_primary_profile,
112 const base::FilePath& app_logs_dir,
113 const base::FilePath& logs_dumps) {
114 LOG(WARNING) << "Deleting " << app_logs_dir.value();
115 LOG(WARNING) << "Deleting " << logs_dumps.value();
117 DCHECK(IsRunningOnSequenceThread());
118 base::DeleteFile(logs_dumps, true);
120 // App-specific logs are stored in /home/chronos/user/log/apps directory that
121 // is shared between all profiles in multi-profile case. We should not
122 // nuke it for non-primary profiles.
123 if (!is_primary_profile)
124 return;
126 base::DeleteFile(app_logs_dir, true);
129 } // namespace
131 const char FileResource::kSequenceToken[] = "log_api_files";
133 FileResource::FileResource(const std::string& owner_extension_id,
134 const base::FilePath& path)
135 : ApiResource(owner_extension_id), path_(path) {
138 FileResource::~FileResource() {
139 base::DeleteFile(path_, true);
142 bool FileResource::IsPersistent() const {
143 return false;
146 // static
147 LogPrivateAPI* LogPrivateAPI::Get(content::BrowserContext* context) {
148 LogPrivateAPI* api = GetFactoryInstance()->Get(context);
149 api->Initialize();
150 return api;
153 LogPrivateAPI::LogPrivateAPI(content::BrowserContext* context)
154 : browser_context_(context),
155 logging_net_internals_(false),
156 event_sink_(api::log_private::EVENT_SINK_CAPTURE),
157 extension_registry_observer_(this),
158 log_file_resources_(context),
159 initialized_(false) {
162 LogPrivateAPI::~LogPrivateAPI() {
165 void LogPrivateAPI::StartNetInternalsWatch(
166 const std::string& extension_id,
167 api::log_private::EventSink event_sink,
168 const base::Closure& closure) {
169 net_internal_watches_.insert(extension_id);
171 // Nuke any leftover app-specific or dumped log files from previous sessions.
172 BrowserThread::PostTaskAndReply(
173 BrowserThread::IO,
174 FROM_HERE,
175 base::Bind(&LogPrivateAPI::MaybeStartNetInternalLogging,
176 base::Unretained(this),
177 extension_id,
178 g_browser_process->io_thread(),
179 event_sink),
180 closure);
183 void LogPrivateAPI::StopNetInternalsWatch(const std::string& extension_id,
184 const base::Closure& closure) {
185 net_internal_watches_.erase(extension_id);
186 MaybeStopNetInternalLogging(closure);
189 void LogPrivateAPI::StopAllWatches(const std::string& extension_id,
190 const base::Closure& closure) {
191 StopNetInternalsWatch(extension_id, closure);
194 void LogPrivateAPI::RegisterTempFile(const std::string& owner_extension_id,
195 const base::FilePath& file_path) {
196 if (!IsRunningOnSequenceThread()) {
197 GetSequencedTaskRunner()->PostTask(
198 FROM_HERE,
199 base::Bind(&LogPrivateAPI::RegisterTempFile,
200 base::Unretained(this),
201 owner_extension_id,
202 file_path));
203 return;
206 log_file_resources_.Add(new FileResource(owner_extension_id, file_path));
209 static base::LazyInstance<BrowserContextKeyedAPIFactory<LogPrivateAPI> >
210 g_factory = LAZY_INSTANCE_INITIALIZER;
212 // static
213 BrowserContextKeyedAPIFactory<LogPrivateAPI>*
214 LogPrivateAPI::GetFactoryInstance() {
215 return g_factory.Pointer();
218 void LogPrivateAPI::OnAddEntry(const net::NetLog::Entry& entry) {
219 // We could receive events on whatever thread they happen to be generated,
220 // since we are only interested in network events, we should ignore any
221 // other thread than BrowserThread::IO.
222 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
223 return;
226 if (!pending_entries_.get()) {
227 pending_entries_.reset(new base::ListValue());
228 BrowserThread::PostDelayedTask(
229 BrowserThread::IO, FROM_HERE,
230 base::Bind(&LogPrivateAPI::PostPendingEntries, base::Unretained(this)),
231 base::TimeDelta::FromMilliseconds(kNetLogEventDelayMilliseconds));
233 pending_entries_->Append(entry.ToValue());
236 void LogPrivateAPI::PostPendingEntries() {
237 BrowserThread::PostTask(
238 BrowserThread::UI, FROM_HERE,
239 base::Bind(&LogPrivateAPI:: AddEntriesOnUI,
240 base::Unretained(this),
241 base::Passed(&pending_entries_)));
244 void LogPrivateAPI::AddEntriesOnUI(scoped_ptr<base::ListValue> value) {
245 DCHECK_CURRENTLY_ON(BrowserThread::UI);
247 for (std::set<std::string>::iterator ix = net_internal_watches_.begin();
248 ix != net_internal_watches_.end(); ++ix) {
249 // Create the event's arguments value.
250 scoped_ptr<base::ListValue> event_args(new base::ListValue());
251 event_args->Append(value->DeepCopy());
252 scoped_ptr<Event> event(
253 new Event(::extensions::events::LOG_PRIVATE_ON_CAPTURED_EVENTS,
254 ::events::kOnCapturedEvents, event_args.Pass()));
255 EventRouter::Get(browser_context_)
256 ->DispatchEventToExtension(*ix, event.Pass());
260 void LogPrivateAPI::CreateTempNetLogFile(const std::string& owner_extension_id,
261 base::ScopedFILE* file) {
262 DCHECK(IsRunningOnSequenceThread());
264 // Create app-specific subdirectory in session logs folder.
265 base::FilePath app_log_dir = GetAppLogDirectory().Append(owner_extension_id);
266 if (!base::DirectoryExists(app_log_dir)) {
267 if (!base::CreateDirectory(app_log_dir)) {
268 LOG(ERROR) << "Could not create dir " << app_log_dir.value();
269 return;
273 base::FilePath file_path = app_log_dir.Append(kLogFileNameBase);
274 file_path = logging::GenerateTimestampedName(file_path, base::Time::Now());
275 FILE* file_ptr = fopen(file_path.value().c_str(), "w");
276 if (file_ptr == nullptr) {
277 LOG(ERROR) << "Could not open " << file_path.value();
278 return;
281 RegisterTempFile(owner_extension_id, file_path);
282 return file->reset(file_ptr);
285 void LogPrivateAPI::StartObservingNetEvents(
286 IOThread* io_thread,
287 base::ScopedFILE* file) {
288 DCHECK_CURRENTLY_ON(BrowserThread::IO);
289 if (!file->get())
290 return;
292 write_to_file_observer_.reset(new net::WriteToFileNetLogObserver());
293 write_to_file_observer_->set_capture_mode(
294 net::NetLogCaptureMode::IncludeCookiesAndCredentials());
295 write_to_file_observer_->StartObserving(io_thread->net_log(), file->Pass(),
296 nullptr, nullptr);
299 void LogPrivateAPI::MaybeStartNetInternalLogging(
300 const std::string& caller_extension_id,
301 IOThread* io_thread,
302 api::log_private::EventSink event_sink) {
303 DCHECK_CURRENTLY_ON(BrowserThread::IO);
304 if (!logging_net_internals_) {
305 logging_net_internals_ = true;
306 event_sink_ = event_sink;
307 switch (event_sink_) {
308 case api::log_private::EVENT_SINK_CAPTURE: {
309 io_thread->net_log()->DeprecatedAddObserver(
310 this, net::NetLogCaptureMode::IncludeCookiesAndCredentials());
311 break;
313 case api::log_private::EVENT_SINK_FILE: {
314 base::ScopedFILE* file = new base::ScopedFILE();
315 // Initialize a FILE on the blocking pool and start observing event
316 // on IO thread.
317 GetSequencedTaskRunner()->PostTaskAndReply(
318 FROM_HERE,
319 base::Bind(&LogPrivateAPI::CreateTempNetLogFile,
320 base::Unretained(this),
321 caller_extension_id,
322 file),
323 base::Bind(&LogPrivateAPI::StartObservingNetEvents,
324 base::Unretained(this),
325 io_thread,
326 base::Owned(file)));
327 break;
329 case api::log_private::EVENT_SINK_NONE: {
330 NOTREACHED();
331 break;
337 void LogPrivateAPI::MaybeStopNetInternalLogging(const base::Closure& closure) {
338 if (net_internal_watches_.empty()) {
339 if (closure.is_null()) {
340 BrowserThread::PostTask(
341 BrowserThread::IO,
342 FROM_HERE,
343 base::Bind(&LogPrivateAPI::StopNetInternalLogging,
344 base::Unretained(this)));
345 } else {
346 BrowserThread::PostTaskAndReply(
347 BrowserThread::IO,
348 FROM_HERE,
349 base::Bind(&LogPrivateAPI::StopNetInternalLogging,
350 base::Unretained(this)),
351 closure);
356 void LogPrivateAPI::StopNetInternalLogging() {
357 DCHECK_CURRENTLY_ON(BrowserThread::IO);
358 if (net_log() && logging_net_internals_) {
359 logging_net_internals_ = false;
360 switch (event_sink_) {
361 case api::log_private::EVENT_SINK_CAPTURE:
362 net_log()->DeprecatedRemoveObserver(this);
363 break;
364 case api::log_private::EVENT_SINK_FILE:
365 write_to_file_observer_->StopObserving(nullptr);
366 write_to_file_observer_.reset();
367 break;
368 case api::log_private::EVENT_SINK_NONE:
369 NOTREACHED();
370 break;
375 void LogPrivateAPI::Initialize() {
376 if (initialized_)
377 return;
379 // Clean up temp files and folders from the previous sessions.
380 initialized_ = true;
381 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
382 GetSequencedTaskRunner()->PostTask(
383 FROM_HERE,
384 base::Bind(&CleanUpLeftoverLogs,
385 Profile::FromBrowserContext(browser_context_) ==
386 ProfileManager::GetPrimaryUserProfile(),
387 GetAppLogDirectory(),
388 GetLogDumpDirectory(browser_context_)));
391 void LogPrivateAPI::OnExtensionUnloaded(
392 content::BrowserContext* browser_context,
393 const Extension* extension,
394 UnloadedExtensionInfo::Reason reason) {
395 StopNetInternalsWatch(extension->id(), base::Closure());
398 LogPrivateGetHistoricalFunction::LogPrivateGetHistoricalFunction() {
401 LogPrivateGetHistoricalFunction::~LogPrivateGetHistoricalFunction() {
404 bool LogPrivateGetHistoricalFunction::RunAsync() {
405 // Get parameters
406 scoped_ptr<api::log_private::GetHistorical::Params> params(
407 api::log_private::GetHistorical::Params::Create(*args_));
408 EXTENSION_FUNCTION_VALIDATE(params.get());
409 filter_handler_.reset(new FilterHandler(params->filter));
411 system_logs::SystemLogsFetcherBase* fetcher;
412 if ((params->filter).scrub) {
413 fetcher = new system_logs::ScrubbedSystemLogsFetcher();
414 } else {
415 fetcher = new system_logs::AboutSystemLogsFetcher();
417 fetcher->Fetch(
418 base::Bind(&LogPrivateGetHistoricalFunction::OnSystemLogsLoaded, this));
420 return true;
423 void LogPrivateGetHistoricalFunction::OnSystemLogsLoaded(
424 scoped_ptr<system_logs::SystemLogsResponse> sys_info) {
425 std::vector<linked_ptr<api::log_private::LogEntry> > data;
427 CollectLogInfo(filter_handler_.get(), sys_info.get(), &data);
429 // Prepare result
430 api::log_private::Result result;
431 result.data = data;
432 api::log_private::Filter::Populate(
433 *((filter_handler_->GetFilter())->ToValue()), &result.filter);
434 SetResult(result.ToValue().release());
435 SendResponse(true);
438 LogPrivateStartEventRecorderFunction::LogPrivateStartEventRecorderFunction() {
441 LogPrivateStartEventRecorderFunction::~LogPrivateStartEventRecorderFunction() {
444 bool LogPrivateStartEventRecorderFunction::RunAsync() {
445 scoped_ptr<api::log_private::StartEventRecorder::Params> params(
446 api::log_private::StartEventRecorder::Params::Create(*args_));
447 EXTENSION_FUNCTION_VALIDATE(params.get());
448 switch (params->event_type) {
449 case api::log_private::EVENT_TYPE_NETWORK:
450 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
451 ->StartNetInternalsWatch(
452 extension_id(),
453 params->sink,
454 base::Bind(
455 &LogPrivateStartEventRecorderFunction::OnEventRecorderStarted,
456 this));
457 break;
458 case api::log_private::EVENT_TYPE_NONE:
459 NOTREACHED();
460 return false;
463 return true;
466 void LogPrivateStartEventRecorderFunction::OnEventRecorderStarted() {
467 SendResponse(true);
470 LogPrivateStopEventRecorderFunction::LogPrivateStopEventRecorderFunction() {
473 LogPrivateStopEventRecorderFunction::~LogPrivateStopEventRecorderFunction() {
476 bool LogPrivateStopEventRecorderFunction::RunAsync() {
477 scoped_ptr<api::log_private::StopEventRecorder::Params> params(
478 api::log_private::StopEventRecorder::Params::Create(*args_));
479 EXTENSION_FUNCTION_VALIDATE(params.get());
480 switch (params->event_type) {
481 case api::log_private::EVENT_TYPE_NETWORK:
482 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
483 ->StopNetInternalsWatch(
484 extension_id(),
485 base::Bind(
486 &LogPrivateStopEventRecorderFunction::OnEventRecorderStopped,
487 this));
488 break;
489 case api::log_private::EVENT_TYPE_NONE:
490 NOTREACHED();
491 return false;
493 return true;
496 void LogPrivateStopEventRecorderFunction::OnEventRecorderStopped() {
497 SendResponse(true);
500 LogPrivateDumpLogsFunction::LogPrivateDumpLogsFunction() {
503 LogPrivateDumpLogsFunction::~LogPrivateDumpLogsFunction() {
506 bool LogPrivateDumpLogsFunction::RunAsync() {
507 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
508 ->StopAllWatches(
509 extension_id(),
510 base::Bind(&LogPrivateDumpLogsFunction::OnStopAllWatches, this));
511 return true;
514 void LogPrivateDumpLogsFunction::OnStopAllWatches() {
515 chromeos::DebugLogWriter::StoreCombinedLogs(
516 GetLogDumpDirectory(browser_context()).Append(extension_id()),
517 FileResource::kSequenceToken,
518 base::Bind(&LogPrivateDumpLogsFunction::OnStoreLogsCompleted, this));
521 void LogPrivateDumpLogsFunction::OnStoreLogsCompleted(
522 const base::FilePath& log_path,
523 bool succeeded) {
524 if (succeeded) {
525 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
526 ->RegisterTempFile(extension_id(), log_path);
529 scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
530 extensions::GrantedFileEntry file_entry =
531 extensions::app_file_handler_util::CreateFileEntry(
532 Profile::FromBrowserContext(browser_context()),
533 extension(),
534 render_frame_host()->GetProcess()->GetID(),
535 log_path,
536 false);
538 base::DictionaryValue* entry = new base::DictionaryValue();
539 entry->SetString("fileSystemId", file_entry.filesystem_id);
540 entry->SetString("baseName", file_entry.registered_name);
541 entry->SetString("id", file_entry.id);
542 entry->SetBoolean("isDirectory", false);
543 base::ListValue* entry_list = new base::ListValue();
544 entry_list->Append(entry);
545 response->Set("entries", entry_list);
546 response->SetBoolean("multiple", false);
547 SetResult(response.release());
548 SendResponse(succeeded);
551 } // namespace extensions