// Copyright (c) 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/browser_watcher/exit_code_watcher_win.h"

#include <utility>

#include "base/logging.h"
#include "base/process/kill.h"
#include "base/strings/stringprintf.h"
#include "base/win/registry.h"

namespace browser_watcher {

namespace {

base::string16 GetValueName(const base::Time creation_time,
                            base::ProcessId pid) {
  // Convert the PID and creation time to a string value unique to this
  // process instance.
  return base::StringPrintf(L"%d-%lld", pid, creation_time.ToInternalValue());
}

}  // namespace

ExitCodeWatcher::ExitCodeWatcher(base::StringPiece16 registry_path)
    : registry_path_(registry_path.as_string()), exit_code_(STILL_ACTIVE) {}

ExitCodeWatcher::~ExitCodeWatcher() {
}

bool ExitCodeWatcher::Initialize(base::Process process) {
  if (!process.IsValid()) {
    LOG(ERROR) << "Invalid parent handle, can't get parent process ID.";
    return false;
  }

  DWORD process_pid = process.Pid();
  if (process_pid == 0) {
    LOG(ERROR) << "Invalid parent handle, can't get parent process ID.";
    return false;
  }

  FILETIME creation_time = {};
  FILETIME dummy = {};
  if (!::GetProcessTimes(process.Handle(), &creation_time, &dummy, &dummy,
                         &dummy)) {
    PLOG(ERROR) << "Invalid parent handle, can't get parent process times.";
    return false;
  }

  // Success, take ownership of the process.
  process_ = std::move(process);
  process_creation_time_ = base::Time::FromFileTime(creation_time);

  // Start by writing the value STILL_ACTIVE to registry, to allow detection
  // of the case where the watcher itself is somehow terminated before it can
  // write the process' actual exit code.
  return WriteProcessExitCode(STILL_ACTIVE);
}

void ExitCodeWatcher::WaitForExit() {
  if (!process_.WaitForExit(&exit_code_)) {
    LOG(ERROR) << "Failed to wait for process.";
    return;
  }

  WriteProcessExitCode(exit_code_);
}

bool ExitCodeWatcher::WriteProcessExitCode(int exit_code) {
  base::win::RegKey key(HKEY_CURRENT_USER,
                        registry_path_.c_str(),
                        KEY_WRITE);
  base::string16 value_name(
      GetValueName(process_creation_time_, process_.Pid()));

  ULONG result = key.WriteValue(value_name.c_str(), exit_code);
  if (result != ERROR_SUCCESS) {
    LOG(ERROR) << "Unable to write to registry, error " << result;
    return false;
  }

  return true;
}

}  // namespace browser_watcher
