Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(66)

Side by Side Diff: remoting/host/setup/daemon_controller_delegate_win.cc

Issue 23606019: Refactor the daemon controller so that the callbacks are called on the caller thread. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix the license Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/setup/daemon_controller.h" 5 #include "remoting/host/setup/daemon_controller_delegate_win.h"
6 6
7 #include "base/basictypes.h" 7 #include "base/basictypes.h"
8 #include "base/bind.h" 8 #include "base/bind.h"
9 #include "base/bind_helpers.h" 9 #include "base/bind_helpers.h"
10 #include "base/compiler_specific.h" 10 #include "base/compiler_specific.h"
11 #include "base/file_util.h"
12 #include "base/files/file_path.h"
13 #include "base/json/json_reader.h" 11 #include "base/json/json_reader.h"
14 #include "base/json/json_writer.h" 12 #include "base/json/json_writer.h"
15 #include "base/logging.h" 13 #include "base/logging.h"
16 #include "base/strings/string16.h" 14 #include "base/strings/string16.h"
17 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread.h" 16 #include "base/thread_task_runner_handle.h"
19 #include "base/time/time.h" 17 #include "base/time/time.h"
20 #include "base/timer/timer.h" 18 #include "base/timer/timer.h"
21 #include "base/values.h" 19 #include "base/values.h"
22 #include "base/win/scoped_bstr.h" 20 #include "base/win/scoped_bstr.h"
23 #include "base/win/scoped_comptr.h" 21 #include "base/win/scoped_comptr.h"
24 #include "base/win/windows_version.h" 22 #include "base/win/windows_version.h"
25 #include "remoting/base/scoped_sc_handle_win.h" 23 #include "remoting/base/scoped_sc_handle_win.h"
26 #include "remoting/host/branding.h" 24 #include "remoting/host/branding.h"
27 // chromoting_lib.h contains MIDL-generated declarations. 25 // chromoting_lib.h contains MIDL-generated declarations.
28 #include "remoting/host/chromoting_lib.h" 26 #include "remoting/host/chromoting_lib.h"
29 #include "remoting/host/setup/daemon_installer_win.h" 27 #include "remoting/host/setup/daemon_installer_win.h"
30 #include "remoting/host/usage_stats_consent.h" 28 #include "remoting/host/usage_stats_consent.h"
31 29
32 using base::win::ScopedBstr; 30 using base::win::ScopedBstr;
33 using base::win::ScopedComPtr; 31 using base::win::ScopedComPtr;
34 32
35 namespace remoting { 33 namespace remoting {
36 34
37 namespace { 35 namespace {
38 36
39 // ProgID of the daemon controller. 37 // ProgID of the daemon controller.
40 const wchar_t kDaemonController[] = 38 const wchar_t kDaemonController[] =
41 L"ChromotingElevatedController.ElevatedController"; 39 L"ChromotingElevatedController.ElevatedController";
42 40
43 // The COM elevation moniker for the Elevated Controller. 41 // The COM elevation moniker for the Elevated Controller.
44 const wchar_t kDaemonControllerElevationMoniker[] = 42 const wchar_t kDaemonControllerElevationMoniker[] =
45 L"Elevation:Administrator!new:" 43 L"Elevation:Administrator!new:"
46 L"ChromotingElevatedController.ElevatedController"; 44 L"ChromotingElevatedController.ElevatedController";
47 45
48 // Name of the Daemon Controller's worker thread.
49 const char kDaemonControllerThreadName[] = "Daemon Controller thread";
50
51 // The maximum duration of keeping a reference to a privileged instance of 46 // The maximum duration of keeping a reference to a privileged instance of
52 // the Daemon Controller. This effectively reduces number of UAC prompts a user 47 // the Daemon Controller. This effectively reduces number of UAC prompts a user
53 // sees. 48 // sees.
54 const int kPrivilegedTimeoutSec = 5 * 60; 49 const int kPrivilegedTimeoutSec = 5 * 60;
55 50
56 // The maximum duration of keeping a reference to an unprivileged instance of 51 // The maximum duration of keeping a reference to an unprivileged instance of
57 // the Daemon Controller. This interval should not be too long. If upgrade 52 // the Daemon Controller. This interval should not be too long. If upgrade
58 // happens while there is a live reference to a Daemon Controller instance 53 // happens while there is a live reference to a Daemon Controller instance
59 // the old binary still can be used. So dropping the references often makes sure 54 // the old binary still can be used. So dropping the references often makes sure
60 // that the old binary will go away sooner. 55 // that the old binary will go away sooner.
61 const int kUnprivilegedTimeoutSec = 60; 56 const int kUnprivilegedTimeoutSec = 60;
62 57
63 class DaemonControllerWin : public remoting::DaemonController { 58 void ConfigToString(const base::DictionaryValue& config, ScopedBstr* out) {
64 public: 59 std::string config_str;
65 DaemonControllerWin(); 60 base::JSONWriter::Write(&config, &config_str);
66 virtual ~DaemonControllerWin(); 61 ScopedBstr config_scoped_bstr(UTF8ToUTF16(config_str).c_str());
62 out->Swap(config_scoped_bstr);
63 }
67 64
68 virtual State GetState() OVERRIDE; 65 DaemonController::State ConvertToDaemonState(DWORD service_state) {
69 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; 66 switch (service_state) {
70 virtual void SetConfigAndStart( 67 case SERVICE_RUNNING:
71 scoped_ptr<base::DictionaryValue> config, 68 return DaemonController::STATE_STARTED;
72 bool consent,
73 const CompletionCallback& done) OVERRIDE;
74 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config,
75 const CompletionCallback& done_callback) OVERRIDE;
76 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE;
77 virtual void SetWindow(void* window_handle) OVERRIDE;
78 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE;
79 virtual void GetUsageStatsConsent(
80 const GetUsageStatsConsentCallback& done) OVERRIDE;
81 69
82 private: 70 case SERVICE_CONTINUE_PENDING:
83 // Activates an unprivileged instance of the daemon controller and caches it. 71 case SERVICE_START_PENDING:
84 HRESULT ActivateController(); 72 return DaemonController::STATE_STARTING;
73 break;
85 74
86 // Activates an instance of the daemon controller and caches it. If COM 75 case SERVICE_PAUSE_PENDING:
87 // Elevation is supported (Vista+) the activated instance is elevated, 76 case SERVICE_STOP_PENDING:
88 // otherwise it is activated under credentials of the caller. 77 return DaemonController::STATE_STOPPING;
89 HRESULT ActivateElevatedController(); 78 break;
90 79
91 // Releases the cached instance of the controller. 80 case SERVICE_PAUSED:
92 void ReleaseController(); 81 case SERVICE_STOPPED:
82 return DaemonController::STATE_STOPPED;
83 break;
93 84
94 // Procedes with the daemon configuration if the installation succeeded, 85 default:
95 // otherwise reports the error. 86 NOTREACHED();
96 void OnInstallationComplete(scoped_ptr<base::DictionaryValue> config, 87 return DaemonController::STATE_UNKNOWN;
97 bool consent,
98 const CompletionCallback& done,
99 HRESULT result);
100
101 // Opens the Chromoting service returning its handle in |service_out|.
102 DWORD OpenService(ScopedScHandle* service_out);
103
104 // Converts a config dictionary to a scoped BSTR.
105 static void ConfigToString(const base::DictionaryValue& config,
106 ScopedBstr* out);
107
108 // Converts a Windows service status code to a Daemon state.
109 static State ConvertToDaemonState(DWORD service_state);
110
111 // Converts HRESULT to the AsyncResult.
112 static AsyncResult HResultToAsyncResult(HRESULT hr);
113
114 // The functions that actually do the work. They should be called in
115 // the context of |worker_thread_|;
116 void DoGetConfig(const GetConfigCallback& callback);
117 void DoInstallAsNeededAndStart(scoped_ptr<base::DictionaryValue> config,
118 bool consent,
119 const CompletionCallback& done_callback);
120 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config,
121 bool consent,
122 const CompletionCallback& done);
123 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config,
124 const CompletionCallback& done_callback);
125 void DoStop(const CompletionCallback& done_callback);
126 void DoSetWindow(void* window_handle);
127 void DoGetVersion(const GetVersionCallback& callback);
128 void DoGetUsageStatsConsent(
129 const GetUsageStatsConsentCallback& done);
130
131 // |control_| and |control2_| hold references to an instance of the daemon
132 // controller to prevent a UAC prompt on every operation.
133 ScopedComPtr<IDaemonControl> control_;
134 ScopedComPtr<IDaemonControl2> control2_;
135
136 // True if |control_| holds a reference to an elevated instance of the daemon
137 // controller.
138 bool control_is_elevated_;
139
140 // This timer is used to release |control_| after a timeout.
141 scoped_ptr<base::OneShotTimer<DaemonControllerWin> > release_timer_;
142
143 // Handle of the plugin window.
144 HWND window_handle_;
145
146 // The worker thread used for servicing long running operations.
147 base::Thread worker_thread_;
148
149 scoped_ptr<DaemonInstallerWin> installer_;
150
151 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin);
152 };
153
154 DaemonControllerWin::DaemonControllerWin()
155 : control_is_elevated_(false),
156 window_handle_(NULL),
157 worker_thread_(kDaemonControllerThreadName) {
158 worker_thread_.init_com_with_mta(false);
159 if (!worker_thread_.Start()) {
160 LOG(FATAL) << "Failed to start the Daemon Controller worker thread.";
161 } 88 }
162 } 89 }
163 90
164 DaemonControllerWin::~DaemonControllerWin() { 91 DWORD OpenService(ScopedScHandle* service_out) {
165 // Clean up resources allocated on the worker thread. 92 // Open the service and query its current state.
166 worker_thread_.message_loop_proxy()->PostTask( 93 ScopedScHandle scmanager(
167 FROM_HERE, 94 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
168 base::Bind(&DaemonControllerWin::ReleaseController, 95 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
169 base::Unretained(this))); 96 if (!scmanager.IsValid()) {
170 worker_thread_.Stop(); 97 DWORD error = GetLastError();
98 LOG_GETLASTERROR(ERROR)
99 << "Failed to connect to the service control manager";
100 return error;
101 }
102
103 ScopedScHandle service(
104 ::OpenServiceW(scmanager, kWindowsServiceName, SERVICE_QUERY_STATUS));
105 if (!service.IsValid()) {
106 DWORD error = GetLastError();
107 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
108 LOG_GETLASTERROR(ERROR)
109 << "Failed to open to the '" << kWindowsServiceName << "' service";
110 }
111 return error;
112 }
113
114 service_out->Set(service.Take());
115 return ERROR_SUCCESS;
171 } 116 }
172 117
173 remoting::DaemonController::State DaemonControllerWin::GetState() { 118 DaemonController::AsyncResult HResultToAsyncResult(
119 HRESULT hr) {
120 if (SUCCEEDED(hr)) {
121 return DaemonController::RESULT_OK;
122 } else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
123 return DaemonController::RESULT_CANCELLED;
124 } else {
125 // TODO(sergeyu): Report other errors to the webapp once it knows
126 // how to handle them.
127 return DaemonController::RESULT_FAILED;
128 }
129 }
130
131 } // namespace
132
133 DaemonControllerDelegateWin::DaemonControllerDelegateWin()
134 : control_is_elevated_(false),
135 window_handle_(NULL) {
136 }
137
138 DaemonControllerDelegateWin::~DaemonControllerDelegateWin() {
139 }
140
141 DaemonController::State DaemonControllerDelegateWin::GetState() {
174 if (base::win::GetVersion() < base::win::VERSION_XP) { 142 if (base::win::GetVersion() < base::win::VERSION_XP) {
175 return STATE_NOT_IMPLEMENTED; 143 return DaemonController::STATE_NOT_IMPLEMENTED;
176 } 144 }
177 // TODO(alexeypa): Make the thread alertable, so we can switch to APC 145 // TODO(alexeypa): Make the thread alertable, so we can switch to APC
178 // notifications rather than polling. 146 // notifications rather than polling.
179 ScopedScHandle service; 147 ScopedScHandle service;
180 DWORD error = OpenService(&service); 148 DWORD error = OpenService(&service);
181 149
182 switch (error) { 150 switch (error) {
183 case ERROR_SUCCESS: { 151 case ERROR_SUCCESS: {
184 SERVICE_STATUS status; 152 SERVICE_STATUS status;
185 if (::QueryServiceStatus(service, &status)) { 153 if (::QueryServiceStatus(service, &status)) {
186 return ConvertToDaemonState(status.dwCurrentState); 154 return ConvertToDaemonState(status.dwCurrentState);
187 } else { 155 } else {
188 LOG_GETLASTERROR(ERROR) 156 LOG_GETLASTERROR(ERROR)
189 << "Failed to query the state of the '" << kWindowsServiceName 157 << "Failed to query the state of the '" << kWindowsServiceName
190 << "' service"; 158 << "' service";
191 return STATE_UNKNOWN; 159 return DaemonController::STATE_UNKNOWN;
192 } 160 }
193 break; 161 break;
194 } 162 }
195 case ERROR_SERVICE_DOES_NOT_EXIST: 163 case ERROR_SERVICE_DOES_NOT_EXIST:
196 return STATE_NOT_INSTALLED; 164 return DaemonController::STATE_NOT_INSTALLED;
197 default: 165 default:
198 return STATE_UNKNOWN; 166 return DaemonController::STATE_UNKNOWN;
199 } 167 }
200 } 168 }
201 169
202 void DaemonControllerWin::GetConfig(const GetConfigCallback& callback) { 170 scoped_ptr<base::DictionaryValue> DaemonControllerDelegateWin::GetConfig() {
203 worker_thread_.message_loop_proxy()->PostTask( 171 // Configure and start the Daemon Controller if it is installed already.
204 FROM_HERE, 172 HRESULT hr = ActivateController();
205 base::Bind(&DaemonControllerWin::DoGetConfig, 173 if (FAILED(hr))
206 base::Unretained(this), callback)); 174 return scoped_ptr<base::DictionaryValue>();
175
176 // Get the host configuration.
177 ScopedBstr host_config;
178 hr = control_->GetConfig(host_config.Receive());
179 if (FAILED(hr))
180 return scoped_ptr<base::DictionaryValue>();
181
182 // Parse the string into a dictionary.
183 string16 file_content(static_cast<BSTR>(host_config), host_config.Length());
184 scoped_ptr<base::Value> config(
185 base::JSONReader::Read(UTF16ToUTF8(file_content),
186 base::JSON_ALLOW_TRAILING_COMMAS));
187
188 if (!config || config->GetType() != base::Value::TYPE_DICTIONARY)
189 return scoped_ptr<base::DictionaryValue>();
190
191 return scoped_ptr<base::DictionaryValue>(
192 static_cast<base::DictionaryValue*>(config.release()));
207 } 193 }
208 194
209 void DaemonControllerWin::SetConfigAndStart( 195 void DaemonControllerDelegateWin::SetConfigAndStart(
210 scoped_ptr<base::DictionaryValue> config, 196 scoped_ptr<base::DictionaryValue> config,
211 bool consent, 197 bool consent,
212 const CompletionCallback& done) { 198 const DaemonController::CompletionCallback& done) {
213 worker_thread_.message_loop_proxy()->PostTask( 199 // Configure and start the Daemon Controller if it is installed already.
214 FROM_HERE, base::Bind( 200 HRESULT hr = ActivateElevatedController();
215 &DaemonControllerWin::DoInstallAsNeededAndStart, 201 if (SUCCEEDED(hr)) {
216 base::Unretained(this), base::Passed(&config), consent, done)); 202 OnInstallationComplete(config.Pass(), consent, done, S_OK);
203 return;
204 }
205
206 // Otherwise, install it if its COM registration entry is missing.
207 if (hr == CO_E_CLASSSTRING) {
208 DCHECK(!installer_);
209
210 installer_ = DaemonInstallerWin::Create(
211 GetTopLevelWindow(window_handle_),
212 base::Bind(&DaemonControllerDelegateWin::OnInstallationComplete,
213 base::Unretained(this),
214 base::Passed(&config),
215 consent,
216 done));
217 installer_->Install();
218 return;
219 }
220
221 LOG(ERROR) << "Failed to initiate the Chromoting Host installation "
222 << "(error: 0x" << std::hex << hr << std::dec << ").";
223 done.Run(HResultToAsyncResult(hr));
217 } 224 }
218 225
219 void DaemonControllerWin::UpdateConfig( 226 void DaemonControllerDelegateWin::UpdateConfig(
220 scoped_ptr<base::DictionaryValue> config, 227 scoped_ptr<base::DictionaryValue> config,
221 const CompletionCallback& done_callback) { 228 const DaemonController::CompletionCallback& done) {
222 worker_thread_.message_loop_proxy()->PostTask( 229 HRESULT hr = ActivateElevatedController();
223 FROM_HERE, base::Bind( 230 if (FAILED(hr)) {
224 &DaemonControllerWin::DoUpdateConfig, 231 done.Run(HResultToAsyncResult(hr));
225 base::Unretained(this), base::Passed(&config), done_callback)); 232 return;
233 }
234
235 // Update the configuration.
236 ScopedBstr config_str(NULL);
237 ConfigToString(*config, &config_str);
238 if (config_str == NULL) {
239 done.Run(HResultToAsyncResult(E_OUTOFMEMORY));
240 return;
241 }
242
243 // Make sure that the PIN confirmation dialog is focused properly.
244 hr = control_->SetOwnerWindow(
245 reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_)));
246 if (FAILED(hr)) {
247 done.Run(HResultToAsyncResult(hr));
248 return;
249 }
250
251 hr = control_->UpdateConfig(config_str);
252 done.Run(HResultToAsyncResult(hr));
226 } 253 }
227 254
228 void DaemonControllerWin::Stop(const CompletionCallback& done_callback) { 255 void DaemonControllerDelegateWin::Stop(
229 worker_thread_.message_loop_proxy()->PostTask( 256 const DaemonController::CompletionCallback& done) {
230 FROM_HERE, base::Bind( 257 HRESULT hr = ActivateElevatedController();
231 &DaemonControllerWin::DoStop, base::Unretained(this), 258 if (SUCCEEDED(hr))
232 done_callback)); 259 hr = control_->StopDaemon();
260
261 done.Run(HResultToAsyncResult(hr));
233 } 262 }
234 263
235 void DaemonControllerWin::SetWindow(void* window_handle) { 264 void DaemonControllerDelegateWin::SetWindow(void* window_handle) {
236 worker_thread_.message_loop_proxy()->PostTask( 265 window_handle_ = reinterpret_cast<HWND>(window_handle);
237 FROM_HERE, base::Bind(
238 &DaemonControllerWin::DoSetWindow, base::Unretained(this),
239 window_handle));
240 } 266 }
241 267
242 void DaemonControllerWin::GetVersion(const GetVersionCallback& callback) { 268 std::string DaemonControllerDelegateWin::GetVersion() {
243 worker_thread_.message_loop_proxy()->PostTask( 269 // Configure and start the Daemon Controller if it is installed already.
244 FROM_HERE, 270 HRESULT hr = ActivateController();
245 base::Bind(&DaemonControllerWin::DoGetVersion, 271 if (FAILED(hr))
246 base::Unretained(this), callback)); 272 return std::string();
273
274 // Get the version string.
275 ScopedBstr version;
276 hr = control_->GetVersion(version.Receive());
277 if (FAILED(hr))
278 return std::string();
279
280 return UTF16ToUTF8(string16(static_cast<BSTR>(version), version.Length()));
247 } 281 }
248 282
249 void DaemonControllerWin::GetUsageStatsConsent( 283 DaemonController::UsageStatsConsent
250 const GetUsageStatsConsentCallback& done) { 284 DaemonControllerDelegateWin::GetUsageStatsConsent() {
251 worker_thread_.message_loop_proxy()->PostTask( 285 DaemonController::UsageStatsConsent consent;
252 FROM_HERE, 286 consent.supported = true;
253 base::Bind(&DaemonControllerWin::DoGetUsageStatsConsent, 287 consent.allowed = false;
254 base::Unretained(this), done)); 288 consent.set_by_policy = false;
289
290 // Activate the Daemon Controller and see if it supports |IDaemonControl2|.
291 HRESULT hr = ActivateController();
292 if (FAILED(hr)) {
293 // The host is not installed yet. Assume that the user didn't consent to
294 // collecting crash dumps.
295 return consent;
296 }
297
298 if (control2_.get() == NULL) {
299 // The host is installed and does not support crash dump reporting.
300 return consent;
301 }
302
303 // Get the recorded user's consent.
304 BOOL allowed;
305 BOOL set_by_policy;
306 hr = control2_->GetUsageStatsConsent(&allowed, &set_by_policy);
307 if (FAILED(hr)) {
308 // If the user's consent is not recorded yet, assume that the user didn't
309 // consent to collecting crash dumps.
310 return consent;
311 }
312
313 consent.allowed = !!allowed;
314 consent.set_by_policy = !!set_by_policy;
315 return consent;
255 } 316 }
256 317
257 HRESULT DaemonControllerWin::ActivateController() { 318 HRESULT DaemonControllerDelegateWin::ActivateController() {
258 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); 319 if (!control_) {
259
260 if (control_.get() == NULL) {
261 CLSID class_id; 320 CLSID class_id;
262 HRESULT hr = CLSIDFromProgID(kDaemonController, &class_id); 321 HRESULT hr = CLSIDFromProgID(kDaemonController, &class_id);
263 if (FAILED(hr)) { 322 if (FAILED(hr)) {
264 return hr; 323 return hr;
265 } 324 }
266 325
267 hr = CoCreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER, 326 hr = CoCreateInstance(class_id, NULL, CLSCTX_LOCAL_SERVER,
268 IID_IDaemonControl, control_.ReceiveVoid()); 327 IID_IDaemonControl, control_.ReceiveVoid());
269 if (FAILED(hr)) { 328 if (FAILED(hr)) {
270 return hr; 329 return hr;
271 } 330 }
272 331
273 // Ignore the error. IID_IDaemonControl2 is optional. 332 // Ignore the error. IID_IDaemonControl2 is optional.
274 control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid()); 333 control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
275 334
276 // Release |control_| upon expiration of the timeout. 335 // Release |control_| upon expiration of the timeout.
277 release_timer_.reset(new base::OneShotTimer<DaemonControllerWin>()); 336 release_timer_.reset(new base::OneShotTimer<DaemonControllerDelegateWin>());
278 release_timer_->Start(FROM_HERE, 337 release_timer_->Start(FROM_HERE,
279 base::TimeDelta::FromSeconds(kUnprivilegedTimeoutSec), 338 base::TimeDelta::FromSeconds(kUnprivilegedTimeoutSec),
280 this, 339 this,
281 &DaemonControllerWin::ReleaseController); 340 &DaemonControllerDelegateWin::ReleaseController);
282 } 341 }
283 342
284 return S_OK; 343 return S_OK;
285 } 344 }
286 345
287 HRESULT DaemonControllerWin::ActivateElevatedController() { 346 HRESULT DaemonControllerDelegateWin::ActivateElevatedController() {
288 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
289
290 // The COM elevation is supported on Vista and above. 347 // The COM elevation is supported on Vista and above.
291 if (base::win::GetVersion() < base::win::VERSION_VISTA) { 348 if (base::win::GetVersion() < base::win::VERSION_VISTA)
292 return ActivateController(); 349 return ActivateController();
293 }
294 350
295 // Release an unprivileged instance of the daemon controller if any. 351 // Release an unprivileged instance of the daemon controller if any.
296 if (!control_is_elevated_) { 352 if (!control_is_elevated_)
297 ReleaseController(); 353 ReleaseController();
298 }
299 354
300 if (control_.get() == NULL) { 355 if (!control_) {
301 BIND_OPTS3 bind_options; 356 BIND_OPTS3 bind_options;
302 memset(&bind_options, 0, sizeof(bind_options)); 357 memset(&bind_options, 0, sizeof(bind_options));
303 bind_options.cbStruct = sizeof(bind_options); 358 bind_options.cbStruct = sizeof(bind_options);
304 bind_options.hwnd = GetTopLevelWindow(window_handle_); 359 bind_options.hwnd = GetTopLevelWindow(window_handle_);
305 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER; 360 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER;
306 361
307 HRESULT hr = ::CoGetObject( 362 HRESULT hr = ::CoGetObject(
308 kDaemonControllerElevationMoniker, 363 kDaemonControllerElevationMoniker,
309 &bind_options, 364 &bind_options,
310 IID_IDaemonControl, 365 IID_IDaemonControl,
311 control_.ReceiveVoid()); 366 control_.ReceiveVoid());
312 if (FAILED(hr)) { 367 if (FAILED(hr)) {
313 return hr; 368 return hr;
314 } 369 }
315 370
316 // Ignore the error. IID_IDaemonControl2 is optional. 371 // Ignore the error. IID_IDaemonControl2 is optional.
317 control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid()); 372 control_.QueryInterface(IID_IDaemonControl2, control2_.ReceiveVoid());
318 373
319 // Note that we hold a reference to an elevated instance now. 374 // Note that we hold a reference to an elevated instance now.
320 control_is_elevated_ = true; 375 control_is_elevated_ = true;
321 376
322 // Release |control_| upon expiration of the timeout. 377 // Release |control_| upon expiration of the timeout.
323 release_timer_.reset(new base::OneShotTimer<DaemonControllerWin>()); 378 release_timer_.reset(new base::OneShotTimer<DaemonControllerDelegateWin>());
324 release_timer_->Start(FROM_HERE, 379 release_timer_->Start(FROM_HERE,
325 base::TimeDelta::FromSeconds(kPrivilegedTimeoutSec), 380 base::TimeDelta::FromSeconds(kPrivilegedTimeoutSec),
326 this, 381 this,
327 &DaemonControllerWin::ReleaseController); 382 &DaemonControllerDelegateWin::ReleaseController);
328 } 383 }
329 384
330 return S_OK; 385 return S_OK;
331 } 386 }
332 387
333 void DaemonControllerWin::ReleaseController() { 388 void DaemonControllerDelegateWin::ReleaseController() {
334 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
335
336 control_.Release(); 389 control_.Release();
337 control2_.Release(); 390 control2_.Release();
338 release_timer_.reset(); 391 release_timer_.reset();
339 control_is_elevated_ = false; 392 control_is_elevated_ = false;
340 } 393 }
341 394
342 void DaemonControllerWin::OnInstallationComplete( 395 void DaemonControllerDelegateWin::OnInstallationComplete(
343 scoped_ptr<base::DictionaryValue> config, 396 scoped_ptr<base::DictionaryValue> config,
344 bool consent, 397 bool consent,
345 const CompletionCallback& done, 398 const DaemonController::CompletionCallback& done,
346 HRESULT result) { 399 HRESULT hr) {
347 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); 400 installer_.reset();
348 401
349 if (SUCCEEDED(result)) { 402 if (FAILED(hr)) {
350 DoSetConfigAndStart(config.Pass(), consent, done);
351 } else {
352 LOG(ERROR) << "Failed to install the Chromoting Host " 403 LOG(ERROR) << "Failed to install the Chromoting Host "
353 << "(error: 0x" << std::hex << result << std::dec << ")."; 404 << "(error: 0x" << std::hex << hr << std::dec << ").";
354 done.Run(HResultToAsyncResult(result)); 405 done.Run(HResultToAsyncResult(hr));
355 }
356
357 DCHECK(installer_.get() != NULL);
358 installer_.reset();
359 }
360
361 DWORD DaemonControllerWin::OpenService(ScopedScHandle* service_out) {
362 // Open the service and query its current state.
363 ScopedScHandle scmanager(
364 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
365 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
366 if (!scmanager.IsValid()) {
367 DWORD error = GetLastError();
368 LOG_GETLASTERROR(ERROR)
369 << "Failed to connect to the service control manager";
370 return error;
371 }
372
373 ScopedScHandle service(
374 ::OpenServiceW(scmanager, kWindowsServiceName, SERVICE_QUERY_STATUS));
375 if (!service.IsValid()) {
376 DWORD error = GetLastError();
377 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
378 LOG_GETLASTERROR(ERROR)
379 << "Failed to open to the '" << kWindowsServiceName << "' service";
380 }
381 return error;
382 }
383
384 service_out->Set(service.Take());
385 return ERROR_SUCCESS;
386 }
387
388 // static
389 void DaemonControllerWin::ConfigToString(const base::DictionaryValue& config,
390 ScopedBstr* out) {
391 std::string config_str;
392 base::JSONWriter::Write(&config, &config_str);
393 ScopedBstr config_scoped_bstr(UTF8ToUTF16(config_str).c_str());
394 out->Swap(config_scoped_bstr);
395 }
396
397 // static
398 remoting::DaemonController::State DaemonControllerWin::ConvertToDaemonState(
399 DWORD service_state) {
400 switch (service_state) {
401 case SERVICE_RUNNING:
402 return STATE_STARTED;
403
404 case SERVICE_CONTINUE_PENDING:
405 case SERVICE_START_PENDING:
406 return STATE_STARTING;
407 break;
408
409 case SERVICE_PAUSE_PENDING:
410 case SERVICE_STOP_PENDING:
411 return STATE_STOPPING;
412 break;
413
414 case SERVICE_PAUSED:
415 case SERVICE_STOPPED:
416 return STATE_STOPPED;
417 break;
418
419 default:
420 NOTREACHED();
421 return STATE_UNKNOWN;
422 }
423 }
424
425 // static
426 DaemonController::AsyncResult DaemonControllerWin::HResultToAsyncResult(
427 HRESULT hr) {
428 if (SUCCEEDED(hr)) {
429 return RESULT_OK;
430 } else if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) {
431 return RESULT_CANCELLED;
432 } else {
433 // TODO(sergeyu): Report other errors to the webapp once it knows
434 // how to handle them.
435 return RESULT_FAILED;
436 }
437 }
438
439 void DaemonControllerWin::DoGetConfig(const GetConfigCallback& callback) {
440 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
441
442 scoped_ptr<base::DictionaryValue> dictionary_null;
443
444 // Configure and start the Daemon Controller if it is installed already.
445 HRESULT hr = ActivateController();
446 if (FAILED(hr)) {
447 callback.Run(dictionary_null.Pass());
448 return; 406 return;
449 } 407 }
450 408
451 // Get the host configuration. 409 hr = ActivateElevatedController();
452 ScopedBstr host_config;
453 hr = control_->GetConfig(host_config.Receive());
454 if (FAILED(hr)) {
455 callback.Run(dictionary_null.Pass());
456 return;
457 }
458
459 // Parse the string into a dictionary.
460 string16 file_content(static_cast<BSTR>(host_config), host_config.Length());
461 scoped_ptr<base::Value> config(
462 base::JSONReader::Read(UTF16ToUTF8(file_content),
463 base::JSON_ALLOW_TRAILING_COMMAS));
464
465 base::DictionaryValue* dictionary;
466 if (config.get() == NULL || !config->GetAsDictionary(&dictionary)) {
467 callback.Run(dictionary_null.Pass());
468 return;
469 }
470 // Release |config|, because dictionary points to the same object.
471 config.release();
472
473 callback.Run(scoped_ptr<base::DictionaryValue>(dictionary));
474 }
475
476 void DaemonControllerWin::DoInstallAsNeededAndStart(
477 scoped_ptr<base::DictionaryValue> config,
478 bool consent,
479 const CompletionCallback& done) {
480 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
481
482 // Configure and start the Daemon Controller if it is installed already.
483 HRESULT hr = ActivateElevatedController();
484 if (SUCCEEDED(hr)) {
485 DoSetConfigAndStart(config.Pass(), consent, done);
486 return;
487 }
488
489 // Otherwise, install it if its COM registration entry is missing.
490 if (hr == CO_E_CLASSSTRING) {
491 scoped_ptr<DaemonInstallerWin> installer = DaemonInstallerWin::Create(
492 GetTopLevelWindow(window_handle_),
493 base::Bind(&DaemonControllerWin::OnInstallationComplete,
494 base::Unretained(this),
495 base::Passed(&config),
496 consent,
497 done));
498 if (installer.get()) {
499 DCHECK(!installer_.get());
500 installer_ = installer.Pass();
501 installer_->Install();
502 }
503 } else {
504 LOG(ERROR) << "Failed to initiate the Chromoting Host installation "
505 << "(error: 0x" << std::hex << hr << std::dec << ").";
506 done.Run(HResultToAsyncResult(hr));
507 }
508 }
509
510 void DaemonControllerWin::DoSetConfigAndStart(
511 scoped_ptr<base::DictionaryValue> config,
512 bool consent,
513 const CompletionCallback& done) {
514 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
515
516 HRESULT hr = ActivateElevatedController();
517 if (FAILED(hr)) { 410 if (FAILED(hr)) {
518 done.Run(HResultToAsyncResult(hr)); 411 done.Run(HResultToAsyncResult(hr));
519 return; 412 return;
520 } 413 }
521 414
522 // Record the user's consent. 415 // Record the user's consent.
523 if (control2_.get()) { 416 if (control2_) {
524 hr = control2_->SetUsageStatsConsent(consent); 417 hr = control2_->SetUsageStatsConsent(consent);
525 if (FAILED(hr)) { 418 if (FAILED(hr)) {
526 done.Run(HResultToAsyncResult(hr)); 419 done.Run(HResultToAsyncResult(hr));
527 return; 420 return;
528 } 421 }
529 } 422 }
530 423
531 // Set the configuration. 424 // Set the configuration.
532 ScopedBstr config_str(NULL); 425 ScopedBstr config_str(NULL);
533 ConfigToString(*config, &config_str); 426 ConfigToString(*config, &config_str);
(...skipping 13 matching lines...) Expand all
547 if (FAILED(hr)) { 440 if (FAILED(hr)) {
548 done.Run(HResultToAsyncResult(hr)); 441 done.Run(HResultToAsyncResult(hr));
549 return; 442 return;
550 } 443 }
551 444
552 // Start daemon. 445 // Start daemon.
553 hr = control_->StartDaemon(); 446 hr = control_->StartDaemon();
554 done.Run(HResultToAsyncResult(hr)); 447 done.Run(HResultToAsyncResult(hr));
555 } 448 }
556 449
557 void DaemonControllerWin::DoUpdateConfig( 450 scoped_refptr<DaemonController> DaemonController::Create() {
558 scoped_ptr<base::DictionaryValue> config, 451 scoped_ptr<DaemonController::Delegate> delegate(
559 const CompletionCallback& done_callback) { 452 new DaemonControllerDelegateWin());
560 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread()); 453 return new DaemonController(delegate.Pass());
561
562 HRESULT hr = ActivateElevatedController();
563 if (FAILED(hr)) {
564 done_callback.Run(HResultToAsyncResult(hr));
565 return;
566 }
567
568 // Update the configuration.
569 ScopedBstr config_str(NULL);
570 ConfigToString(*config, &config_str);
571 if (config_str == NULL) {
572 done_callback.Run(HResultToAsyncResult(E_OUTOFMEMORY));
573 return;
574 }
575
576 // Make sure that the PIN confirmation dialog is focused properly.
577 hr = control_->SetOwnerWindow(
578 reinterpret_cast<LONG_PTR>(GetTopLevelWindow(window_handle_)));
579 if (FAILED(hr)) {
580 done_callback.Run(HResultToAsyncResult(hr));
581 return;
582 }
583
584 hr = control_->UpdateConfig(config_str);
585 done_callback.Run(HResultToAsyncResult(hr));
586 }
587
588 void DaemonControllerWin::DoStop(const CompletionCallback& done_callback) {
589 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
590
591 HRESULT hr = ActivateElevatedController();
592 if (FAILED(hr)) {
593 done_callback.Run(HResultToAsyncResult(hr));
594 return;
595 }
596
597 hr = control_->StopDaemon();
598 done_callback.Run(HResultToAsyncResult(hr));
599 }
600
601 void DaemonControllerWin::DoSetWindow(void* window_handle) {
602 window_handle_ = reinterpret_cast<HWND>(window_handle);
603 }
604
605 void DaemonControllerWin::DoGetVersion(const GetVersionCallback& callback) {
606 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
607
608 std::string version_null;
609
610 // Configure and start the Daemon Controller if it is installed already.
611 HRESULT hr = ActivateController();
612 if (FAILED(hr)) {
613 callback.Run(version_null);
614 return;
615 }
616
617 // Get the version string.
618 ScopedBstr version;
619 hr = control_->GetVersion(version.Receive());
620 if (FAILED(hr)) {
621 callback.Run(version_null);
622 return;
623 }
624
625 callback.Run(UTF16ToUTF8(
626 string16(static_cast<BSTR>(version), version.Length())));
627 }
628
629 void DaemonControllerWin::DoGetUsageStatsConsent(
630 const GetUsageStatsConsentCallback& done) {
631 DCHECK(worker_thread_.message_loop_proxy()->BelongsToCurrentThread());
632
633 // Activate the Daemon Controller and see if it supports |IDaemonControl2|.
634 HRESULT hr = ActivateController();
635 if (FAILED(hr)) {
636 // The host is not installed yet. Assume that the user didn't consent to
637 // collecting crash dumps.
638 done.Run(true, false, false);
639 return;
640 }
641
642 if (control2_.get() == NULL) {
643 // The host is installed and does not support crash dump reporting.
644 done.Run(false, false, false);
645 return;
646 }
647
648 // Get the recorded user's consent.
649 BOOL allowed;
650 BOOL set_by_policy;
651 hr = control2_->GetUsageStatsConsent(&allowed, &set_by_policy);
652 if (FAILED(hr)) {
653 // If the user's consent is not recorded yet, assume that the user didn't
654 // consent to collecting crash dumps.
655 done.Run(true, false, false);
656 return;
657 }
658
659 done.Run(true, !!allowed, !!set_by_policy);
660 }
661
662 } // namespace
663
664 scoped_ptr<DaemonController> remoting::DaemonController::Create() {
665 return scoped_ptr<DaemonController>(new DaemonControllerWin());
666 } 454 }
667 455
668 } // namespace remoting 456 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/setup/daemon_controller_delegate_win.h ('k') | remoting/host/setup/daemon_controller_linux.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698