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 <shellapi.h> |
11 #include <wtsapi32.h> | 12 #include <wtsapi32.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/base_switches.h" |
16 #include "base/bind.h" | 17 #include "base/bind.h" |
17 #include "base/command_line.h" | 18 #include "base/command_line.h" |
18 #include "base/file_path.h" | 19 #include "base/file_path.h" |
19 #include "base/logging.h" | 20 #include "base/logging.h" |
20 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
21 #include "base/single_thread_task_runner.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" |
(...skipping 20 matching lines...) Expand all Loading... |
46 | 47 |
47 // Session id that does not represent any session. | 48 // Session id that does not represent any session. |
48 const uint32 kInvalidSessionId = 0xffffffffu; | 49 const uint32 kInvalidSessionId = 0xffffffffu; |
49 | 50 |
50 const char kIoThreadName[] = "I/O thread"; | 51 const char kIoThreadName[] = "I/O thread"; |
51 | 52 |
52 // A window class for the session change notifications window. | 53 // A window class for the session change notifications window. |
53 const wchar_t kSessionNotificationWindowClass[] = | 54 const wchar_t kSessionNotificationWindowClass[] = |
54 L"Chromoting_SessionNotificationWindow"; | 55 L"Chromoting_SessionNotificationWindow"; |
55 | 56 |
56 // Command line actions and switches: | 57 // Command line switches: |
57 // "run" sumply runs the service as usual. | |
58 const wchar_t kRunActionName[] = L"run"; | |
59 | 58 |
60 // "--console" runs the service interactively for debugging purposes. | 59 // "--console" runs the service interactively for debugging purposes. |
61 const char kConsoleSwitchName[] = "console"; | 60 const char kConsoleSwitchName[] = "console"; |
62 | 61 |
| 62 // "--elevate=<binary>" requests <binary> to be launched elevated, presenting |
| 63 // a UAC prompt if necessary. |
| 64 const char kElevateSwitchName[] = "elevate"; |
| 65 |
63 // "--help" or "--?" prints the usage message. | 66 // "--help" or "--?" prints the usage message. |
64 const char kHelpSwitchName[] = "help"; | 67 const char kHelpSwitchName[] = "help"; |
65 const char kQuestionSwitchName[] = "?"; | 68 const char kQuestionSwitchName[] = "?"; |
66 | 69 |
67 const char kUsageMessage[] = | 70 const wchar_t kUsageMessage[] = |
68 "\n" | 71 L"\n" |
69 "Usage: %s [action] [options]\n" | 72 L"Usage: %ls [options]\n" |
70 "\n" | 73 L"\n" |
71 "Actions:\n" | 74 L"Options:\n" |
72 " run - Run the service (default if no action was specified).\n" | 75 L" --console - Run the service interactively for debugging purposes.\n" |
73 "\n" | 76 L" --elevate=<...> - Run <...> elevated.\n" |
74 "Options:\n" | 77 L" --help, --? - Print this message.\n"; |
75 " --console - Run the service interactively for debugging purposes.\n" | 78 |
76 " --help, --? - Print this message.\n"; | 79 // The command line parameters that should be copied from the service's command |
| 80 // line when launching an elevated child. |
| 81 const char* kCopiedSwitchNames[] = { |
| 82 "auth-config", "host-config", "chromoting-ipc", switches::kV, |
| 83 switches::kVModule }; |
77 | 84 |
78 // Exit codes: | 85 // Exit codes: |
79 const int kSuccessExitCode = 0; | 86 const int kSuccessExitCode = 0; |
80 const int kUsageExitCode = 1; | 87 const int kUsageExitCode = 1; |
81 const int kErrorExitCode = 2; | 88 const int kErrorExitCode = 2; |
82 | 89 |
83 void usage(const char* program_name) { | 90 void usage(const FilePath& program_name) { |
84 fprintf(stderr, kUsageMessage, program_name); | 91 LOG(INFO) << StringPrintf(kUsageMessage, |
| 92 UTF16ToWide(program_name.value()).c_str()); |
85 } | 93 } |
86 | 94 |
87 } // namespace | 95 } // namespace |
88 | 96 |
89 namespace remoting { | 97 namespace remoting { |
90 | 98 |
91 HostService::HostService() : | 99 HostService::HostService() : |
92 console_session_id_(kInvalidSessionId), | 100 console_session_id_(kInvalidSessionId), |
93 run_routine_(&HostService::RunAsService), | 101 run_routine_(&HostService::RunAsService), |
94 service_status_handle_(0), | 102 service_status_handle_(0), |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
157 } | 165 } |
158 } | 166 } |
159 | 167 |
160 HostService* HostService::GetInstance() { | 168 HostService* HostService::GetInstance() { |
161 return Singleton<HostService>::get(); | 169 return Singleton<HostService>::get(); |
162 } | 170 } |
163 | 171 |
164 bool HostService::InitWithCommandLine(const CommandLine* command_line) { | 172 bool HostService::InitWithCommandLine(const CommandLine* command_line) { |
165 CommandLine::StringVector args = command_line->GetArgs(); | 173 CommandLine::StringVector args = command_line->GetArgs(); |
166 | 174 |
167 // Choose the action to perform. | 175 // Check if launch with elevation was requested. |
| 176 if (command_line->HasSwitch(kElevateSwitchName)) { |
| 177 run_routine_ = &HostService::Elevate; |
| 178 return true; |
| 179 } |
| 180 |
168 if (!args.empty()) { | 181 if (!args.empty()) { |
169 if (args.size() > 1) { | 182 LOG(ERROR) << "No positional parameters expected."; |
170 LOG(ERROR) << "Invalid command line: more than one action requested."; | 183 return false; |
171 return false; | |
172 } | |
173 if (args[0] != kRunActionName) { | |
174 LOG(ERROR) << "Invalid command line: invalid action specified: " | |
175 << args[0]; | |
176 return false; | |
177 } | |
178 } | 184 } |
179 | 185 |
180 // Run interactively if needed. | 186 // Run interactively if needed. |
181 if (run_routine_ == &HostService::RunAsService && | 187 if (run_routine_ == &HostService::RunAsService && |
182 command_line->HasSwitch(kConsoleSwitchName)) { | 188 command_line->HasSwitch(kConsoleSwitchName)) { |
183 run_routine_ = &HostService::RunInConsole; | 189 run_routine_ = &HostService::RunInConsole; |
184 } | 190 } |
185 | 191 |
186 return true; | 192 return true; |
187 } | 193 } |
(...skipping 30 matching lines...) Expand all Loading... |
218 | 224 |
219 #endif // !defined(REMOTING_MULTI_PROCESS) | 225 #endif // !defined(REMOTING_MULTI_PROCESS) |
220 | 226 |
221 // Run the service. | 227 // Run the service. |
222 message_loop->Run(); | 228 message_loop->Run(); |
223 | 229 |
224 // Release the control handler. | 230 // Release the control handler. |
225 stopped_event_.Signal(); | 231 stopped_event_.Signal(); |
226 } | 232 } |
227 | 233 |
| 234 int HostService::Elevate() { |
| 235 // Get the name of the binary to launch. |
| 236 FilePath binary = |
| 237 CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName); |
| 238 |
| 239 // Create the child process command line by copying known switches from our |
| 240 // command line. |
| 241 CommandLine command_line(CommandLine::NO_PROGRAM); |
| 242 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), |
| 243 kCopiedSwitchNames, |
| 244 _countof(kCopiedSwitchNames)); |
| 245 CommandLine::StringType parameters = command_line.GetCommandLineString(); |
| 246 |
| 247 // Launch the child process requesting elevation. |
| 248 SHELLEXECUTEINFO info; |
| 249 memset(&info, 0, sizeof(info)); |
| 250 info.cbSize = sizeof(info); |
| 251 info.lpVerb = L"runas"; |
| 252 info.lpFile = binary.value().c_str(); |
| 253 info.lpParameters = parameters.c_str(); |
| 254 info.nShow = SW_SHOWNORMAL; |
| 255 |
| 256 if (!ShellExecuteEx(&info)) { |
| 257 return GetLastError(); |
| 258 } |
| 259 |
| 260 return kSuccessExitCode; |
| 261 } |
| 262 |
228 int HostService::RunAsService() { | 263 int HostService::RunAsService() { |
229 SERVICE_TABLE_ENTRYW dispatch_table[] = { | 264 SERVICE_TABLE_ENTRYW dispatch_table[] = { |
230 { const_cast<LPWSTR>(kWindowsServiceName), &HostService::ServiceMain }, | 265 { const_cast<LPWSTR>(kWindowsServiceName), &HostService::ServiceMain }, |
231 { NULL, NULL } | 266 { NULL, NULL } |
232 }; | 267 }; |
233 | 268 |
234 if (!StartServiceCtrlDispatcherW(dispatch_table)) { | 269 if (!StartServiceCtrlDispatcherW(dispatch_table)) { |
235 LOG_GETLASTERROR(ERROR) | 270 LOG_GETLASTERROR(ERROR) |
236 << "Failed to connect to the service control manager"; | 271 << "Failed to connect to the service control manager"; |
237 return kErrorExitCode; | 272 return kErrorExitCode; |
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
402 return 0; | 437 return 0; |
403 } | 438 } |
404 | 439 |
405 default: | 440 default: |
406 return DefWindowProc(hwnd, message, wparam, lparam); | 441 return DefWindowProc(hwnd, message, wparam, lparam); |
407 } | 442 } |
408 } | 443 } |
409 | 444 |
410 } // namespace remoting | 445 } // namespace remoting |
411 | 446 |
412 int main(int argc, char** argv) { | 447 int CALLBACK WinMain(HINSTANCE instance, |
| 448 HINSTANCE previous_instance, |
| 449 LPSTR raw_command_line, |
| 450 int show_command) { |
413 #ifdef OFFICIAL_BUILD | 451 #ifdef OFFICIAL_BUILD |
414 if (remoting::IsUsageStatsAllowed()) { | 452 if (remoting::IsUsageStatsAllowed()) { |
415 remoting::InitializeCrashReporting(); | 453 remoting::InitializeCrashReporting(); |
416 } | 454 } |
417 #endif // OFFICIAL_BUILD | 455 #endif // OFFICIAL_BUILD |
418 | 456 |
419 CommandLine::Init(argc, argv); | 457 // CommandLine::Init() ignores the passed |argc| and |argv| on Windows getting |
| 458 // the command line from GetCommandLineW(), so we can safely pass NULL here. |
| 459 CommandLine::Init(0, NULL); |
420 | 460 |
421 // This object instance is required by Chrome code (for example, | 461 // This object instance is required by Chrome code (for example, |
422 // FilePath, LazyInstance, MessageLoop). | 462 // FilePath, LazyInstance, MessageLoop). |
423 base::AtExitManager exit_manager; | 463 base::AtExitManager exit_manager; |
424 | 464 |
425 // Write logs to the application profile directory. | 465 // Write logs to the application profile directory. |
426 FilePath debug_log = remoting::GetConfigDir(). | 466 FilePath debug_log = remoting::GetConfigDir(). |
427 Append(FILE_PATH_LITERAL("debug.log")); | 467 Append(FILE_PATH_LITERAL("debug.log")); |
428 InitLogging(debug_log.value().c_str(), | 468 InitLogging(debug_log.value().c_str(), |
429 logging::LOG_ONLY_TO_FILE, | 469 logging::LOG_ONLY_TO_FILE, |
430 logging::DONT_LOCK_LOG_FILE, | 470 logging::DONT_LOCK_LOG_FILE, |
431 logging::APPEND_TO_OLD_LOG_FILE, | 471 logging::APPEND_TO_OLD_LOG_FILE, |
432 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); | 472 logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); |
433 | 473 |
434 const CommandLine* command_line = CommandLine::ForCurrentProcess(); | 474 const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
435 | |
436 if (command_line->HasSwitch(kHelpSwitchName) || | 475 if (command_line->HasSwitch(kHelpSwitchName) || |
437 command_line->HasSwitch(kQuestionSwitchName)) { | 476 command_line->HasSwitch(kQuestionSwitchName)) { |
438 usage(argv[0]); | 477 usage(command_line->GetProgram()); |
439 return kSuccessExitCode; | 478 return kSuccessExitCode; |
440 } | 479 } |
441 | 480 |
442 remoting::HostService* service = remoting::HostService::GetInstance(); | 481 remoting::HostService* service = remoting::HostService::GetInstance(); |
443 if (!service->InitWithCommandLine(command_line)) { | 482 if (!service->InitWithCommandLine(command_line)) { |
444 usage(argv[0]); | 483 usage(command_line->GetProgram()); |
445 return kUsageExitCode; | 484 return kUsageExitCode; |
446 } | 485 } |
447 | 486 |
448 return service->Run(); | 487 return service->Run(); |
449 } | 488 } |
OLD | NEW |