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