diff options
author | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-10 02:55:36 +0000 |
---|---|---|
committer | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-02-10 02:55:36 +0000 |
commit | cd5a32f483300bc2ba17f1b9aae589bd4f128eaa (patch) | |
tree | 01d8d98cf19fb4de0dc3c06545cd5ec7d38c0541 | |
parent | f4c378dd2231a2917ee1e07206e631ced38ea811 (diff) | |
download | chromium_src-cd5a32f483300bc2ba17f1b9aae589bd4f128eaa.zip chromium_src-cd5a32f483300bc2ba17f1b9aae589bd4f128eaa.tar.gz chromium_src-cd5a32f483300bc2ba17f1b9aae589bd4f128eaa.tar.bz2 |
1. Implemented installation and uninstallation code for the Chromoting service.
2. Introduced base::win::ScopedScHandle wrapper to make sure SCM handles are released properly.
Review URL: http://codereview.chromium.org/9358034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@121389 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | remoting/base/scoped_sc_handle_win.cc | 45 | ||||
-rw-r--r-- | remoting/base/scoped_sc_handle_win.h | 43 | ||||
-rw-r--r-- | remoting/host/remoting_host_service.rc | 113 | ||||
-rw-r--r-- | remoting/host/remoting_host_service_resource.h | 21 | ||||
-rw-r--r-- | remoting/host/remoting_host_service_win.cc | 238 | ||||
-rw-r--r-- | remoting/host/remoting_host_service_win.h | 42 | ||||
-rw-r--r-- | remoting/remoting.gyp | 5 |
7 files changed, 457 insertions, 50 deletions
diff --git a/remoting/base/scoped_sc_handle_win.cc b/remoting/base/scoped_sc_handle_win.cc new file mode 100644 index 0000000..3e062e6 --- /dev/null +++ b/remoting/base/scoped_sc_handle_win.cc @@ -0,0 +1,45 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/base/scoped_sc_handle_win.h" + +namespace remoting { + +ScopedScHandle::ScopedScHandle() : handle_(NULL) { +} + +ScopedScHandle::ScopedScHandle(SC_HANDLE h) : handle_(NULL) { + Set(h); +} + +ScopedScHandle::~ScopedScHandle() { + Close(); +} + +void ScopedScHandle::Close() { + if (handle_) { + if (!::CloseServiceHandle(handle_)) { + NOTREACHED(); + } + handle_ = NULL; + } +} + +bool ScopedScHandle::IsValid() const { + return handle_ != NULL; +} + +void ScopedScHandle::Set(SC_HANDLE new_handle) { + Close(); + handle_ = new_handle; +} + +SC_HANDLE ScopedScHandle::Take() { + // transfers ownership away from this object + SC_HANDLE h = handle_; + handle_ = NULL; + return h; +} + +} // namespace remoting diff --git a/remoting/base/scoped_sc_handle_win.h b/remoting/base/scoped_sc_handle_win.h new file mode 100644 index 0000000..da623ad --- /dev/null +++ b/remoting/base/scoped_sc_handle_win.h @@ -0,0 +1,43 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_BASE_SCOPED_SC_HANDLE_WIN_H_ +#define REMOTING_BASE_SCOPED_SC_HANDLE_WIN_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace remoting { + +// Used so we always remember to close service control manager handles. +// The class interface matches that of base::win::ScopedHandle. +class ScopedScHandle { + public: + ScopedScHandle(); + explicit ScopedScHandle(SC_HANDLE h); + ~ScopedScHandle(); + + SC_HANDLE Get() { + return handle_; + } + + operator SC_HANDLE() { + return handle_; + } + + void Close(); + bool IsValid() const; + void Set(SC_HANDLE new_handle); + SC_HANDLE Take(); + + private: + SC_HANDLE handle_; + DISALLOW_COPY_AND_ASSIGN(ScopedScHandle); +}; + +} // namespace remoting + +#endif // REMOTING_BASE_SCOPED_SC_HANDLE_WIN_H_ diff --git a/remoting/host/remoting_host_service.rc b/remoting/host/remoting_host_service.rc new file mode 100644 index 0000000..a24db8c --- /dev/null +++ b/remoting/host/remoting_host_service.rc @@ -0,0 +1,113 @@ +// Microsoft Visual C++ generated resource script. +// +#include "remoting_host_service_resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "remoting_host_service_resource.\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Chromoting Host Service" + VALUE "FileVersion", "1, 0, 0, 1" + VALUE "InternalName", "remoting_host_service" + VALUE "LegalCopyright", "Copyright (C) 2012 The Chromium Authors. All rights reserved." + VALUE "OriginalFilename", "remoting_host_service.exe" + VALUE "ProductName", "Chromoting Host Service" + VALUE "ProductVersion", "1, 0, 0, 1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_DISPLAY_SERVICE_NAME "Chromoting Host Service" + IDS_SERVICE_DESCRIPTION "This service enables incoming Chromoting connections." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/remoting/host/remoting_host_service_resource.h b/remoting/host/remoting_host_service_resource.h new file mode 100644 index 0000000..36be281 --- /dev/null +++ b/remoting/host/remoting_host_service_resource.h @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by remoting_host_service.rc +// +#define IDS_DISPLAY_SERVICE_NAME 101 +#define IDS_SERVICE_DESCRIPTION 102 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/remoting/host/remoting_host_service_win.cc b/remoting/host/remoting_host_service_win.cc index 50a5fa8..22b3d10 100644 --- a/remoting/host/remoting_host_service_win.cc +++ b/remoting/host/remoting_host_service_win.cc @@ -5,23 +5,45 @@ // This file implements the Windows service controlling Me2Me host processes // running within user sessions. +#include "remoting/host/remoting_host_service_win.h" + #include <windows.h> #include <stdio.h> +#include "base/at_exit.h" +#include "base/base_paths.h" #include "base/command_line.h" +#include "base/file_path.h" #include "base/logging.h" +#include "base/path_service.h" +#include "base/string16.h" +#include "base/stringprintf.h" +#include "base/utf_string_conversions.h" + +#include "remoting/base/scoped_sc_handle_win.h" +#include "remoting/host/remoting_host_service_resource.h" + +using base::StringPrintf; namespace { +// Service name. +const char kServiceName[] = "chromoting"; +// TODO(alexeypa): investigate and migrate this over to Chrome's i18n framework. +const char kMuiStringFormat[] = "@%ls,-%d"; +const char kServiceDependencies[] = ""; + +const DWORD kServiceStopTimeoutMs = 30 * 1000; + // Command line actions and switches: // "run" sumply runs the service as usual. -const wchar_t kRunActionName[] = L"run"; +const char kRunActionName[] = "run"; // "install" requests the service to be installed. -const wchar_t kInstallActionName[] = L"install"; +const char kInstallActionName[] = "install"; // "remove" uninstalls the service. -const wchar_t kRemoveActionName[] = L"remove"; +const char kRemoveActionName[] = "remove"; // "--console" runs the service interactively for debugging purposes. const char kConsoleSwitchName[] = "console"; @@ -46,6 +68,7 @@ const char kUsageMessage[] = // Exit codes: const int kSuccessExitCode = 0; const int kUsageExitCode = 1; +const int kErrorExitCode = 2; void usage(const char* program_name) { fprintf(stderr, kUsageMessage, program_name); @@ -55,79 +78,194 @@ void usage(const char* program_name) { namespace remoting { -class HostService { - public: - HostService(): - run_routine_(&HostService::RunAsService) { +HostService::HostService() : + run_routine_(&HostService::RunAsService) { +} + +HostService::~HostService() { +} + +bool HostService::InitWithCommandLine(const CommandLine* command_line) { + CommandLine::StringVector args = command_line->GetArgs(); + + // Choose the action to perform. + if (!args.empty()) { + if (args.size() > 1) { + LOG(ERROR) << "Invalid command line: more than one action requested."; + return false; + } + if (args[0] == ASCIIToUTF16(kInstallActionName)) { + run_routine_ = &HostService::Install; + } else if (args[0] == ASCIIToUTF16(kRemoveActionName)) { + run_routine_ = &HostService::Remove; + } else if (args[0] != ASCIIToUTF16(kRunActionName)) { + LOG(ERROR) << "Invalid command line: invalid action specified: " + << args[0]; + return false; + } + } + + // Run interactively if needed. + if (run_routine_ == &HostService::RunAsService && + command_line->HasSwitch(kConsoleSwitchName)) { + run_routine_ = &HostService::RunInConsole; + } + + return true; +} + +int HostService::Install() { + ScopedScHandle scmanager( + OpenSCManagerW(NULL, NULL, + SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE)); + if (!scmanager.IsValid()) { + LOG_GETLASTERROR(ERROR) + << "Failed to connect to the service control manager"; + return kErrorExitCode; } - ~HostService() { + FilePath exe; + if (!PathService::Get(base::FILE_EXE, &exe)) { + LOG(ERROR) << "Unable to retrieve the service binary path."; + return kErrorExitCode; } - // This function parses the command line and selects the action routine. - bool InitWithCommandLine(const CommandLine* command_line) { - CommandLine::StringVector args = command_line->GetArgs(); - - // Choose the action to perform. - if (!args.empty()) { - if (args.size() > 1) { - LOG(ERROR) << "Invalid command line: more than one action requested."; - return false; - } - if (args[0] == kInstallActionName) { - run_routine_ = &HostService::Install; - } else if (args[0] == kRemoveActionName) { - run_routine_ = &HostService::Remove; - } else if (args[0] != kRunActionName) { - LOG(ERROR) << "Invalid command line: invalid action specified: " - << args[0]; - return false; - } + string16 name = StringPrintf(ASCIIToUTF16(kMuiStringFormat).c_str(), + exe.value().c_str(), + IDS_DISPLAY_SERVICE_NAME); + ScopedScHandle service( + CreateServiceW(scmanager, + ASCIIToUTF16(kServiceName).c_str(), + name.c_str(), + SERVICE_QUERY_STATUS | SERVICE_CHANGE_CONFIG, + SERVICE_WIN32_OWN_PROCESS, + SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, + exe.value().c_str(), + NULL, + NULL, + ASCIIToUTF16(kServiceDependencies).c_str(), + NULL, + NULL)); + + if (service.IsValid()) { + // Set the service description if the service is freshly installed. + string16 description = StringPrintf( + ASCIIToUTF16(kMuiStringFormat).c_str(), + exe.value().c_str(), + IDS_SERVICE_DESCRIPTION); + + SERVICE_DESCRIPTIONW info; + info.lpDescription = const_cast<LPWSTR>(description.c_str()); + if (!ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, &info)) { + LOG_GETLASTERROR(ERROR) << "Failed to set the service description"; + return kErrorExitCode; } - // Run interactively if needed. - if (run_routine_ == &HostService::RunAsService && - command_line->HasSwitch(kConsoleSwitchName)) { - run_routine_ = &HostService::RunInConsole; + printf("The service has been installed successfully.\n"); + return kSuccessExitCode; + } else { + if (GetLastError() == ERROR_SERVICE_EXISTS) { + printf("The service is installed already.\n"); + return kSuccessExitCode; + } else { + LOG_GETLASTERROR(ERROR) + << "Failed to create the service"; + return kErrorExitCode; } + } +} - return true; +VOID CALLBACK HostService::OnServiceStopped(PVOID context) { + SERVICE_NOTIFY* notify = reinterpret_cast<SERVICE_NOTIFY*>(context); + DCHECK(notify != NULL); + DCHECK(notify->dwNotificationStatus == ERROR_SUCCESS); + DCHECK(notify->dwNotificationTriggered == SERVICE_NOTIFY_STOPPED); + DCHECK(notify->ServiceStatus.dwCurrentState == SERVICE_STOPPED); +} + +int HostService::Remove() { + ScopedScHandle scmanager(OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT)); + if (!scmanager.IsValid()) { + LOG_GETLASTERROR(ERROR) + << "Failed to connect to the service control manager"; + return kErrorExitCode; } - // Invoke the choosen action routine. - int Run() { - return (this->*run_routine_)(); + ScopedScHandle service( + OpenServiceW(scmanager, ASCIIToUTF16(kServiceName).c_str(), + DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS)); + if (!service.IsValid()) { + if (GetLastError() == ERROR_SERVICE_DOES_NOT_EXIST) { + printf("The service is not installed.\n"); + return kSuccessExitCode; + } else { + LOG_GETLASTERROR(ERROR) << "Failed to open the service"; + return kErrorExitCode; + } } - private: - int Install() { - NOTIMPLEMENTED(); - return 0; + // Register for the service status notifications. The notification is + // going to be delivered even if the service is stopped already. + SERVICE_NOTIFY notify; + ZeroMemory(¬ify, sizeof(notify)); + notify.dwVersion = SERVICE_NOTIFY_STATUS_CHANGE; + notify.pfnNotifyCallback = &HostService::OnServiceStopped; + notify.pContext = this; + + // The notification callback will be unregistered once the service handle + // is closed. + if (ERROR_SUCCESS != NotifyServiceStatusChange( + service, SERVICE_NOTIFY_STOPPED, ¬ify)) { + LOG_GETLASTERROR(ERROR) + << "Failed to register for the service status notifications"; + return kErrorExitCode; } - int Remove() { - NOTIMPLEMENTED(); - return 0; + // Ask SCM to stop the service and wait. + SERVICE_STATUS status; + if (ControlService(service, SERVICE_CONTROL_STOP, &status)) { + printf("Stopping...\n"); } - int RunAsService() { - NOTIMPLEMENTED(); - return 0; + DWORD wait_result = SleepEx(kServiceStopTimeoutMs, TRUE); + if (wait_result != WAIT_IO_COMPLETION) { + LOG(ERROR) << "Failed to stop the service."; + return kErrorExitCode; } - int RunInConsole() { - NOTIMPLEMENTED(); - return 0; + // Try to delete the service now. + if (!DeleteService(service)) { + LOG_GETLASTERROR(ERROR) << "Failed to delete the service"; + return kErrorExitCode; } - int (HostService::*run_routine_)(); -}; + printf("The service has been removed successfully.\n"); + return kSuccessExitCode; +} + +int HostService::Run() { + return (this->*run_routine_)(); +} + +int HostService::RunAsService() { + NOTIMPLEMENTED(); + return 0; +} + +int HostService::RunInConsole() { + NOTIMPLEMENTED(); + return 0; +} } // namespace remoting int main(int argc, char** argv) { CommandLine::Init(argc, argv); + // This object instance is required by Chrome code (for example, + // FilePath, LazyInstance, MessageLoop). + base::AtExitManager exit_manager; + const CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(kHelpSwitchName) || diff --git a/remoting/host/remoting_host_service_win.h b/remoting/host/remoting_host_service_win.h new file mode 100644 index 0000000..6cfd55e --- /dev/null +++ b/remoting/host/remoting_host_service_win.h @@ -0,0 +1,42 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_REMOTING_HOST_SERVICE_WIN_H_ +#define REMOTING_HOST_REMOTING_HOST_SERVICE_WIN_H_ + +#include <windows.h> + +class CommandLine; + +namespace remoting { + +class HostService { + public: + HostService(); + ~HostService(); + + // This function parses the command line and selects the action routine. + bool InitWithCommandLine(const CommandLine* command_line); + + // Invoke the choosen action routine. + int Run(); + + private: + // This routine registers the service with the service control manager. + int Install(); + + static VOID CALLBACK OnServiceStopped(PVOID context); + + // This routine uninstalls the service previously regerested by Install(). + int Remove(); + + int RunAsService(); + int RunInConsole(); + + int (HostService::*run_routine_)(); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_REMOTING_HOST_SERVICE_WIN_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 2501195..ab5b31b 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -237,7 +237,12 @@ '../base/base.gyp:base', ], 'sources': [ + 'base/scoped_sc_handle_win.cc', + 'base/scoped_sc_handle_win.h', + 'host/remoting_host_service.rc', + 'host/remoting_host_service_resource.h', 'host/remoting_host_service_win.cc', + 'host/remoting_host_service_win.h', ], }, # end of target 'remoting_service' ], |