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

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: CR feedback 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
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.
Wez 2012/03/30 22:11:01 nit: Consider adding a sentence to explain what th
alexeypa (please no reviews) 2012/03/30 23:47:09 This is typical RTFM. I.e. one sentence is too lit
33 const char kElevationMoniker[] = "Elevation:Administrator!new:"
34 "{430a9403-8176-4733-afdc-0b325a8fda84}";
35
36 // Name of the worker thread.
Wez 2012/03/30 22:11:01 nit: Similarly, what worker thread? What does the
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
37 const char kWorkerThreadName[] = "Daemon Controller thread";
38
39 // A simple wrapper around base::Thread making sure that COM is initialized on
40 // the owner thread.
Wez 2012/03/30 22:11:01 nit: I think you mean "A base::Thread implementati
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
41 class ComThread : public base::Thread {
42 public:
43 explicit ComThread(const char* name);
44
45 // Activates an elevated instance of elevated controller and returns
Wez 2012/03/30 22:11:01 nit: ... elevated instance of the controller ...
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
46 // the pointer to the control interface in |control_out|. This routine keeps
Wez 2012/03/30 22:11:01 nit: The routine can't keep ownership; do you mean
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
47 // the ownership of the pointer so the caller should not call call AddRef() or
48 // Release().
49 HRESULT ActivateElevatedController(IDaemonControl** control_out);
50
51 protected:
52 virtual void Init() OVERRIDE;
53 virtual void CleanUp() OVERRIDE;
54
55 IDaemonControl* control_;
Wez 2012/03/30 22:11:01 nit: Perhaps we should have a ScopedIPtr... ;)
alexeypa (please no reviews) 2012/03/30 23:47:09 It is not going to help in this case.
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 // Activates an elevated instance of elevated controller and returns
Wez 2012/03/30 22:11:01 nit: See above.
alexeypa (please no reviews) 2012/03/30 23:47:09 Stale code. Removed.
74 // the pointer to the control interface in |control_out|.
75 HRESULT ActivateElevatedController(IDaemonControl** control_out);
76
77 // Opens the controlled service handle.
Wez 2012/03/30 22:11:01 nit: What is the controlled service handle? You m
alexeypa (please no reviews) 2012/03/30 23:47:09 Reworded.
78 DWORD OpenService(ScopedScHandle* service_out);
79
80 // Worker functions called in the context of the worker thread.
Wez 2012/03/30 22:11:01 nit: Double use of the term "worker" makes this co
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
81 void DoGetConfig(const GetConfigCallback& callback);
82 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config);
83 void DoStop();
84
85 // Converts SERVICE_XXX contants representing the service state (i.e.
86 // SERVICE_RUNNING, SERVICE_STOPPED) to the daemon state.
Wez 2012/03/30 22:11:01 nit: "Converts a Windows service status code to a
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
87 static State ConvertToDaemonState(DWORD service_state);
88
89 // Service status change notification callback.
Wez 2012/03/30 22:11:01 nit: Suggest rewording e.g. "Status change callbac
alexeypa (please no reviews) 2012/03/30 23:47:09 Stale code.
90 static VOID CALLBACK OnServiceStatusChange(PVOID context);
91
92 // The worker thread used for servicing long running operations.
93 ComThread worker_thread_;
94
95 // The lock protecting access to all data members below.
96 base::Lock lock_;
97
98 // The error occurred during the last transition.
99 HRESULT last_error_;
100
101 // Cached daemon state.
Wez 2012/03/30 22:11:01 nit: This is the state as of the most recent statu
alexeypa (please no reviews) 2012/03/30 23:47:09 No, it is not. Reworded.
102 State state_;
103
104 // The state that should never be reported to JS unless there is an error.
105 // For instance, when Start() is called, the state of the service doesn't
106 // switch to "starting" immediately. This could lead to JS interpreting
107 // "stopped" as a failure to start the service.
Wez 2012/03/30 22:11:01 This comment is very confusing, as is the name |fo
alexeypa (please no reviews) 2012/03/30 23:47:09 forbidden_state_ is a hack and it is confusing bec
108 // TODO(alexeypa): remove this variable once JS interafce supports callbacks.
Wez 2012/03/30 22:11:01 typo: interafce
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
109 State forbidden_state_;
110
28 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin); 111 DISALLOW_COPY_AND_ASSIGN(DaemonControllerWin);
29 }; 112 };
30 113
31 DaemonControllerWin::DaemonControllerWin() { 114 ComThread::ComThread(const char* name) : base::Thread(name), control_(NULL) {
32 } 115 }
33 116
34 DaemonController::State DaemonControllerWin::GetState() { 117 void ComThread::Init() {
35 return DaemonController::STATE_NOT_IMPLEMENTED; 118 CoInitialize(NULL);
119 }
120
121 void ComThread::CleanUp() {
122 if (control_ != NULL) {
123 control_->Release();
124 }
125
Wez 2012/03/30 22:11:01 nit: Personally I'd lose the {} on the if, and thi
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
126 CoUninitialize();
127 }
128
129 HRESULT ComThread::ActivateElevatedController(
130 IDaemonControl** control_out) {
131
Wez 2012/03/30 22:11:01 nit: No need for blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 I replaced it with a comment.
132 if (control_ == NULL) {
133 BIND_OPTS3 bind_options;
134 memset(&bind_options, 0, sizeof(bind_options));
135 bind_options.cbStruct = sizeof(bind_options);
136 bind_options.hwnd = NULL;
137 bind_options.dwClassContext = CLSCTX_LOCAL_SERVER;
138
139 IDaemonControl* control = NULL;
Wez 2012/03/30 22:11:01 This doesn't seem to be used?
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
140 HRESULT hr = ::CoGetObject(ASCIIToUTF16(kElevationMoniker).c_str(),
141 &bind_options,
142 IID_IDaemonControl,
143 reinterpret_cast<void**>(&control_));
144 if (FAILED(hr)) {
145 LOG(ERROR) << "Failed to create the elevated controller (error: 0x"
146 << std::hex << hr << std::dec << ").";
147 return hr;
148 }
149 }
150
151 *control_out = control_;
152 return S_OK;
153 }
154
155 DaemonControllerWin::DaemonControllerWin()
156 : last_error_(S_OK),
157 state_(STATE_UNKNOWN),
158 forbidden_state_(STATE_UNKNOWN),
159 worker_thread_(kWorkerThreadName) {
160 // N.B. The UI message loop is required for the single threaded COM apartment.
Wez 2012/03/30 22:11:01 typo: The UI ... is required for ... -> COM must
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
161 base::Thread::Options thread_options(MessageLoop::TYPE_UI, 0);
162 if (!worker_thread_.StartWithOptions(thread_options)) {
Wez 2012/03/30 22:11:01 Since ComThread doesn't make sense unless started
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
163 // N.B. StartWithOptions() does not report the error code returned by
164 // the system.
165 last_error_ = E_FAIL;
166 }
167 }
168
169 DaemonControllerWin::~DaemonControllerWin() {
170 worker_thread_.Stop();
171 }
172
173 remoting::DaemonController::State DaemonControllerWin::GetState() {
174 // TODO(alexeypa): convert polling to async callbacks once there is a thread
175 // that can receive APC callbacks.
Wez 2012/03/30 22:11:01 nit: Suggest "Make the thread alertable, so we can
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
176 ScopedScHandle service;
177 DWORD error = OpenService(&service);
178
179 if (error == ERROR_SUCCESS) {
180 SERVICE_STATUS status;
181 if (::QueryServiceStatus(service, &status)) {
182 State new_state = ConvertToDaemonState(status.dwCurrentState);
183
184 base::AutoLock lock(lock_);
185 // TODO(alexeypa): remove |forbidden_state_| hack once JS interface
186 // supports callbacks.
Wez 2012/03/30 22:11:01 nit: remove ... -> Remove ...
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
187 if (forbidden_state_ != new_state || FAILED(last_error_)) {
188 state_ = new_state;
189 }
190
191 // TODO(alexeypa): remove this hack once JS nicely reports errors.
Wez 2012/03/30 22:11:01 nit: It's not immediately obvious what the hack is
alexeypa (please no reviews) 2012/03/30 23:47:09 I'm sorry. It is there. Look more closely. :-)
192 if (FAILED(last_error_)) {
193 state_ = STATE_START_FAILED;
194 }
195
196 return state_;
197 } else {
198 error = GetLastError();
199 LOG_GETLASTERROR(ERROR)
200 << "Failed to query the state of the '" << kWindowsServiceName
201 << "' service";
202 }
203 }
204
205 base::AutoLock lock(lock_);
206 if (error == ERROR_SERVICE_DOES_NOT_EXIST) {
207 state_ = STATE_NOT_IMPLEMENTED;
208 } else {
209 last_error_ = HRESULT_FROM_WIN32(error);
210 state_ = STATE_UNKNOWN;
211 }
212
213 return state_;
36 } 214 }
37 215
38 void DaemonControllerWin::GetConfig(const GetConfigCallback& callback) { 216 void DaemonControllerWin::GetConfig(const GetConfigCallback& callback) {
39 NOTIMPLEMENTED(); 217 worker_thread_.message_loop_proxy()->PostTask(
218 FROM_HERE,
219 base::Bind(&DaemonControllerWin::DoGetConfig,
220 base::Unretained(this), callback));
40 } 221 }
41 222
42 void DaemonControllerWin::SetConfigAndStart( 223 void DaemonControllerWin::SetConfigAndStart(
43 scoped_ptr<base::DictionaryValue> config) { 224 scoped_ptr<base::DictionaryValue> config) {
44 NOTIMPLEMENTED(); 225 base::AutoLock lock(lock_);
226
227 // TODO(alexeypa): implement on-demand installation.
Wez 2012/03/30 22:11:01 typo: implement ... -> Implement ...
Wez 2012/03/30 22:11:01 nit: We generally prefer to create TODOs with an a
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
alexeypa (please no reviews) 2012/03/30 23:47:09 While being a good practice (I presume) I haven't
228 if (state_ == STATE_STOPPED) {
229 last_error_ = S_OK;
230 forbidden_state_ = STATE_STOPPED;
231 state_ = STATE_STARTING;
232 worker_thread_.message_loop_proxy()->PostTask(
233 FROM_HERE,
234 base::Bind(&DaemonControllerWin::DoSetConfigAndStart,
235 base::Unretained(this), base::Passed(&config)));
236 }
45 } 237 }
46 238
47 void DaemonControllerWin::SetPin(const std::string& pin) { 239 void DaemonControllerWin::SetPin(const std::string& pin) {
48 NOTIMPLEMENTED(); 240 NOTIMPLEMENTED();
49 } 241 }
50 242
51 void DaemonControllerWin::Stop() { 243 void DaemonControllerWin::Stop() {
52 NOTIMPLEMENTED(); 244 base::AutoLock lock(lock_);
245
246 if (state_ == STATE_STARTING ||
247 state_ == STATE_STARTED ||
248 state_ == STATE_STOPPING) {
Wez 2012/03/30 22:11:01 These are cached states; isn't there a risk that s
alexeypa (please no reviews) 2012/03/30 23:47:09 There is no danger. The state is inherently out-on
249
Wez 2012/03/30 22:11:01 nit: Errant blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
250 last_error_ = S_OK;
251 forbidden_state_ = STATE_STARTED;
252 state_ = STATE_STOPPING;
253 worker_thread_.message_loop_proxy()->PostTask(
254 FROM_HERE,
255 base::Bind(&DaemonControllerWin::DoStop, base::Unretained(this)));
256 }
257 }
258
259 DWORD DaemonControllerWin::OpenService(ScopedScHandle* service_out) {
Wez 2012/03/30 22:11:01 nit: This function looks oddly familiar from Eleva
alexeypa (please no reviews) 2012/03/30 23:47:09 There is little benefit in sharing the same implem
260 // Open the service and query its current state.
261 ScopedScHandle scmanager(
262 ::OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASE,
263 SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE));
264 if (!scmanager.IsValid()) {
265 DWORD error = GetLastError();
266 LOG_GETLASTERROR(ERROR)
267 << "Failed to connect to the service control manager";
268 return error;
269 }
270
271 ScopedScHandle service(
272 ::OpenServiceW(scmanager, ASCIIToUTF16(kWindowsServiceName).c_str(),
273 SERVICE_QUERY_STATUS));
274 if (!service.IsValid()) {
275 DWORD error = GetLastError();
276 if (error != ERROR_SERVICE_DOES_NOT_EXIST) {
277 LOG_GETLASTERROR(ERROR)
278 << "Failed to open to the '" << kWindowsServiceName << "' service";
279 }
280 return error;
281 }
282
283 service_out->Set(service.Take());
284 return ERROR_SUCCESS;
285 }
286
287 void DaemonControllerWin::DoGetConfig(const GetConfigCallback& callback) {
288 IDaemonControl* control = NULL;
289 HRESULT hr = worker_thread_.ActivateElevatedController(&control);
290 if (FAILED(hr)) {
291 callback.Run(scoped_ptr<base::DictionaryValue>());
292 return;
293 }
294
295 // Get the host configuration.
296 BSTR host_config = NULL;
Wez 2012/03/30 22:11:01 Is there no managed container for BSTR?
alexeypa (please no reviews) 2012/03/30 23:47:09 There is. It drags ATL. We don't want it here.
297 hr = control->GetConfig(&host_config);
298 if (FAILED(hr)) {
299 callback.Run(scoped_ptr<base::DictionaryValue>());
300 return;
301 }
302
303 string16 file_content(static_cast<char16*>(host_config),
304 ::SysStringLen(host_config));
305 SysFreeString(host_config);
306
307 // Parse the string into a dictionary.
308 scoped_ptr<base::Value> config(
309 base::JSONReader::Read(UTF16ToUTF8(file_content), true));
310
311 base::DictionaryValue* dictionary;
312 if (config.get() == NULL || !config->GetAsDictionary(&dictionary)) {
313 callback.Run(scoped_ptr<base::DictionaryValue>());
314 return;
315 }
316
317 config.release();
318 callback.Run(scoped_ptr<base::DictionaryValue>(dictionary));
Wez 2012/03/30 22:11:01 Yup. Still gross ... :P
319 }
320
321 void DaemonControllerWin::DoSetConfigAndStart(
322 scoped_ptr<base::DictionaryValue> config) {
323
Wez 2012/03/30 22:11:01 nit: Errant blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
324 IDaemonControl* control = NULL;
325 HRESULT hr = worker_thread_.ActivateElevatedController(&control);
326 if (FAILED(hr)) {
327 base::AutoLock lock(lock_);
328 last_error_ = hr;
Wez 2012/03/30 22:11:01 Why do we lock when setting |last_error_|? Surely
alexeypa (please no reviews) 2012/03/30 23:47:09 Race is fine. Corruption (state_ is not synched wi
329 return;
330 }
331
332 // Set the configuration file
Wez 2012/03/30 22:11:01 Suggest "Store the configuration."
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
333 std::string file_content;
334 base::JSONWriter::Write(config.get(), &file_content);
335
336 BSTR host_config = ::SysAllocString(UTF8ToUTF16(file_content).c_str());
337 if (host_config == NULL) {
338 base::AutoLock lock(lock_);
339 last_error_ = E_OUTOFMEMORY;
340 return;
341 }
342
343 hr = control->SetConfig(host_config);
344 ::SysFreeString(host_config);
345
Wez 2012/03/30 22:11:01 nit: No need for blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
346 if (FAILED(hr)) {
347 base::AutoLock lock(lock_);
348 last_error_ = hr;
349 return;
350 }
351
352 // Start daemon.
353 hr = control->StartDaemon();
354
Wez 2012/03/30 22:11:01 nit: No need for blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
355 if (FAILED(hr)) {
356 base::AutoLock lock(lock_);
357 last_error_ = hr;
358 }
359 }
360
361 void DaemonControllerWin::DoStop() {
362 IDaemonControl* control = NULL;
363 HRESULT hr = worker_thread_.ActivateElevatedController(&control);
364 if (FAILED(hr)) {
365 base::AutoLock lock(lock_);
366 last_error_ = hr;
367 return;
368 }
369
370 hr = control->StopDaemon();
371
Wez 2012/03/30 22:11:01 nit: Blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
372 if (FAILED(hr)) {
373 base::AutoLock lock(lock_);
374 last_error_ = hr;
375 }
376 }
377
378 // static
379 remoting::DaemonController::State DaemonControllerWin::ConvertToDaemonState(
380 DWORD service_state) {
381
Wez 2012/03/30 22:11:01 nit: Blank line.
alexeypa (please no reviews) 2012/03/30 23:47:09 Done.
382 switch (service_state) {
383 case SERVICE_RUNNING:
384 return STATE_STARTED;
385
Wez 2012/03/30 22:11:01 nit: No need for these blank lines.
alexeypa (please no reviews) 2012/03/30 23:47:09 My eyes hurt. It is unreadable.
386 case SERVICE_CONTINUE_PENDING:
387 case SERVICE_START_PENDING:
388 return STATE_STARTING;
389 break;
Wez 2012/03/30 22:11:01 nit: Nor for this break, or the ones below.
alexeypa (please no reviews) 2012/03/30 23:47:09 See above.
390
391 case SERVICE_PAUSE_PENDING:
392 case SERVICE_STOP_PENDING:
393 return STATE_STOPPING;
394 break;
395
396 case SERVICE_PAUSED:
397 case SERVICE_STOPPED:
398 return STATE_STOPPED;
399 break;
400
401 default:
402 NOTREACHED();
403 return STATE_UNKNOWN;
404 }
53 } 405 }
54 406
55 } // namespace 407 } // namespace
56 408
57 DaemonController* remoting::DaemonController::Create() { 409 DaemonController* remoting::DaemonController::Create() {
58 return new DaemonControllerWin(); 410 return new DaemonControllerWin();
59 } 411 }
60 412
61 } // namespace remoting 413 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698