summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-10 02:55:36 +0000
committeralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-10 02:55:36 +0000
commitcd5a32f483300bc2ba17f1b9aae589bd4f128eaa (patch)
tree01d8d98cf19fb4de0dc3c06545cd5ec7d38c0541
parentf4c378dd2231a2917ee1e07206e631ced38ea811 (diff)
downloadchromium_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.cc45
-rw-r--r--remoting/base/scoped_sc_handle_win.h43
-rw-r--r--remoting/host/remoting_host_service.rc113
-rw-r--r--remoting/host/remoting_host_service_resource.h21
-rw-r--r--remoting/host/remoting_host_service_win.cc238
-rw-r--r--remoting/host/remoting_host_service_win.h42
-rw-r--r--remoting/remoting.gyp5
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(&notify, 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, &notify)) {
+ 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'
],