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

Side by Side Diff: remoting/host/host_service_win.cc

Issue 10832068: Moving Windows-only files: remoting/host -> remoting/host/win. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 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/host_service_win.h ('k') | remoting/host/launch_process_in_session_win.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4 //
5 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions.
7
8 #include "remoting/host/host_service_win.h"
9
10 #include <windows.h>
11 #include <wtsapi32.h>
12 #include <stdio.h>
13
14 #include "base/at_exit.h"
15 #include "base/base_paths.h"
16 #include "base/bind.h"
17 #include "base/command_line.h"
18 #include "base/file_util.h"
19 #include "base/logging.h"
20 #include "base/message_loop.h"
21 #include "base/path_service.h"
22 #include "base/stringprintf.h"
23 #include "base/threading/thread.h"
24 #include "base/utf_string_conversions.h"
25 #include "base/win/wrapped_window_proc.h"
26 #include "remoting/base/breakpad.h"
27 #include "remoting/base/scoped_sc_handle_win.h"
28 #include "remoting/host/branding.h"
29 #include "remoting/host/host_service_resource.h"
30 #include "remoting/host/usage_stats_consent.h"
31 #include "remoting/host/wts_console_observer_win.h"
32 #include "remoting/host/wts_session_process_launcher_win.h"
33
34 using base::StringPrintf;
35
36 namespace {
37
38 const char kIoThreadName[] = "I/O thread";
39
40 // A window class for the session change notifications window.
41 const wchar_t kSessionNotificationWindowClass[] =
42 L"Chromoting_SessionNotificationWindow";
43
44 // Command line actions and switches:
45 // "run" sumply runs the service as usual.
46 const wchar_t kRunActionName[] = L"run";
47
48 // "--console" runs the service interactively for debugging purposes.
49 const char kConsoleSwitchName[] = "console";
50
51 // "--host-binary" specifies the host binary to run in console session.
52 const char kHostBinarySwitchName[] = "host-binary";
53
54 // "--help" or "--?" prints the usage message.
55 const char kHelpSwitchName[] = "help";
56 const char kQuestionSwitchName[] = "?";
57
58 const char kUsageMessage[] =
59 "\n"
60 "Usage: %s [action] [options]\n"
61 "\n"
62 "Actions:\n"
63 " run - Run the service (default if no action was specified).\n"
64 "\n"
65 "Options:\n"
66 " --console - Run the service interactively for debugging purposes.\n"
67 " --host-binary - Specifies the host binary to run.\n"
68 " --help, --? - Print this message.\n";
69
70 // Exit codes:
71 const int kSuccessExitCode = 0;
72 const int kUsageExitCode = 1;
73 const int kErrorExitCode = 2;
74
75 void usage(const char* program_name) {
76 fprintf(stderr, kUsageMessage, program_name);
77 }
78
79 } // namespace
80
81 namespace remoting {
82
83 HostService::HostService() :
84 console_session_id_(kInvalidSessionId),
85 message_loop_(NULL),
86 run_routine_(&HostService::RunAsService),
87 service_name_(kWindowsServiceName),
88 service_status_handle_(0),
89 shutting_down_(false),
90 stopped_event_(true, false) {
91 }
92
93 HostService::~HostService() {
94 }
95
96 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) {
97 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread());
98
99 console_observers_.AddObserver(observer);
100 }
101
102 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) {
103 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread());
104
105 console_observers_.RemoveObserver(observer);
106
107 // Stop the service if there are no more observers.
108 if (!console_observers_.might_have_observers()) {
109 message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure());
110 }
111 }
112
113 void HostService::OnSessionChange() {
114 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads
115 // a single value from shared memory. Therefore it is better to check if
116 // the console session is still the same every time a session change
117 // notification event is posted. This also takes care of coalescing multiple
118 // events into one since we look at the latest state.
119 uint32 console_session_id = kInvalidSessionId;
120 if (!shutting_down_) {
121 console_session_id = WTSGetActiveConsoleSessionId();
122 }
123 if (console_session_id_ != console_session_id) {
124 if (console_session_id_ != kInvalidSessionId) {
125 FOR_EACH_OBSERVER(WtsConsoleObserver,
126 console_observers_,
127 OnSessionDetached());
128 }
129
130 console_session_id_ = console_session_id;
131
132 if (console_session_id_ != kInvalidSessionId) {
133 FOR_EACH_OBSERVER(WtsConsoleObserver,
134 console_observers_,
135 OnSessionAttached(console_session_id_));
136 }
137 }
138 }
139
140 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) {
141 HostService* self = HostService::GetInstance();
142 switch (event) {
143 case CTRL_C_EVENT:
144 case CTRL_BREAK_EVENT:
145 case CTRL_CLOSE_EVENT:
146 case CTRL_LOGOFF_EVENT:
147 case CTRL_SHUTDOWN_EVENT:
148 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure());
149 self->stopped_event_.Wait();
150 return TRUE;
151
152 default:
153 return FALSE;
154 }
155 }
156
157 HostService* HostService::GetInstance() {
158 return Singleton<HostService>::get();
159 }
160
161 bool HostService::InitWithCommandLine(const CommandLine* command_line) {
162 CommandLine::StringVector args = command_line->GetArgs();
163
164 // Choose the action to perform.
165 if (!args.empty()) {
166 if (args.size() > 1) {
167 LOG(ERROR) << "Invalid command line: more than one action requested.";
168 return false;
169 }
170 if (args[0] != kRunActionName) {
171 LOG(ERROR) << "Invalid command line: invalid action specified: "
172 << args[0];
173 return false;
174 }
175 }
176
177 if (command_line->HasSwitch(kHostBinarySwitchName)) {
178 host_binary_ = command_line->GetSwitchValuePath(kHostBinarySwitchName);
179 } else {
180 LOG(ERROR) << "Invalid command line: --" << kHostBinarySwitchName
181 << " is required.";
182 return false;
183 }
184
185 // Run interactively if needed.
186 if (run_routine_ == &HostService::RunAsService &&
187 command_line->HasSwitch(kConsoleSwitchName)) {
188 run_routine_ = &HostService::RunInConsole;
189 }
190
191 return true;
192 }
193
194 int HostService::Run() {
195 return (this->*run_routine_)();
196 }
197
198 void HostService::RunMessageLoop() {
199 // Launch the I/O thread.
200 base::Thread io_thread(kIoThreadName);
201 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0);
202 if (!io_thread.StartWithOptions(io_thread_options)) {
203 LOG(FATAL) << "Failed to start the I/O thread";
204 shutting_down_ = true;
205 stopped_event_.Signal();
206 return;
207 }
208
209 WtsSessionProcessLauncher launcher(this, host_binary_,
210 message_loop_->message_loop_proxy(),
211 io_thread.message_loop_proxy());
212
213 // Run the service.
214 message_loop_->Run();
215
216 // Clean up the observers by emulating detaching from the console.
217 shutting_down_ = true;
218 OnSessionChange();
219
220 // Release the control handler.
221 stopped_event_.Signal();
222 }
223
224 int HostService::RunAsService() {
225 SERVICE_TABLE_ENTRYW dispatch_table[] = {
226 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain },
227 { NULL, NULL }
228 };
229
230 if (!StartServiceCtrlDispatcherW(dispatch_table)) {
231 LOG_GETLASTERROR(ERROR)
232 << "Failed to connect to the service control manager";
233 return kErrorExitCode;
234 }
235
236 return kSuccessExitCode;
237 }
238
239 int HostService::RunInConsole() {
240 MessageLoop message_loop(MessageLoop::TYPE_UI);
241
242 // Allow other threads to post to our message loop.
243 message_loop_ = &message_loop;
244
245 int result = kErrorExitCode;
246
247 // Subscribe to Ctrl-C and other console events.
248 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) {
249 LOG_GETLASTERROR(ERROR)
250 << "Failed to set console control handler";
251 return result;
252 }
253
254 // Create a window for receiving session change notifications.
255 HWND window = NULL;
256 WNDCLASSEX window_class;
257 base::win::InitializeWindowClass(
258 kSessionNotificationWindowClass,
259 &base::win::WrappedWindowProc<SessionChangeNotificationProc>,
260 0, 0, 0, NULL, NULL, NULL, NULL, NULL,
261 &window_class);
262 HINSTANCE instance = window_class.hInstance;
263 ATOM atom = RegisterClassExW(&window_class);
264 if (atom == 0) {
265 LOG_GETLASTERROR(ERROR)
266 << "Failed to register the window class '"
267 << kSessionNotificationWindowClass << "'";
268 goto cleanup;
269 }
270
271 window = CreateWindowW(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
272 instance, 0);
273 if (window == NULL) {
274 LOG_GETLASTERROR(ERROR)
275 << "Failed to creat the session notificationwindow";
276 goto cleanup;
277 }
278
279 // Post a dummy session change notification to peek up the current console
280 // session.
281 message_loop.PostTask(FROM_HERE, base::Bind(
282 &HostService::OnSessionChange, base::Unretained(this)));
283
284 // Subscribe to session change notifications.
285 if (WTSRegisterSessionNotification(window,
286 NOTIFY_FOR_ALL_SESSIONS) != FALSE) {
287 // Run the service.
288 RunMessageLoop();
289
290 WTSUnRegisterSessionNotification(window);
291 result = kSuccessExitCode;
292 }
293
294 cleanup:
295 if (window != NULL) {
296 DestroyWindow(window);
297 }
298
299 if (atom != 0) {
300 UnregisterClass(MAKEINTATOM(atom), instance);
301 }
302
303 // Unsubscribe from console events. Ignore the exit code. There is nothing
304 // we can do about it now and the program is about to exit anyway. Even if
305 // it crashes nothing is going to be broken because of it.
306 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE);
307
308 message_loop_ = NULL;
309 return result;
310 }
311
312 DWORD WINAPI HostService::ServiceControlHandler(DWORD control,
313 DWORD event_type,
314 LPVOID event_data,
315 LPVOID context) {
316 HostService* self = reinterpret_cast<HostService*>(context);
317 switch (control) {
318 case SERVICE_CONTROL_INTERROGATE:
319 return NO_ERROR;
320
321 case SERVICE_CONTROL_SHUTDOWN:
322 case SERVICE_CONTROL_STOP:
323 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure());
324 self->stopped_event_.Wait();
325 return NO_ERROR;
326
327 case SERVICE_CONTROL_SESSIONCHANGE:
328 self->message_loop_->PostTask(FROM_HERE, base::Bind(
329 &HostService::OnSessionChange, base::Unretained(self)));
330 return NO_ERROR;
331
332 default:
333 return ERROR_CALL_NOT_IMPLEMENTED;
334 }
335 }
336
337 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) {
338 MessageLoop message_loop;
339
340 // Allow other threads to post to our message loop.
341 HostService* self = HostService::GetInstance();
342 self->message_loop_ = &message_loop;
343
344 // Register the service control handler.
345 self->service_status_handle_ =
346 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(),
347 &HostService::ServiceControlHandler,
348 self);
349 if (self->service_status_handle_ == 0) {
350 LOG_GETLASTERROR(ERROR)
351 << "Failed to register the service control handler";
352 return;
353 }
354
355 // Report running status of the service.
356 SERVICE_STATUS service_status;
357 ZeroMemory(&service_status, sizeof(service_status));
358 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
359 service_status.dwCurrentState = SERVICE_RUNNING;
360 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN |
361 SERVICE_ACCEPT_STOP |
362 SERVICE_ACCEPT_SESSIONCHANGE;
363 service_status.dwWin32ExitCode = kSuccessExitCode;
364
365 if (!SetServiceStatus(self->service_status_handle_, &service_status)) {
366 LOG_GETLASTERROR(ERROR)
367 << "Failed to report service status to the service control manager";
368 return;
369 }
370
371 // Post a dummy session change notification to peek up the current console
372 // session.
373 message_loop.PostTask(FROM_HERE, base::Bind(
374 &HostService::OnSessionChange, base::Unretained(self)));
375
376 // Run the service.
377 self->RunMessageLoop();
378
379 // Tell SCM that the service is stopped.
380 service_status.dwCurrentState = SERVICE_STOPPED;
381 service_status.dwControlsAccepted = 0;
382
383 if (!SetServiceStatus(self->service_status_handle_, &service_status)) {
384 LOG_GETLASTERROR(ERROR)
385 << "Failed to report service status to the service control manager";
386 return;
387 }
388
389 self->message_loop_ = NULL;
390 }
391
392 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd,
393 UINT message,
394 WPARAM wparam,
395 LPARAM lparam) {
396 switch (message) {
397 case WM_WTSSESSION_CHANGE: {
398 HostService* self = HostService::GetInstance();
399 self->OnSessionChange();
400 return 0;
401 }
402
403 default:
404 return DefWindowProc(hwnd, message, wparam, lparam);
405 }
406 }
407
408 } // namespace remoting
409
410 int main(int argc, char** argv) {
411 #ifdef OFFICIAL_BUILD
412 if (remoting::IsUsageStatsAllowed()) {
413 remoting::InitializeCrashReporting();
414 }
415 #endif // OFFICIAL_BUILD
416
417 CommandLine::Init(argc, argv);
418
419 // This object instance is required by Chrome code (for example,
420 // FilePath, LazyInstance, MessageLoop).
421 base::AtExitManager exit_manager;
422
423 // Write logs to the application profile directory.
424 FilePath debug_log = remoting::GetConfigDir().
425 Append(FILE_PATH_LITERAL("debug.log"));
426 InitLogging(debug_log.value().c_str(),
427 logging::LOG_ONLY_TO_FILE,
428 logging::DONT_LOCK_LOG_FILE,
429 logging::APPEND_TO_OLD_LOG_FILE,
430 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
431
432 const CommandLine* command_line = CommandLine::ForCurrentProcess();
433
434 if (command_line->HasSwitch(kHelpSwitchName) ||
435 command_line->HasSwitch(kQuestionSwitchName)) {
436 usage(argv[0]);
437 return kSuccessExitCode;
438 }
439
440 remoting::HostService* service = remoting::HostService::GetInstance();
441 if (!service->InitWithCommandLine(command_line)) {
442 usage(argv[0]);
443 return kUsageExitCode;
444 }
445
446 return service->Run();
447 }
OLDNEW
« no previous file with comments | « remoting/host/host_service_win.h ('k') | remoting/host/launch_process_in_session_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698