blob: 0c9e7df7af0ddc03c7b8d997784168e7b0621221 [file] [log] [blame]
siggic0d0a0e2014-11-17 23:29:191// Copyright (c) 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
siggi420541c2014-11-18 23:16:325#include "components/browser_watcher/exit_code_watcher_win.h"
siggic0d0a0e2014-11-17 23:29:196
7#include "base/command_line.h"
8#include "base/logging.h"
9#include "base/process/kill.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/stringprintf.h"
12#include "base/win/registry.h"
13
14namespace browser_watcher {
15
16namespace {
17
18base::string16 GetValueName(const base::Time creation_time,
19 base::ProcessId pid) {
20 // Convert the PID and creation time to a string value unique to this
21 // process instance.
22 return base::StringPrintf(L"%d-%lld", pid, creation_time.ToInternalValue());
23}
24
25} // namespace
26
27const char ExitCodeWatcher::kParenthHandleSwitch[] = "parent-handle";
28
29ExitCodeWatcher::ExitCodeWatcher(const base::char16* registry_path) :
siggid497a1a2014-12-19 15:20:5130 registry_path_(registry_path), exit_code_(STILL_ACTIVE) {
siggic0d0a0e2014-11-17 23:29:1931}
32
33ExitCodeWatcher::~ExitCodeWatcher() {
34}
35
36bool ExitCodeWatcher::ParseArguments(const base::CommandLine& cmd_line) {
37 std::string process_handle_str =
38 cmd_line.GetSwitchValueASCII(kParenthHandleSwitch);
39 unsigned process_handle_uint = 0;
40 if (process_handle_str.empty() ||
41 !base::StringToUint(process_handle_str, &process_handle_uint)) {
42 LOG(ERROR) << "Missing or invalid parent-handle argument.";
43 return false;
44 }
45
46 HANDLE process_handle = reinterpret_cast<HANDLE>(process_handle_uint);
47 // Initial test of the handle, a zero PID indicates invalid handle, or not
48 // a process handle. In this case, bail immediately and avoid closing the
49 // handle.
50 DWORD process_pid = ::GetProcessId(process_handle);
51 if (process_pid == 0) {
52 LOG(ERROR) << "Invalid parent-handle, can't get parent PID.";
53 return false;
54 }
55
56 FILETIME creation_time = {};
57 FILETIME dummy = {};
58 if (!::GetProcessTimes(process_handle, &creation_time,
59 &dummy, &dummy, &dummy)) {
60 LOG(ERROR) << "Invalid parent handle, can't get parent process times.";
61 return false;
62 }
63
64 // Success, take ownership of the process handle.
siggi581938f2014-12-18 21:17:1565 process_ = base::Process(process_handle);
siggic0d0a0e2014-11-17 23:29:1966 process_creation_time_ = base::Time::FromFileTime(creation_time);
67
68 // Start by writing the value STILL_ACTIVE to registry, to allow detection
69 // of the case where the watcher itself is somehow terminated before it can
70 // write the process' actual exit code.
71 return WriteProcessExitCode(STILL_ACTIVE);
72}
73
74void ExitCodeWatcher::WaitForExit() {
siggid497a1a2014-12-19 15:20:5175 if (!process_.WaitForExit(&exit_code_)) {
siggic0d0a0e2014-11-17 23:29:1976 LOG(ERROR) << "Failed to wait for process.";
77 return;
78 }
siggic0d0a0e2014-11-17 23:29:1979
siggid497a1a2014-12-19 15:20:5180 WriteProcessExitCode(exit_code_);
siggic0d0a0e2014-11-17 23:29:1981}
82
83bool ExitCodeWatcher::WriteProcessExitCode(int exit_code) {
84 base::win::RegKey key(HKEY_CURRENT_USER,
85 registry_path_.c_str(),
86 KEY_WRITE);
siggi581938f2014-12-18 21:17:1587 base::string16 value_name(
88 GetValueName(process_creation_time_, process_.pid()));
siggic0d0a0e2014-11-17 23:29:1989
90 ULONG result = key.WriteValue(value_name.c_str(), exit_code);
91 if (result != ERROR_SUCCESS) {
92 LOG(ERROR) << "Unable to write to registry, error " << result;
93 return false;
94 }
95
96 return true;
97}
98
99} // namespace browser_watcher