| OLD | NEW |
| 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 // This file implements the Windows service controlling Me2Me host processes | 5 // This file implements the Windows service controlling Me2Me host processes |
| 6 // running within user sessions. | 6 // running within user sessions. |
| 7 | 7 |
| 8 #include "remoting/host/win/host_service.h" | 8 #include "remoting/host/win/host_service.h" |
| 9 | 9 |
| 10 #include <windows.h> | 10 #include <windows.h> |
| 11 #include <wtsapi32.h> | 11 #include <wtsapi32.h> |
| 12 #include <stdio.h> | 12 #include <stdio.h> |
| 13 | 13 |
| 14 #include "base/at_exit.h" | 14 #include "base/at_exit.h" |
| 15 #include "base/base_paths.h" | 15 #include "base/base_paths.h" |
| 16 #include "base/bind.h" | 16 #include "base/bind.h" |
| 17 #include "base/command_line.h" | 17 #include "base/command_line.h" |
| 18 #include "base/file_util.h" | 18 #include "base/file_util.h" |
| 19 #include "base/logging.h" | 19 #include "base/logging.h" |
| 20 #include "base/message_loop.h" | 20 #include "base/message_loop.h" |
| 21 #include "base/path_service.h" | 21 #include "base/path_service.h" |
| 22 #include "base/single_thread_task_runner.h" |
| 22 #include "base/stringprintf.h" | 23 #include "base/stringprintf.h" |
| 23 #include "base/threading/thread.h" | 24 #include "base/threading/thread.h" |
| 24 #include "base/utf_string_conversions.h" | 25 #include "base/utf_string_conversions.h" |
| 25 #include "base/win/wrapped_window_proc.h" | 26 #include "base/win/wrapped_window_proc.h" |
| 26 #include "remoting/base/breakpad.h" | 27 #include "remoting/base/breakpad.h" |
| 27 #include "remoting/base/scoped_sc_handle_win.h" | 28 #include "remoting/base/scoped_sc_handle_win.h" |
| 29 #include "remoting/base/stoppable.h" |
| 28 #include "remoting/host/branding.h" | 30 #include "remoting/host/branding.h" |
| 29 #include "remoting/host/usage_stats_consent.h" | 31 #include "remoting/host/usage_stats_consent.h" |
| 30 #include "remoting/host/win/host_service_resource.h" | 32 #include "remoting/host/win/host_service_resource.h" |
| 31 #include "remoting/host/win/wts_console_observer.h" | 33 #include "remoting/host/win/wts_console_observer.h" |
| 32 #include "remoting/host/win/wts_session_process_launcher.h" | 34 #include "remoting/host/win/wts_session_process_launcher.h" |
| 33 | 35 |
| 34 using base::StringPrintf; | 36 using base::StringPrintf; |
| 35 | 37 |
| 36 namespace { | 38 namespace { |
| 37 | 39 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 75 void usage(const char* program_name) { | 77 void usage(const char* program_name) { |
| 76 fprintf(stderr, kUsageMessage, program_name); | 78 fprintf(stderr, kUsageMessage, program_name); |
| 77 } | 79 } |
| 78 | 80 |
| 79 } // namespace | 81 } // namespace |
| 80 | 82 |
| 81 namespace remoting { | 83 namespace remoting { |
| 82 | 84 |
| 83 HostService::HostService() : | 85 HostService::HostService() : |
| 84 console_session_id_(kInvalidSessionId), | 86 console_session_id_(kInvalidSessionId), |
| 85 message_loop_(NULL), | |
| 86 run_routine_(&HostService::RunAsService), | 87 run_routine_(&HostService::RunAsService), |
| 87 service_name_(kWindowsServiceName), | 88 service_name_(kWindowsServiceName), |
| 88 service_status_handle_(0), | 89 service_status_handle_(0), |
| 89 shutting_down_(false), | |
| 90 stopped_event_(true, false) { | 90 stopped_event_(true, false) { |
| 91 } | 91 } |
| 92 | 92 |
| 93 HostService::~HostService() { | 93 HostService::~HostService() { |
| 94 } | 94 } |
| 95 | 95 |
| 96 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { | 96 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { |
| 97 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); | 97 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 98 | 98 |
| 99 console_observers_.AddObserver(observer); | 99 console_observers_.AddObserver(observer); |
| 100 } | 100 } |
| 101 | 101 |
| 102 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { | 102 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { |
| 103 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); | 103 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 104 | 104 |
| 105 console_observers_.RemoveObserver(observer); | 105 console_observers_.RemoveObserver(observer); |
| 106 } |
| 106 | 107 |
| 107 // Stop the service if there are no more observers. | 108 void HostService::OnLauncherShutdown() { |
| 108 if (!console_observers_.might_have_observers()) { | 109 launcher_.reset(NULL); |
| 109 message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 110 main_task_runner_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| 110 } | |
| 111 } | 111 } |
| 112 | 112 |
| 113 void HostService::OnSessionChange() { | 113 void HostService::OnSessionChange() { |
| 114 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads | 114 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads |
| 115 // a single value from shared memory. Therefore it is better to check if | 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 | 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 | 117 // notification event is posted. This also takes care of coalescing multiple |
| 118 // events into one since we look at the latest state. | 118 // events into one since we look at the latest state. |
| 119 uint32 console_session_id = kInvalidSessionId; | 119 uint32 console_session_id = WTSGetActiveConsoleSessionId(); |
| 120 if (!shutting_down_) { | |
| 121 console_session_id = WTSGetActiveConsoleSessionId(); | |
| 122 } | |
| 123 if (console_session_id_ != console_session_id) { | 120 if (console_session_id_ != console_session_id) { |
| 124 if (console_session_id_ != kInvalidSessionId) { | 121 if (console_session_id_ != kInvalidSessionId) { |
| 125 FOR_EACH_OBSERVER(WtsConsoleObserver, | 122 FOR_EACH_OBSERVER(WtsConsoleObserver, |
| 126 console_observers_, | 123 console_observers_, |
| 127 OnSessionDetached()); | 124 OnSessionDetached()); |
| 128 } | 125 } |
| 129 | 126 |
| 130 console_session_id_ = console_session_id; | 127 console_session_id_ = console_session_id; |
| 131 | 128 |
| 132 if (console_session_id_ != kInvalidSessionId) { | 129 if (console_session_id_ != kInvalidSessionId) { |
| 133 FOR_EACH_OBSERVER(WtsConsoleObserver, | 130 FOR_EACH_OBSERVER(WtsConsoleObserver, |
| 134 console_observers_, | 131 console_observers_, |
| 135 OnSessionAttached(console_session_id_)); | 132 OnSessionAttached(console_session_id_)); |
| 136 } | 133 } |
| 137 } | 134 } |
| 138 } | 135 } |
| 139 | 136 |
| 140 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { | 137 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { |
| 141 HostService* self = HostService::GetInstance(); | 138 HostService* self = HostService::GetInstance(); |
| 142 switch (event) { | 139 switch (event) { |
| 143 case CTRL_C_EVENT: | 140 case CTRL_C_EVENT: |
| 144 case CTRL_BREAK_EVENT: | 141 case CTRL_BREAK_EVENT: |
| 145 case CTRL_CLOSE_EVENT: | 142 case CTRL_CLOSE_EVENT: |
| 146 case CTRL_LOGOFF_EVENT: | 143 case CTRL_LOGOFF_EVENT: |
| 147 case CTRL_SHUTDOWN_EVENT: | 144 case CTRL_SHUTDOWN_EVENT: |
| 148 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 145 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 146 &WtsSessionProcessLauncher::Stop, |
| 147 base::Unretained(self->launcher_.get()))); |
| 149 self->stopped_event_.Wait(); | 148 self->stopped_event_.Wait(); |
| 150 return TRUE; | 149 return TRUE; |
| 151 | 150 |
| 152 default: | 151 default: |
| 153 return FALSE; | 152 return FALSE; |
| 154 } | 153 } |
| 155 } | 154 } |
| 156 | 155 |
| 157 HostService* HostService::GetInstance() { | 156 HostService* HostService::GetInstance() { |
| 158 return Singleton<HostService>::get(); | 157 return Singleton<HostService>::get(); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 188 run_routine_ = &HostService::RunInConsole; | 187 run_routine_ = &HostService::RunInConsole; |
| 189 } | 188 } |
| 190 | 189 |
| 191 return true; | 190 return true; |
| 192 } | 191 } |
| 193 | 192 |
| 194 int HostService::Run() { | 193 int HostService::Run() { |
| 195 return (this->*run_routine_)(); | 194 return (this->*run_routine_)(); |
| 196 } | 195 } |
| 197 | 196 |
| 198 void HostService::RunMessageLoop() { | 197 void HostService::RunMessageLoop(MessageLoop* message_loop) { |
| 199 // Launch the I/O thread. | 198 // Launch the I/O thread. |
| 200 base::Thread io_thread(kIoThreadName); | 199 base::Thread io_thread(kIoThreadName); |
| 201 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); | 200 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); |
| 202 if (!io_thread.StartWithOptions(io_thread_options)) { | 201 if (!io_thread.StartWithOptions(io_thread_options)) { |
| 203 LOG(FATAL) << "Failed to start the I/O thread"; | 202 LOG(FATAL) << "Failed to start the I/O thread"; |
| 204 shutting_down_ = true; | |
| 205 stopped_event_.Signal(); | 203 stopped_event_.Signal(); |
| 206 return; | 204 return; |
| 207 } | 205 } |
| 208 | 206 |
| 209 WtsSessionProcessLauncher launcher(this, host_binary_, | 207 // Create the session process launcher. |
| 210 message_loop_->message_loop_proxy(), | 208 launcher_.reset(new WtsSessionProcessLauncher( |
| 211 io_thread.message_loop_proxy()); | 209 base::Bind(&HostService::OnLauncherShutdown, base::Unretained(this)), |
| 210 this, |
| 211 host_binary_, |
| 212 main_task_runner_, |
| 213 io_thread.message_loop_proxy())); |
| 212 | 214 |
| 213 // Run the service. | 215 // Run the service. |
| 214 message_loop_->Run(); | 216 message_loop->Run(); |
| 215 | |
| 216 // Clean up the observers by emulating detaching from the console. | |
| 217 shutting_down_ = true; | |
| 218 OnSessionChange(); | |
| 219 | 217 |
| 220 // Release the control handler. | 218 // Release the control handler. |
| 221 stopped_event_.Signal(); | 219 stopped_event_.Signal(); |
| 222 } | 220 } |
| 223 | 221 |
| 224 int HostService::RunAsService() { | 222 int HostService::RunAsService() { |
| 225 SERVICE_TABLE_ENTRYW dispatch_table[] = { | 223 SERVICE_TABLE_ENTRYW dispatch_table[] = { |
| 226 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, | 224 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, |
| 227 { NULL, NULL } | 225 { NULL, NULL } |
| 228 }; | 226 }; |
| 229 | 227 |
| 230 if (!StartServiceCtrlDispatcherW(dispatch_table)) { | 228 if (!StartServiceCtrlDispatcherW(dispatch_table)) { |
| 231 LOG_GETLASTERROR(ERROR) | 229 LOG_GETLASTERROR(ERROR) |
| 232 << "Failed to connect to the service control manager"; | 230 << "Failed to connect to the service control manager"; |
| 233 return kErrorExitCode; | 231 return kErrorExitCode; |
| 234 } | 232 } |
| 235 | 233 |
| 236 return kSuccessExitCode; | 234 return kSuccessExitCode; |
| 237 } | 235 } |
| 238 | 236 |
| 239 int HostService::RunInConsole() { | 237 int HostService::RunInConsole() { |
| 240 MessageLoop message_loop(MessageLoop::TYPE_UI); | 238 MessageLoop message_loop(MessageLoop::TYPE_UI); |
| 241 | 239 |
| 242 // Allow other threads to post to our message loop. | 240 // Allow other threads to post to our message loop. |
| 243 message_loop_ = &message_loop; | 241 main_task_runner_ = message_loop.message_loop_proxy(); |
| 244 | 242 |
| 245 int result = kErrorExitCode; | 243 int result = kErrorExitCode; |
| 246 | 244 |
| 247 // Subscribe to Ctrl-C and other console events. | 245 // Subscribe to Ctrl-C and other console events. |
| 248 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { | 246 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { |
| 249 LOG_GETLASTERROR(ERROR) | 247 LOG_GETLASTERROR(ERROR) |
| 250 << "Failed to set console control handler"; | 248 << "Failed to set console control handler"; |
| 251 return result; | 249 return result; |
| 252 } | 250 } |
| 253 | 251 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 271 window = CreateWindowW(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, | 269 window = CreateWindowW(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, |
| 272 instance, 0); | 270 instance, 0); |
| 273 if (window == NULL) { | 271 if (window == NULL) { |
| 274 LOG_GETLASTERROR(ERROR) | 272 LOG_GETLASTERROR(ERROR) |
| 275 << "Failed to creat the session notificationwindow"; | 273 << "Failed to creat the session notificationwindow"; |
| 276 goto cleanup; | 274 goto cleanup; |
| 277 } | 275 } |
| 278 | 276 |
| 279 // Post a dummy session change notification to peek up the current console | 277 // Post a dummy session change notification to peek up the current console |
| 280 // session. | 278 // session. |
| 281 message_loop.PostTask(FROM_HERE, base::Bind( | 279 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 282 &HostService::OnSessionChange, base::Unretained(this))); | 280 &HostService::OnSessionChange, base::Unretained(this))); |
| 283 | 281 |
| 284 // Subscribe to session change notifications. | 282 // Subscribe to session change notifications. |
| 285 if (WTSRegisterSessionNotification(window, | 283 if (WTSRegisterSessionNotification(window, |
| 286 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { | 284 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { |
| 287 // Run the service. | 285 // Run the service. |
| 288 RunMessageLoop(); | 286 RunMessageLoop(&message_loop); |
| 289 | 287 |
| 290 WTSUnRegisterSessionNotification(window); | 288 WTSUnRegisterSessionNotification(window); |
| 291 result = kSuccessExitCode; | 289 result = kSuccessExitCode; |
| 292 } | 290 } |
| 293 | 291 |
| 294 cleanup: | 292 cleanup: |
| 295 if (window != NULL) { | 293 if (window != NULL) { |
| 296 DestroyWindow(window); | 294 DestroyWindow(window); |
| 297 } | 295 } |
| 298 | 296 |
| 299 if (atom != 0) { | 297 if (atom != 0) { |
| 300 UnregisterClass(MAKEINTATOM(atom), instance); | 298 UnregisterClass(MAKEINTATOM(atom), instance); |
| 301 } | 299 } |
| 302 | 300 |
| 303 // Unsubscribe from console events. Ignore the exit code. There is nothing | 301 // 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 | 302 // 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. | 303 // it crashes nothing is going to be broken because of it. |
| 306 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); | 304 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); |
| 307 | 305 |
| 308 message_loop_ = NULL; | |
| 309 return result; | 306 return result; |
| 310 } | 307 } |
| 311 | 308 |
| 312 DWORD WINAPI HostService::ServiceControlHandler(DWORD control, | 309 DWORD WINAPI HostService::ServiceControlHandler(DWORD control, |
| 313 DWORD event_type, | 310 DWORD event_type, |
| 314 LPVOID event_data, | 311 LPVOID event_data, |
| 315 LPVOID context) { | 312 LPVOID context) { |
| 316 HostService* self = reinterpret_cast<HostService*>(context); | 313 HostService* self = reinterpret_cast<HostService*>(context); |
| 317 switch (control) { | 314 switch (control) { |
| 318 case SERVICE_CONTROL_INTERROGATE: | 315 case SERVICE_CONTROL_INTERROGATE: |
| 319 return NO_ERROR; | 316 return NO_ERROR; |
| 320 | 317 |
| 321 case SERVICE_CONTROL_SHUTDOWN: | 318 case SERVICE_CONTROL_SHUTDOWN: |
| 322 case SERVICE_CONTROL_STOP: | 319 case SERVICE_CONTROL_STOP: |
| 323 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 320 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 321 &WtsSessionProcessLauncher::Stop, |
| 322 base::Unretained(self->launcher_.get()))); |
| 324 self->stopped_event_.Wait(); | 323 self->stopped_event_.Wait(); |
| 325 return NO_ERROR; | 324 return NO_ERROR; |
| 326 | 325 |
| 327 case SERVICE_CONTROL_SESSIONCHANGE: | 326 case SERVICE_CONTROL_SESSIONCHANGE: |
| 328 self->message_loop_->PostTask(FROM_HERE, base::Bind( | 327 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 329 &HostService::OnSessionChange, base::Unretained(self))); | 328 &HostService::OnSessionChange, base::Unretained(self))); |
| 330 return NO_ERROR; | 329 return NO_ERROR; |
| 331 | 330 |
| 332 default: | 331 default: |
| 333 return ERROR_CALL_NOT_IMPLEMENTED; | 332 return ERROR_CALL_NOT_IMPLEMENTED; |
| 334 } | 333 } |
| 335 } | 334 } |
| 336 | 335 |
| 337 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { | 336 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { |
| 338 MessageLoop message_loop; | 337 MessageLoop message_loop; |
| 339 | 338 |
| 340 // Allow other threads to post to our message loop. | 339 // Allow other threads to post to our message loop. |
| 341 HostService* self = HostService::GetInstance(); | 340 HostService* self = HostService::GetInstance(); |
| 342 self->message_loop_ = &message_loop; | 341 self->main_task_runner_ = message_loop.message_loop_proxy(); |
| 343 | 342 |
| 344 // Register the service control handler. | 343 // Register the service control handler. |
| 345 self->service_status_handle_ = | 344 self->service_status_handle_ = |
| 346 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), | 345 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), |
| 347 &HostService::ServiceControlHandler, | 346 &HostService::ServiceControlHandler, |
| 348 self); | 347 self); |
| 349 if (self->service_status_handle_ == 0) { | 348 if (self->service_status_handle_ == 0) { |
| 350 LOG_GETLASTERROR(ERROR) | 349 LOG_GETLASTERROR(ERROR) |
| 351 << "Failed to register the service control handler"; | 350 << "Failed to register the service control handler"; |
| 352 return; | 351 return; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 363 service_status.dwWin32ExitCode = kSuccessExitCode; | 362 service_status.dwWin32ExitCode = kSuccessExitCode; |
| 364 | 363 |
| 365 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 364 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
| 366 LOG_GETLASTERROR(ERROR) | 365 LOG_GETLASTERROR(ERROR) |
| 367 << "Failed to report service status to the service control manager"; | 366 << "Failed to report service status to the service control manager"; |
| 368 return; | 367 return; |
| 369 } | 368 } |
| 370 | 369 |
| 371 // Post a dummy session change notification to peek up the current console | 370 // Post a dummy session change notification to peek up the current console |
| 372 // session. | 371 // session. |
| 373 message_loop.PostTask(FROM_HERE, base::Bind( | 372 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 374 &HostService::OnSessionChange, base::Unretained(self))); | 373 &HostService::OnSessionChange, base::Unretained(self))); |
| 375 | 374 |
| 376 // Run the service. | 375 // Run the service. |
| 377 self->RunMessageLoop(); | 376 self->RunMessageLoop(&message_loop); |
| 378 | 377 |
| 379 // Tell SCM that the service is stopped. | 378 // Tell SCM that the service is stopped. |
| 380 service_status.dwCurrentState = SERVICE_STOPPED; | 379 service_status.dwCurrentState = SERVICE_STOPPED; |
| 381 service_status.dwControlsAccepted = 0; | 380 service_status.dwControlsAccepted = 0; |
| 382 | 381 |
| 383 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 382 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
| 384 LOG_GETLASTERROR(ERROR) | 383 LOG_GETLASTERROR(ERROR) |
| 385 << "Failed to report service status to the service control manager"; | 384 << "Failed to report service status to the service control manager"; |
| 386 return; | 385 return; |
| 387 } | 386 } |
| 388 | |
| 389 self->message_loop_ = NULL; | |
| 390 } | 387 } |
| 391 | 388 |
| 392 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, | 389 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, |
| 393 UINT message, | 390 UINT message, |
| 394 WPARAM wparam, | 391 WPARAM wparam, |
| 395 LPARAM lparam) { | 392 LPARAM lparam) { |
| 396 switch (message) { | 393 switch (message) { |
| 397 case WM_WTSSESSION_CHANGE: { | 394 case WM_WTSSESSION_CHANGE: { |
| 398 HostService* self = HostService::GetInstance(); | 395 HostService* self = HostService::GetInstance(); |
| 399 self->OnSessionChange(); | 396 self->OnSessionChange(); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 438 } | 435 } |
| 439 | 436 |
| 440 remoting::HostService* service = remoting::HostService::GetInstance(); | 437 remoting::HostService* service = remoting::HostService::GetInstance(); |
| 441 if (!service->InitWithCommandLine(command_line)) { | 438 if (!service->InitWithCommandLine(command_line)) { |
| 442 usage(argv[0]); | 439 usage(argv[0]); |
| 443 return kUsageExitCode; | 440 return kUsageExitCode; |
| 444 } | 441 } |
| 445 | 442 |
| 446 return service->Run(); | 443 return service->Run(); |
| 447 } | 444 } |
| OLD | NEW |