summaryrefslogtreecommitdiffstats
path: root/chrome/app/chrome_watcher_command_line_win.cc
blob: 1e576261e5c6dd60e9681328ff1f2b6429f020ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// 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 "chrome/app/chrome_watcher_command_line_win.h"

#include <string>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "chrome/common/chrome_switches.h"

namespace {

const char kOnIninitializedEventHandleSwitch[] = "on-initialized-event-handle";
const char kParentHandleSwitch[] = "parent-handle";
const char kMainThreadIdSwitch[] = "main-thread-id";

void AppendHandleSwitch(const std::string& switch_name,
                        HANDLE handle,
                        base::CommandLine* command_line) {
  command_line->AppendSwitchASCII(
      switch_name, base::UintToString(reinterpret_cast<uint32_t>(handle)));
}

uint32_t ReadUintSwitch(const base::CommandLine& command_line,
                            const std::string& switch_name) {
  std::string switch_string = command_line.GetSwitchValueASCII(switch_name);
  unsigned int switch_uint = 0;
  if (switch_string.empty() ||
      !base::StringToUint(switch_string, &switch_uint)) {
    DLOG(ERROR) << "Missing or invalid " << switch_name << " argument.";
    return 0;
  }
  return switch_uint;
}

HANDLE ReadHandleFromSwitch(const base::CommandLine& command_line,
                            const std::string& switch_name) {
  return reinterpret_cast<HANDLE>(ReadUintSwitch(command_line, switch_name));
}

}  // namespace

base::CommandLine GenerateChromeWatcherCommandLine(
    const base::FilePath& chrome_exe,
    HANDLE parent_process,
    DWORD main_thread_id,
    HANDLE on_initialized_event) {
  base::CommandLine command_line(chrome_exe);
  command_line.AppendSwitchASCII(switches::kProcessType, "watcher");
  command_line.AppendSwitchASCII(kMainThreadIdSwitch,
                                 base::UintToString(main_thread_id));
  AppendHandleSwitch(kOnIninitializedEventHandleSwitch, on_initialized_event,
                     &command_line);
  AppendHandleSwitch(kParentHandleSwitch, parent_process, &command_line);
  return command_line;
}

bool InterpretChromeWatcherCommandLine(
    const base::CommandLine& command_line,
    base::win::ScopedHandle* parent_process,
    DWORD* main_thread_id,
    base::win::ScopedHandle* on_initialized_event) {
  DCHECK(on_initialized_event);
  DCHECK(parent_process);

  // For consistency, always close any existing HANDLEs here.
  on_initialized_event->Close();
  parent_process->Close();

  HANDLE parent_handle =
      ReadHandleFromSwitch(command_line, kParentHandleSwitch);
  HANDLE on_initialized_event_handle =
      ReadHandleFromSwitch(command_line, kOnIninitializedEventHandleSwitch);
  *main_thread_id = ReadUintSwitch(command_line, kMainThreadIdSwitch);

  if (parent_handle) {
    // Initial test of the handle, a zero PID indicates invalid handle, or not
    // a process handle. In this case, parsing fails and we avoid closing the
    // handle.
    DWORD process_pid = ::GetProcessId(parent_handle);
    if (process_pid == 0) {
      DLOG(ERROR) << "Invalid " << kParentHandleSwitch
                  << " argument. Can't get parent PID.";
    } else {
      parent_process->Set(parent_handle);
    }
  }

  if (on_initialized_event_handle) {
    DWORD result = ::WaitForSingleObject(on_initialized_event_handle, 0);
    if (result == WAIT_FAILED) {
      DPLOG(ERROR)
          << "Unexpected error while testing the initialization event.";
    } else if (result != WAIT_TIMEOUT) {
      DLOG(ERROR) << "Unexpected result while testing the initialization event "
                     "with WaitForSingleObject: " << result;
    } else {
      on_initialized_event->Set(on_initialized_event_handle);
    }
  }

  if (!*main_thread_id || !on_initialized_event->IsValid() ||
      !parent_process->IsValid()) {
    // If one was valid and not the other, free the valid one.
    on_initialized_event->Close();
    parent_process->Close();
    *main_thread_id = 0;
    return false;
  }

  return true;
}