BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / remoting / host / win / host_service.cc
blobe2477478f74dd8f183162604e0c57f9232f53e16
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.
8 #include "remoting/host/win/host_service.h"
10 #include <sddl.h>
11 #include <windows.h>
12 #include <wtsapi32.h>
14 #include "base/base_paths.h"
15 #include "base/base_switches.h"
16 #include "base/bind.h"
17 #include "base/command_line.h"
18 #include "base/files/file_path.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/run_loop.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/threading/thread.h"
24 #include "base/win/message_window.h"
25 #include "base/win/scoped_com_initializer.h"
26 #include "remoting/base/auto_thread.h"
27 #include "remoting/base/scoped_sc_handle_win.h"
28 #include "remoting/host/branding.h"
29 #include "remoting/host/daemon_process.h"
30 #include "remoting/host/host_exit_codes.h"
31 #include "remoting/host/logging.h"
32 #include "remoting/host/win/com_security.h"
33 #include "remoting/host/win/core_resource.h"
34 #include "remoting/host/win/wts_terminal_observer.h"
36 namespace remoting {
38 namespace {
40 const char kIoThreadName[] = "I/O thread";
42 // Command line switches:
44 // "--console" runs the service interactively for debugging purposes.
45 const char kConsoleSwitchName[] = "console";
47 // Security descriptor allowing local processes running under SYSTEM or
48 // LocalService accounts to call COM methods exposed by the daemon.
49 const wchar_t kComProcessSd[] =
50 SDDL_OWNER L":" SDDL_LOCAL_SYSTEM
51 SDDL_GROUP L":" SDDL_LOCAL_SYSTEM
52 SDDL_DACL L":"
53 SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SYSTEM)
54 SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SERVICE);
56 // Appended to |kComProcessSd| to specify that only callers running at medium or
57 // higher integrity level are allowed to call COM methods exposed by the daemon.
58 const wchar_t kComProcessMandatoryLabel[] =
59 SDDL_SACL L":"
60 SDDL_ACE(SDDL_MANDATORY_LABEL, SDDL_NO_EXECUTE_UP, SDDL_ML_MEDIUM);
62 } // namespace
64 HostService* HostService::GetInstance() {
65 return base::Singleton<HostService>::get();
68 bool HostService::InitWithCommandLine(const base::CommandLine* command_line) {
69 base::CommandLine::StringVector args = command_line->GetArgs();
70 if (!args.empty()) {
71 LOG(ERROR) << "No positional parameters expected.";
72 return false;
75 // Run interactively if needed.
76 if (run_routine_ == &HostService::RunAsService &&
77 command_line->HasSwitch(kConsoleSwitchName)) {
78 run_routine_ = &HostService::RunInConsole;
81 return true;
84 int HostService::Run() {
85 return (this->*run_routine_)();
88 bool HostService::AddWtsTerminalObserver(const std::string& terminal_id,
89 WtsTerminalObserver* observer) {
90 DCHECK(main_task_runner_->BelongsToCurrentThread());
92 RegisteredObserver registered_observer;
93 registered_observer.terminal_id = terminal_id;
94 registered_observer.session_id = kInvalidSessionId;
95 registered_observer.observer = observer;
97 bool session_id_found = false;
98 std::list<RegisteredObserver>::const_iterator i;
99 for (i = observers_.begin(); i != observers_.end(); ++i) {
100 // Get the attached session ID from another observer watching the same WTS
101 // console if any.
102 if (i->terminal_id == terminal_id) {
103 registered_observer.session_id = i->session_id;
104 session_id_found = true;
107 // Check that |observer| hasn't been registered already.
108 if (i->observer == observer)
109 return false;
112 // If |terminal_id| is new, enumerate all sessions to see if there is one
113 // attached to |terminal_id|.
114 if (!session_id_found)
115 registered_observer.session_id = LookupSessionId(terminal_id);
117 observers_.push_back(registered_observer);
119 if (registered_observer.session_id != kInvalidSessionId) {
120 observer->OnSessionAttached(registered_observer.session_id);
123 return true;
126 void HostService::RemoveWtsTerminalObserver(WtsTerminalObserver* observer) {
127 DCHECK(main_task_runner_->BelongsToCurrentThread());
129 std::list<RegisteredObserver>::const_iterator i;
130 for (i = observers_.begin(); i != observers_.end(); ++i) {
131 if (i->observer == observer) {
132 observers_.erase(i);
133 return;
138 HostService::HostService() :
139 run_routine_(&HostService::RunAsService),
140 service_status_handle_(0),
141 stopped_event_(true, false),
142 weak_factory_(this) {
145 HostService::~HostService() {
148 void HostService::OnSessionChange(uint32 event, uint32 session_id) {
149 DCHECK(main_task_runner_->BelongsToCurrentThread());
150 DCHECK_NE(session_id, kInvalidSessionId);
152 // Process only attach/detach notifications.
153 if (event != WTS_CONSOLE_CONNECT && event != WTS_CONSOLE_DISCONNECT &&
154 event != WTS_REMOTE_CONNECT && event != WTS_REMOTE_DISCONNECT) {
155 return;
158 // Assuming that notification can arrive later query the current state of
159 // |session_id|.
160 std::string terminal_id;
161 bool attached = LookupTerminalId(session_id, &terminal_id);
163 std::list<RegisteredObserver>::iterator i = observers_.begin();
164 while (i != observers_.end()) {
165 std::list<RegisteredObserver>::iterator next = i;
166 ++next;
168 // Issue a detach notification if the session was detached from a client or
169 // if it is now attached to a different client.
170 if (i->session_id == session_id &&
171 (!attached || !(i->terminal_id == terminal_id))) {
172 i->session_id = kInvalidSessionId;
173 i->observer->OnSessionDetached();
174 i = next;
175 continue;
178 // The client currently attached to |session_id| was attached to a different
179 // session before. Reconnect it to |session_id|.
180 if (attached && i->terminal_id == terminal_id &&
181 i->session_id != session_id) {
182 WtsTerminalObserver* observer = i->observer;
184 if (i->session_id != kInvalidSessionId) {
185 i->session_id = kInvalidSessionId;
186 i->observer->OnSessionDetached();
189 // Verify that OnSessionDetached() above didn't remove |observer|
190 // from the list.
191 std::list<RegisteredObserver>::iterator j = next;
192 --j;
193 if (j->observer == observer) {
194 j->session_id = session_id;
195 observer->OnSessionAttached(session_id);
199 i = next;
203 void HostService::CreateLauncher(
204 scoped_refptr<AutoThreadTaskRunner> task_runner) {
205 // Launch the I/O thread.
206 scoped_refptr<AutoThreadTaskRunner> io_task_runner =
207 AutoThread::CreateWithType(
208 kIoThreadName, task_runner, base::MessageLoop::TYPE_IO);
209 if (!io_task_runner.get()) {
210 LOG(FATAL) << "Failed to start the I/O thread";
211 return;
214 daemon_process_ = DaemonProcess::Create(
215 task_runner,
216 io_task_runner,
217 base::Bind(&HostService::StopDaemonProcess, weak_ptr_));
220 int HostService::RunAsService() {
221 SERVICE_TABLE_ENTRYW dispatch_table[] = {
222 { const_cast<LPWSTR>(kWindowsServiceName), &HostService::ServiceMain },
223 { nullptr, nullptr }
226 if (!StartServiceCtrlDispatcherW(dispatch_table)) {
227 PLOG(ERROR) << "Failed to connect to the service control manager";
228 return kInitializationFailed;
231 // Wait until the service thread completely exited to avoid concurrent
232 // teardown of objects registered with base::AtExitManager and object
233 // destoyed by the service thread.
234 stopped_event_.Wait();
236 return kSuccessExitCode;
239 void HostService::RunAsServiceImpl() {
240 base::MessageLoopForUI message_loop;
241 base::RunLoop run_loop;
242 main_task_runner_ = message_loop.task_runner();
243 weak_ptr_ = weak_factory_.GetWeakPtr();
245 // Register the service control handler.
246 service_status_handle_ = RegisterServiceCtrlHandlerExW(
247 kWindowsServiceName, &HostService::ServiceControlHandler, this);
248 if (service_status_handle_ == 0) {
249 PLOG(ERROR) << "Failed to register the service control handler";
250 return;
253 // Report running status of the service.
254 SERVICE_STATUS service_status;
255 ZeroMemory(&service_status, sizeof(service_status));
256 service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
257 service_status.dwCurrentState = SERVICE_RUNNING;
258 service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN |
259 SERVICE_ACCEPT_STOP |
260 SERVICE_ACCEPT_SESSIONCHANGE;
261 service_status.dwWin32ExitCode = kSuccessExitCode;
262 if (!SetServiceStatus(service_status_handle_, &service_status)) {
263 PLOG(ERROR)
264 << "Failed to report service status to the service control manager";
265 return;
268 // Initialize COM.
269 base::win::ScopedCOMInitializer com_initializer;
270 if (!com_initializer.succeeded())
271 return;
273 if (!InitializeComSecurity(base::WideToUTF8(kComProcessSd),
274 base::WideToUTF8(kComProcessMandatoryLabel),
275 false)) {
276 return;
279 CreateLauncher(scoped_refptr<AutoThreadTaskRunner>(
280 new AutoThreadTaskRunner(main_task_runner_,
281 run_loop.QuitClosure())));
283 // Run the service.
284 run_loop.Run();
285 weak_factory_.InvalidateWeakPtrs();
287 // Tell SCM that the service is stopped.
288 service_status.dwCurrentState = SERVICE_STOPPED;
289 service_status.dwControlsAccepted = 0;
290 if (!SetServiceStatus(service_status_handle_, &service_status)) {
291 PLOG(ERROR)
292 << "Failed to report service status to the service control manager";
293 return;
297 int HostService::RunInConsole() {
298 base::MessageLoopForUI message_loop;
299 base::RunLoop run_loop;
300 main_task_runner_ = message_loop.task_runner();
301 weak_ptr_ = weak_factory_.GetWeakPtr();
303 int result = kInitializationFailed;
305 // Initialize COM.
306 base::win::ScopedCOMInitializer com_initializer;
307 if (!com_initializer.succeeded())
308 return result;
310 if (!InitializeComSecurity(base::WideToUTF8(kComProcessSd),
311 base::WideToUTF8(kComProcessMandatoryLabel),
312 false)) {
313 return result;
316 // Subscribe to Ctrl-C and other console events.
317 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) {
318 PLOG(ERROR) << "Failed to set console control handler";
319 return result;
322 // Create a window for receiving session change notifications.
323 base::win::MessageWindow window;
324 if (!window.Create(base::Bind(&HostService::HandleMessage,
325 base::Unretained(this)))) {
326 PLOG(ERROR) << "Failed to create the session notification window";
327 goto cleanup;
330 // Subscribe to session change notifications.
331 if (WTSRegisterSessionNotification(window.hwnd(),
332 NOTIFY_FOR_ALL_SESSIONS) != FALSE) {
333 CreateLauncher(scoped_refptr<AutoThreadTaskRunner>(
334 new AutoThreadTaskRunner(main_task_runner_,
335 run_loop.QuitClosure())));
337 // Run the service.
338 run_loop.Run();
340 // Release the control handler.
341 stopped_event_.Signal();
343 WTSUnRegisterSessionNotification(window.hwnd());
344 result = kSuccessExitCode;
347 cleanup:
348 weak_factory_.InvalidateWeakPtrs();
350 // Unsubscribe from console events. Ignore the exit code. There is nothing
351 // we can do about it now and the program is about to exit anyway. Even if
352 // it crashes nothing is going to be broken because of it.
353 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE);
355 return result;
358 void HostService::StopDaemonProcess() {
359 DCHECK(main_task_runner_->BelongsToCurrentThread());
361 daemon_process_.reset();
364 bool HostService::HandleMessage(
365 UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
366 if (message == WM_WTSSESSION_CHANGE) {
367 OnSessionChange(wparam, lparam);
368 *result = 0;
369 return true;
372 return false;
375 // static
376 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) {
377 HostService* self = HostService::GetInstance();
378 switch (event) {
379 case CTRL_C_EVENT:
380 case CTRL_BREAK_EVENT:
381 case CTRL_CLOSE_EVENT:
382 case CTRL_LOGOFF_EVENT:
383 case CTRL_SHUTDOWN_EVENT:
384 self->main_task_runner_->PostTask(
385 FROM_HERE, base::Bind(&HostService::StopDaemonProcess,
386 self->weak_ptr_));
387 return TRUE;
389 default:
390 return FALSE;
394 // static
395 DWORD WINAPI HostService::ServiceControlHandler(DWORD control,
396 DWORD event_type,
397 LPVOID event_data,
398 LPVOID context) {
399 HostService* self = reinterpret_cast<HostService*>(context);
400 switch (control) {
401 case SERVICE_CONTROL_INTERROGATE:
402 return NO_ERROR;
404 case SERVICE_CONTROL_SHUTDOWN:
405 case SERVICE_CONTROL_STOP:
406 self->main_task_runner_->PostTask(
407 FROM_HERE, base::Bind(&HostService::StopDaemonProcess,
408 self->weak_ptr_));
409 return NO_ERROR;
411 case SERVICE_CONTROL_SESSIONCHANGE:
412 self->main_task_runner_->PostTask(FROM_HERE, base::Bind(
413 &HostService::OnSessionChange, self->weak_ptr_, event_type,
414 reinterpret_cast<WTSSESSION_NOTIFICATION*>(event_data)->dwSessionId));
415 return NO_ERROR;
417 default:
418 return ERROR_CALL_NOT_IMPLEMENTED;
422 // static
423 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) {
424 HostService* self = HostService::GetInstance();
426 // Run the service.
427 self->RunAsServiceImpl();
429 // Release the control handler and notify the main thread that it can exit
430 // now.
431 self->stopped_event_.Signal();
434 int DaemonProcessMain() {
435 HostService* service = HostService::GetInstance();
436 if (!service->InitWithCommandLine(base::CommandLine::ForCurrentProcess())) {
437 return kUsageExitCode;
440 return service->Run();
443 } // namespace remoting