1// Copyright (c) 2011 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/common/service_process_util.h"
6
7#include "base/callback.h"
8#include "base/command_line.h"
9#include "base/files/file_path.h"
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/path_service.h"
13#include "base/strings/string16.h"
14#include "base/strings/utf_string_conversions.h"
15#include "base/win/object_watcher.h"
16#include "base/win/scoped_handle.h"
17#include "base/win/win_util.h"
18#include "chrome/common/chrome_paths.h"
19#include "chrome/common/chrome_switches.h"
20
21namespace {
22
23const char* kTerminateEventSuffix = "_service_terminate_evt";
24
25base::string16 GetServiceProcessReadyEventName() {
26  return base::UTF8ToWide(
27      GetServiceProcessScopedVersionedName("_service_ready"));
28}
29
30base::string16 GetServiceProcessTerminateEventName() {
31  return base::UTF8ToWide(
32      GetServiceProcessScopedVersionedName(kTerminateEventSuffix));
33}
34
35std::string GetServiceProcessAutoRunKey() {
36  return GetServiceProcessScopedName("_service_run");
37}
38
39// Returns the name of the autotun reg value that we used to use for older
40// versions of Chrome.
41std::string GetObsoleteServiceProcessAutoRunKey() {
42  base::FilePath user_data_dir;
43  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
44  std::string scoped_name = base::WideToUTF8(user_data_dir.value());
45  std::replace(scoped_name.begin(), scoped_name.end(), '\\', '!');
46  std::replace(scoped_name.begin(), scoped_name.end(), '/', '!');
47  scoped_name.append("_service_run");
48  return scoped_name;
49}
50
51class ServiceProcessTerminateMonitor
52    : public base::win::ObjectWatcher::Delegate {
53 public:
54  explicit ServiceProcessTerminateMonitor(const base::Closure& terminate_task)
55      : terminate_task_(terminate_task) {
56  }
57  void Start() {
58    base::string16 event_name = GetServiceProcessTerminateEventName();
59    DCHECK(event_name.length() <= MAX_PATH);
60    terminate_event_.Set(CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
61    watcher_.StartWatching(terminate_event_.Get(), this);
62  }
63
64  // base::ObjectWatcher::Delegate implementation.
65  virtual void OnObjectSignaled(HANDLE object) {
66    if (!terminate_task_.is_null()) {
67      terminate_task_.Run();
68      terminate_task_.Reset();
69    }
70  }
71
72 private:
73  base::win::ScopedHandle terminate_event_;
74  base::win::ObjectWatcher watcher_;
75  base::Closure terminate_task_;
76};
77
78}  // namespace
79
80// Gets the name of the service process IPC channel.
81IPC::ChannelHandle GetServiceProcessChannel() {
82  return GetServiceProcessScopedVersionedName("_service_ipc");
83}
84
85bool ForceServiceProcessShutdown(const std::string& version,
86                                 base::ProcessId process_id) {
87  base::win::ScopedHandle terminate_event;
88  std::string versioned_name = version;
89  versioned_name.append(kTerminateEventSuffix);
90  base::string16 event_name =
91      base::UTF8ToWide(GetServiceProcessScopedName(versioned_name));
92  terminate_event.Set(OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name.c_str()));
93  if (!terminate_event.IsValid())
94    return false;
95  SetEvent(terminate_event.Get());
96  return true;
97}
98
99bool CheckServiceProcessReady() {
100  base::string16 event_name = GetServiceProcessReadyEventName();
101  base::win::ScopedHandle event(
102      OpenEvent(SYNCHRONIZE | READ_CONTROL, false, event_name.c_str()));
103  if (!event.IsValid())
104    return false;
105  // Check if the event is signaled.
106  return WaitForSingleObject(event.Get(), 0) == WAIT_OBJECT_0;
107}
108
109struct ServiceProcessState::StateData {
110  // An event that is signaled when a service process is ready.
111  base::win::ScopedHandle ready_event;
112  scoped_ptr<ServiceProcessTerminateMonitor> terminate_monitor;
113};
114
115void ServiceProcessState::CreateState() {
116  DCHECK(!state_);
117  state_ = new StateData;
118}
119
120bool ServiceProcessState::TakeSingletonLock() {
121  DCHECK(state_);
122  base::string16 event_name = GetServiceProcessReadyEventName();
123  DCHECK(event_name.length() <= MAX_PATH);
124  base::win::ScopedHandle service_process_ready_event;
125  service_process_ready_event.Set(
126      CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
127  DWORD error = GetLastError();
128  if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED))
129    return false;
130  DCHECK(service_process_ready_event.IsValid());
131  state_->ready_event.Set(service_process_ready_event.Take());
132  return true;
133}
134
135bool ServiceProcessState::SignalReady(
136    base::MessageLoopProxy* message_loop_proxy,
137    const base::Closure& terminate_task) {
138  DCHECK(state_);
139  DCHECK(state_->ready_event.IsValid());
140  if (!SetEvent(state_->ready_event.Get())) {
141    return false;
142  }
143  if (!terminate_task.is_null()) {
144    state_->terminate_monitor.reset(
145        new ServiceProcessTerminateMonitor(terminate_task));
146    state_->terminate_monitor->Start();
147  }
148  return true;
149}
150
151bool ServiceProcessState::AddToAutoRun() {
152  DCHECK(autorun_command_line_.get());
153  // Remove the old autorun value first because we changed the naming scheme
154  // for the autorun value name.
155  base::win::RemoveCommandFromAutoRun(
156      HKEY_CURRENT_USER,
157      base::UTF8ToWide(GetObsoleteServiceProcessAutoRunKey()));
158  return base::win::AddCommandToAutoRun(
159      HKEY_CURRENT_USER,
160      base::UTF8ToWide(GetServiceProcessAutoRunKey()),
161      autorun_command_line_->GetCommandLineString());
162}
163
164bool ServiceProcessState::RemoveFromAutoRun() {
165  // Remove the old autorun value first because we changed the naming scheme
166  // for the autorun value name.
167  base::win::RemoveCommandFromAutoRun(
168      HKEY_CURRENT_USER,
169      base::UTF8ToWide(GetObsoleteServiceProcessAutoRunKey()));
170  return base::win::RemoveCommandFromAutoRun(
171      HKEY_CURRENT_USER, base::UTF8ToWide(GetServiceProcessAutoRunKey()));
172}
173
174void ServiceProcessState::TearDownState() {
175  delete state_;
176  state_ = NULL;
177}
178