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

Side by Side Diff: remoting/host/plugin/daemon_controller_win.cc

Issue 9953002: The me2me host is now configurable from the web UI on Windows. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed one or two nits. :-) Created 8 years, 8 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
« no previous file with comments | « remoting/host/plugin/daemon_controller.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/plugin/daemon_controller.h" 5 #include "remoting/host/plugin/daemon_controller.h"
6 6
7 #include <objbase.h>
8
7 #include "base/basictypes.h" 9 #include "base/basictypes.h"
10 #include "base/bind.h"
8 #include "base/compiler_specific.h" 11 #include "base/compiler_specific.h"
12 #include "base/file_path.h"
13 #include "base/file_util.h"
14 #include "base/json/json_reader.h"
15 #include "base/json/json_writer.h"
9 #include "base/logging.h" 16 #include "base/logging.h"
17 #include "base/synchronization/lock.h"
18 #include "base/threading/thread.h"
19 #include "base/utf_string_conversions.h"
10 #include "base/values.h" 20 #include "base/values.h"
21 #include "remoting/base/scoped_sc_handle_win.h"
22 #include "remoting/host/branding.h"
23
24 // MIDL-generated declarations and definitions.
25 #include "elevated_controller.h"
26 #include "elevated_controller_i.c"
11 27
12 namespace remoting { 28 namespace remoting {
13 29
14 namespace { 30 namespace {
15 31
32 // The COM elevation moniker for the elevated controller.
33 const char kElevationMoniker[] = "Elevation:Administrator!new:"
34 "{430a9403-8176-4733-afdc-0b325a8fda84}";
35
36 // Name of the Daemon Controller's worker thread.
37 const char kDaemonControllerThreadName[] = "Daemon Controller thread";
38
39 // A base::Thread implementation that initializes COM on the new thread.
40 class ComThread : public base::Thread {
41 public:
42 explicit ComThread(const char* name);
43
44 // Activates an elevated instance of the controller and returns the pointer
45 // to the control interface in |control_out|. This class keeps the ownership
46 // of the pointer so the caller should not call call AddRef() or Release().
47 HRESULT ActivateElevatedController(IDaemonControl** control_out);
48
49 bool Start();
50
51 protected:
52 virtual void Init() OVERRIDE;
53 virtual void CleanUp() OVERRIDE;
54
55 IDaemonControl* control_;
56
57 DISALLOW_COPY_AND_ASSIGN(ComThread);
58 };
59
16 class DaemonControllerWin : public remoting::DaemonController { 60 class DaemonControllerWin : public remoting::DaemonController {
17 public: 61 public:
18 DaemonControllerWin(); 62 DaemonControllerWin();
63 virtual ~DaemonControllerWin();
19 64
20 virtual State GetState() OVERRIDE; 65 virtual State GetState() OVERRIDE;
21 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; 66 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE;
22 virtual void SetConfigAndStart( 67 virtual void SetConfigAndStart(
23 scoped_ptr<base::DictionaryValue> config) OVERRIDE; 68 scoped_ptr<base::DictionaryValue> config) OVERRIDE;
24 virtual void SetPin(const std::string& pin) OVERRIDE; 69 virtual void SetPin(const std::string& pin) OVERRIDE;
25 virtual void Stop() OVERRIDE; 70 virtual void Stop() OVERRIDE;
26 71
27 private: 72 private:
73 // Opens the Chromoting service returning its handle in |service_out|.
74 DWORD OpenService(ScopedScHandle* service_out);
75
76 // The functions that actually do the work. They should be called in
77 // the context of |worker_thread_|;
78 void DoGetConfig(const GetConfigCallback& callback);
79 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config);
80 void DoStop();
81
82 // Converts a Windows service status code to a Daemon state.
83 static State ConvertToDaemonState(DWORD service_state);
84
85 // The worker thread used for servicing long running operations.
86 ComThread worker_thread_;
87
88 // The lock protecting access to all data members below.
89 base::Lock lock_;
90
91 // The error occurred during the last transition.
92 HRESULT last_error_;
93
94 // The daemon state reported to the JavaScript code.
95 State state_;
96
97 // The state that should never be reported to JS unless there is an error.
98 // For instance, when Start() is called, the state of the service doesn't
99 // switch to "starting" immediately. This could lead to JS interpreting
100 // "stopped" as a failure to start the service.
101 // TODO(alexeypa): remove this variable once JS interface supports callbacks.
102 State forbidden_state_;
103
28 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin); 104 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin);
29 }; 105 };
30 106
31 DaemonControllerWin::DaemonControllerWin() { 107 ComThread::ComThread(const char* name) : base::Thread(name), control_(NULL) {
32 } 108 }
33 109
34 DaemonController::State DaemonControllerWin::GetState() { 110 void ComThread::Init() {
35 return DaemonController::STATE_NOT_IMPLEMENTED; 111 CoInitialize(NULL);
112 }
113
114 void ComThread::CleanUp() {
115 if (control_ != NULL)
116 control_->Release();
117 CoUninitialize();
118 }
119
120 HRESULT ComThread::ActivateElevatedController(
121 IDaemonControl** control_out) {
122 // Chache the instance of Elevated Controller to prevent a UAC prompt on every
123 // operation.
124 if (control_ == NULL) {
125 BIND_OPTS3 bind_options;
126 memset(&bind_options, 0, sizeof(bind_options));
127 bind_options.cbStruct = sizeof(bind_options);
128 bind_options.hwnd = NULL;
129 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER;
130
131 HRESULT hr = ::CoGetObject(ASCIIToUTF16(kElevationMoniker).c_str(),
132 &bind_options,
133 IID_IDaemonControl,
134 reinterpret_cast<void**>(&control_));
135 if (FAILED(hr)) {
136 LOG(ERROR) << "Failed to create the elevated controller (error: 0x"
137 << std::hex << hr << std::dec << ").";
138 return hr;
139 }
140 }
141
142 *control_out = control_;
143 return S_OK;
144 }
145
146 bool ComThread::Start() {
147 // N.B. The single threaded COM apartment must be run on a UI message loop.
148 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0);
149 return StartWithOptions(thread_options);
150 }
151
152 DaemonControllerWin::DaemonControllerWin()
153 : last_error_(S_OK),
154 state_(STATE_UNKNOWN),
155 forbidden_state_(STATE_UNKNOWN),
156 worker_thread_(kDaemonControllerThreadName) {
157 if (!worker_thread_.Start()) {
158 // N.B. Start() does not report the error code returned by the system.
159 last_error_ = E_FAIL;
160 }
161 }
162
163 DaemonControllerWin::~DaemonControllerWin() {
164 worker_thread_.Stop();
165 }
166
167 remoting::DaemonController::State DaemonControllerWin::GetState() {
168 // TODO(alexeypa): Make the thread alertable, so we can switch to APC
169 // notifications rather than polling.
170 ScopedScHandle service;
171 DWORD error = OpenService(&service);
172
173 if (error == ERROR_SUCCESS) {
174 SERVICE_STATUS status;
175 if (::QueryServiceStatus(service, &status)) {
176 State new_state = ConvertToDaemonState(status.dwCurrentState);
177
178 base::AutoLock lock(lock_);
179 // TODO(alexeypa): Remove |forbidden_state_| hack once JS interface
180 // supports callbacks.
181 if (forbidden_state_ != new_state || FAILED(last_error_)) {
182 state_ = new_state;
183 }
184
185 // TODO(alexeypa): Remove this hack once JS nicely reports errors.
186 if (FAILED(last_error_)) {
187 state_ = STATE_START_FAILED;
188 }
189
190 return state_;
191 } else {
192 error = GetLastError();
193 LOG_GETLASTERROR(ERROR)
194 << "Failed to query the state of the '" << kWindowsServiceName
195 << "' service";
196 }
197 }
198
199 base::AutoLock lock(lock_);
200 if (error == ERROR_SERVICE_DOES_NOT_EXIST) {
201 state_ = STATE_NOT_IMPLEMENTED;
202 } else {
203 last_error_ = HRESULT_FROM_WIN32(error);
204 state_ = STATE_UNKNOWN;
205 }
206
207 return state_;
36 } 208 }
37 209
38 void DaemonControllerWin::GetConfig(const GetConfigCallback& callback) { 210 void DaemonControllerWin::GetConfig(const GetConfigCallback& callback) {
39 NOTIMPLEMENTED(); 211 worker_thread_.message_loop_proxy()->PostTask(
212 FROM_HERE,
213 base::Bind(&DaemonControllerWin::DoGetConfig,
214 base::Unretained(this), callback));
40 } 215 }
41 216
42 void DaemonControllerWin::SetConfigAndStart( 217 void DaemonControllerWin::SetConfigAndStart(
43 scoped_ptr<base::DictionaryValue> config) { 218 scoped_ptr<base::DictionaryValue> config) {
44 NOTIMPLEMENTED(); 219 base::AutoLock lock(lock_);
220
221 // TODO(alexeypa): Implement on-demand installation.
222 if (state_ == STATE_STOPPED) {
223 last_error_ = S_OK;
224 forbidden_state_ = STATE_STOPPED;
225 state_ = STATE_STARTING;
226 worker_thread_.message_loop_proxy()->PostTask(
227 FROM_HERE,
228 base::Bind(&DaemonControllerWin::DoSetConfigAndStart,
229 base::Unretained(this), base::Passed(&config)));
230 }
45 } 231 }
46 232
47 void DaemonControllerWin::SetPin(const std::string& pin) { 233 void DaemonControllerWin::SetPin(const std::string& pin) {
48 NOTIMPLEMENTED(); 234 NOTIMPLEMENTED();
49 } 235 }
50 236
51 void DaemonControllerWin::Stop() { 237 void DaemonControllerWin::Stop() {
52 NOTIMPLEMENTED(); 238 base::AutoLock lock(lock_);
239
240 if (state_ == STATE_STARTING ||
241 state_ == STATE_STARTED ||
242 state_ == STATE_STOPPING) {
243 last_error_ = S_OK;
244 forbidden_state_ = STATE_STARTED;
245 state_ = STATE_STOPPING;
246 worker_thread_.message_loop_proxy()->PostTask(
247 FROM_HERE,
248 base::Bind(&DaemonControllerWin::DoStop, base::Unretained(this)));
249 }
250 }
251
252 DWORD DaemonControllerWin::OpenService(ScopedScHandle* service_out) {
253 // Open the service and query its current state.
254 ScopedScHandle scmanager(
255 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
256 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
257 if (!scmanager.IsValid()) {
258 DWORD error = GetLastError();
259 LOG_GETLASTERROR(ERROR)
260 << "Failed to connect to the service control manager";
261 return error;
262 }
263
264 ScopedScHandle service(
265 ::OpenServiceW(scmanager, UTF8ToUTF16(kWindowsServiceName).c_str(),
266 SERVICE_QUERY_STATUS));
267 if (!service.IsValid()) {
268 DWORD error = GetLastError();
269 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
270 LOG_GETLASTERROR(ERROR)
271 << "Failed to open to the '" << kWindowsServiceName << "' service";
272 }
273 return error;
274 }
275
276 service_out->Set(service.Take());
277 return ERROR_SUCCESS;
278 }
279
280 void DaemonControllerWin::DoGetConfig(const GetConfigCallback& callback) {
281 IDaemonControl* control = NULL;
282 HRESULT hr = worker_thread_.ActivateElevatedController(&control);
283 if (FAILED(hr)) {
284 callback.Run(scoped_ptr<base::DictionaryValue>());
285 return;
286 }
287
288 // Get the host configuration.
289 BSTR host_config = NULL;
290 hr = control->GetConfig(&host_config);
291 if (FAILED(hr)) {
292 callback.Run(scoped_ptr<base::DictionaryValue>());
293 return;
294 }
295
296 string16 file_content(static_cast<char16*>(host_config),
297 ::SysStringLen(host_config));
298 SysFreeString(host_config);
299
300 // Parse the string into a dictionary.
301 scoped_ptr<base::Value> config(
302 base::JSONReader::Read(UTF16ToUTF8(file_content), true));
303
304 base::DictionaryValue* dictionary;
305 if (config.get() == NULL || !config->GetAsDictionary(&dictionary)) {
306 callback.Run(scoped_ptr<base::DictionaryValue>());
307 return;
308 }
309
310 config.release();
311 callback.Run(scoped_ptr<base::DictionaryValue>(dictionary));
312 }
313
314 void DaemonControllerWin::DoSetConfigAndStart(
315 scoped_ptr<base::DictionaryValue> config) {
316 IDaemonControl* control = NULL;
317 HRESULT hr = worker_thread_.ActivateElevatedController(&control);
318 if (FAILED(hr)) {
319 base::AutoLock lock(lock_);
320 last_error_ = hr;
321 return;
322 }
323
324 // Store the configuration.
325 std::string file_content;
326 base::JSONWriter::Write(config.get(), &file_content);
327
328 BSTR host_config = ::SysAllocString(UTF8ToUTF16(file_content).c_str());
329 if (host_config == NULL) {
330 base::AutoLock lock(lock_);
331 last_error_ = E_OUTOFMEMORY;
332 return;
333 }
334
335 hr = control->SetConfig(host_config);
336 ::SysFreeString(host_config);
337 if (FAILED(hr)) {
338 base::AutoLock lock(lock_);
339 last_error_ = hr;
340 return;
341 }
342
343 // Start daemon.
344 hr = control->StartDaemon();
345 if (FAILED(hr)) {
346 base::AutoLock lock(lock_);
347 last_error_ = hr;
348 }
349 }
350
351 void DaemonControllerWin::DoStop() {
352 IDaemonControl* control = NULL;
353 HRESULT hr = worker_thread_.ActivateElevatedController(&control);
354 if (FAILED(hr)) {
355 base::AutoLock lock(lock_);
356 last_error_ = hr;
357 return;
358 }
359
360 hr = control->StopDaemon();
361 if (FAILED(hr)) {
362 base::AutoLock lock(lock_);
363 last_error_ = hr;
364 }
365 }
366
367 // static
368 remoting::DaemonController::State DaemonControllerWin::ConvertToDaemonState(
369 DWORD service_state) {
370 switch (service_state) {
371 case SERVICE_RUNNING:
372 return STATE_STARTED;
373
374 case SERVICE_CONTINUE_PENDING:
375 case SERVICE_START_PENDING:
376 return STATE_STARTING;
377 break;
378
379 case SERVICE_PAUSE_PENDING:
380 case SERVICE_STOP_PENDING:
381 return STATE_STOPPING;
382 break;
383
384 case SERVICE_PAUSED:
385 case SERVICE_STOPPED:
386 return STATE_STOPPED;
387 break;
388
389 default:
390 NOTREACHED();
391 return STATE_UNKNOWN;
392 }
53 } 393 }
54 394
55 } // namespace 395 } // namespace
56 396
57 DaemonController* remoting::DaemonController::Create() { 397 DaemonController* remoting::DaemonController::Create() {
58 return new DaemonControllerWin(); 398 return new DaemonControllerWin();
59 } 399 }
60 400
61 } // namespace remoting 401 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/plugin/daemon_controller.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698