Home | History | Annotate | Download | only in system_logs
      1 // Copyright 2014 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_logs/debug_log_writer.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/callback.h"
     10 #include "base/command_line.h"
     11 #include "base/files/file.h"
     12 #include "base/files/file_util.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/process/kill.h"
     15 #include "base/process/launch.h"
     16 #include "chrome/common/logging_chrome.h"
     17 #include "chromeos/dbus/dbus_thread_manager.h"
     18 #include "chromeos/dbus/debug_daemon_client.h"
     19 #include "content/public/browser/browser_thread.h"
     20 
     21 namespace chromeos {
     22 
     23 namespace {
     24 
     25 // Callback for returning status of executed external command.
     26 typedef base::Callback<void(bool succeeded)> CommandCompletionCallback;
     27 
     28 const char kGzipCommand[] = "/bin/gzip";
     29 const char kTarCommand[] = "/bin/tar";
     30 
     31 scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner(
     32     const std::string sequence_name) {
     33   base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool();
     34   return pool->GetSequencedTaskRunnerWithShutdownBehavior(
     35       pool->GetNamedSequenceToken(sequence_name),
     36       base::SequencedWorkerPool::BLOCK_SHUTDOWN);
     37 }
     38 
     39 // Called upon completion of |WriteDebugLogToFile|. Closes file
     40 // descriptor, deletes log file in the case of failure and calls
     41 // |callback|.
     42 void WriteDebugLogToFileCompleted(
     43     const base::FilePath& file_path,
     44     const std::string& sequence_token_name,
     45     const DebugLogWriter::StoreLogsCallback& callback,
     46     bool succeeded) {
     47   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     48   if (!succeeded) {
     49     bool posted = GetSequencedTaskRunner(sequence_token_name)->PostTaskAndReply(
     50         FROM_HERE,
     51         base::Bind(base::IgnoreResult(&base::DeleteFile), file_path, false),
     52         base::Bind(callback, file_path, false));
     53     DCHECK(posted);
     54     return;
     55   }
     56   if (!callback.is_null())
     57     callback.Run(file_path, true);
     58 }
     59 
     60 // Stores into |file_path| debug logs in the .tgz format. Calls
     61 // |callback| upon completion.
     62 void WriteDebugLogToFile(base::File* file,
     63                          const std::string& sequence_token_name,
     64                          const base::FilePath& file_path,
     65                          bool should_compress,
     66                          const DebugLogWriter::StoreLogsCallback& callback) {
     67   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
     68   if (!file->IsValid()) {
     69     LOG(ERROR) << "Can't create debug log file: " << file_path.AsUTF8Unsafe()
     70                << ", "
     71                << "error: " << file->error_details();
     72     return;
     73   }
     74   scoped_refptr<base::TaskRunner> task_runner =
     75       GetSequencedTaskRunner(sequence_token_name);
     76   chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->DumpDebugLogs(
     77       should_compress,
     78       file->Pass(),
     79       task_runner,
     80       base::Bind(&WriteDebugLogToFileCompleted,
     81                  file_path,
     82                  sequence_token_name,
     83                  callback));
     84 }
     85 
     86 // Runs command with its parameters as defined in |argv|.
     87 // Upon completion, it will report command run outcome via |callback| on the
     88 // same thread from where it was initially called from.
     89 void RunCommand(const std::vector<std::string>& argv,
     90                 const CommandCompletionCallback& callback) {
     91   base::ProcessHandle handle = base::kNullProcessHandle;
     92   if (!base::LaunchProcess(argv, base::LaunchOptions(), &handle)) {
     93     LOG(ERROR) << "Failed to execute command " << argv[0];
     94     if (!callback.is_null())
     95       callback.Run(false);
     96 
     97     return;
     98   }
     99 
    100   int exit_code = 0;
    101   if (!base::WaitForExitCode(handle, &exit_code)) {
    102     LOG(ERROR) << "Can't get exit code for pid " << handle;
    103     if (!callback.is_null())
    104       callback.Run(false);
    105 
    106     return;
    107   }
    108   if (!callback.is_null())
    109     callback.Run(exit_code == 0);
    110 }
    111 
    112 // Callback for handling the outcome of CompressArchive(). It reports
    113 // the final outcome of log retreival process at via |callback|.
    114 void OnCompressArchiveCompleted(
    115     const base::FilePath& tar_file_path,
    116     const base::FilePath& compressed_output_path,
    117     const DebugLogWriter::StoreLogsCallback& callback,
    118     bool compression_command_success) {
    119   if (!compression_command_success) {
    120     LOG(ERROR) << "Failed compressing " << compressed_output_path.value();
    121     content::BrowserThread::PostTask(
    122         content::BrowserThread::UI,
    123         FROM_HERE,
    124         base::Bind(callback, base::FilePath(), false));
    125     base::DeleteFile(tar_file_path, true);
    126     base::DeleteFile(compressed_output_path, true);
    127     return;
    128   }
    129 
    130   content::BrowserThread::PostTask(
    131       content::BrowserThread::UI,
    132       FROM_HERE,
    133       base::Bind(callback, compressed_output_path, true));
    134 }
    135 
    136 // Gzips |tar_file_path| and stores results in |compressed_output_path|.
    137 void CompressArchive(const base::FilePath& tar_file_path,
    138                      const base::FilePath& compressed_output_path,
    139                      const DebugLogWriter::StoreLogsCallback& callback,
    140                      bool add_user_logs_command_success) {
    141   if (!add_user_logs_command_success) {
    142     LOG(ERROR) << "Failed adding user logs to " << tar_file_path.value();
    143     content::BrowserThread::PostTask(
    144         content::BrowserThread::UI,
    145         FROM_HERE,
    146         base::Bind(callback, base::FilePath(), false));
    147     base::DeleteFile(tar_file_path, true);
    148     return;
    149   }
    150 
    151   std::vector<std::string> argv;
    152   argv.push_back(kGzipCommand);
    153   argv.push_back(tar_file_path.value());
    154   RunCommand(argv,
    155              base::Bind(&OnCompressArchiveCompleted,
    156                         tar_file_path,
    157                         compressed_output_path,
    158                         callback));
    159 }
    160 
    161 // Adds user sessions specific logs from |user_log_dir| into tar archive file
    162 // at |tar_file_path|. Upon completion, it will call CompressArchive() to
    163 // produce |compressed_output_path|.
    164 void AddUserLogsToArchive(const base::FilePath& user_log_dir,
    165                           const base::FilePath& tar_file_path,
    166                           const base::FilePath& compressed_output_path,
    167                           const DebugLogWriter::StoreLogsCallback& callback) {
    168   std::vector<std::string> argv;
    169   argv.push_back(kTarCommand);
    170   argv.push_back("-rvf");
    171   argv.push_back(tar_file_path.value());
    172   argv.push_back(user_log_dir.value());
    173   RunCommand(
    174       argv,
    175       base::Bind(
    176           &CompressArchive, tar_file_path, compressed_output_path, callback));
    177 }
    178 
    179 // Appends user logs after system logs are archived into |tar_file_path|.
    180 void OnSystemLogsAdded(const DebugLogWriter::StoreLogsCallback& callback,
    181                        const base::FilePath& tar_file_path,
    182                        bool succeeded) {
    183   if (!succeeded) {
    184     if (!callback.is_null())
    185       callback.Run(base::FilePath(), false);
    186 
    187     return;
    188   }
    189 
    190   base::FilePath compressed_output_path =
    191       tar_file_path.AddExtension(FILE_PATH_LITERAL(".gz"));
    192   base::FilePath user_log_dir =
    193       logging::GetSessionLogDir(*CommandLine::ForCurrentProcess());
    194 
    195   content::BrowserThread::PostBlockingPoolTask(
    196       FROM_HERE,
    197       base::Bind(&AddUserLogsToArchive,
    198                  user_log_dir,
    199                  tar_file_path,
    200                  compressed_output_path,
    201                  callback));
    202 }
    203 
    204 void IntializeLogFile(base::File* file,
    205                       const base::FilePath& file_path,
    206                       uint32 flags) {
    207   base::FilePath dir = file_path.DirName();
    208   if (!base::DirectoryExists(dir)) {
    209     if (!base::CreateDirectory(dir)) {
    210       LOG(ERROR) << "Can not create " << dir.value();
    211       return;
    212     }
    213   }
    214 
    215   file->Initialize(file_path, flags);
    216 }
    217 
    218 // Starts logs retrieval process. The output will be stored in file with name
    219 // derived from |file_name_template|.
    220 void StartLogRetrieval(const base::FilePath& file_name_template,
    221                        bool should_compress,
    222                        const std::string& sequence_token_name,
    223                        const DebugLogWriter::StoreLogsCallback& callback) {
    224   base::FilePath file_path =
    225       logging::GenerateTimestampedName(file_name_template, base::Time::Now());
    226 
    227   int flags = base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE;
    228   base::File* file = new base::File;
    229   GetSequencedTaskRunner(sequence_token_name)->PostTaskAndReply(
    230       FROM_HERE,
    231       base::Bind(&IntializeLogFile, base::Unretained(file), file_path, flags),
    232       base::Bind(&WriteDebugLogToFile,
    233                  base::Owned(file),
    234                  sequence_token_name,
    235                  file_path,
    236                  should_compress,
    237                  callback));
    238 }
    239 
    240 const char kDefaultSequenceName[] = "DebugLogWriter";
    241 
    242 }  // namespace
    243 
    244 // static.
    245 void DebugLogWriter::StoreLogs(const base::FilePath& fileshelf,
    246                                bool should_compress,
    247                                const StoreLogsCallback& callback) {
    248   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    249   DCHECK(!callback.is_null());
    250 
    251   base::FilePath file_path =
    252       fileshelf.Append(should_compress ? FILE_PATH_LITERAL("debug-logs.tgz")
    253                                        : FILE_PATH_LITERAL("debug-logs.tar"));
    254 
    255   StartLogRetrieval(file_path, should_compress, kDefaultSequenceName, callback);
    256 }
    257 
    258 // static.
    259 void DebugLogWriter::StoreCombinedLogs(const base::FilePath& fileshelf,
    260                                        const std::string& sequence_token_name,
    261                                        const StoreLogsCallback& callback) {
    262   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
    263   DCHECK(!callback.is_null());
    264 
    265   base::FilePath file_path =
    266       fileshelf.Append(FILE_PATH_LITERAL("combined-logs.tar"));
    267 
    268   // Get system logs from /var/log first, then add user-specific stuff.
    269   StartLogRetrieval(file_path,
    270                     false,
    271                     sequence_token_name,
    272                     base::Bind(&OnSystemLogsAdded, callback));
    273 }
    274 
    275 }  // namespace chromeos
    276