From 17ac9a41a5febde114e988b02e3bb1decb15c62d Mon Sep 17 00:00:00 2001 From: "vitalybuka@chromium.org" Date: Tue, 23 Apr 2013 08:27:43 +0000 Subject: Added Cloud Print Service installer/uninstaller. Added Google Update integration. R=gene BUG=229183 NOTRY=true Review URL: https://chromiumcodereview.appspot.com/14358019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195765 0039d316-1c4b-4281-b951-d872f2087c98 --- cloud_print/service/service.gyp | 4 + cloud_print/service/service_constants.cc | 10 ++ cloud_print/service/service_constants.h | 13 +++ cloud_print/service/service_switches.cc | 1 + cloud_print/service/service_switches.h | 1 + cloud_print/service/win/chrome_launcher.cc | 14 ++- cloud_print/service/win/cloud_print_service.cc | 5 +- .../service/win/cloud_print_service_config.cc | 8 +- cloud_print/service/win/installer.cc | 125 +++++++++++++++++++++ cloud_print/service/win/installer.h | 15 +++ cloud_print/service/win/service_controller.cc | 62 ++++++++-- cloud_print/service/win/service_controller.h | 8 +- cloud_print/service/win/service_resources.grd | 3 + 13 files changed, 251 insertions(+), 18 deletions(-) create mode 100644 cloud_print/service/service_constants.cc create mode 100644 cloud_print/service/service_constants.h create mode 100644 cloud_print/service/win/installer.cc create mode 100644 cloud_print/service/win/installer.h diff --git a/cloud_print/service/service.gyp b/cloud_print/service/service.gyp index 9b5bb5bef..17384c7 100644 --- a/cloud_print/service/service.gyp +++ b/cloud_print/service/service.gyp @@ -75,6 +75,8 @@ '<(DEPTH)/content/public/common/content_switches.cc', '<(DEPTH)/cloud_print/common/win/cloud_print_utils.cc', '<(DEPTH)/cloud_print/common/win/cloud_print_utils.h', + 'service_constants.cc', + 'service_constants.h', 'service_state.cc', 'service_state.h', 'service_switches.cc', @@ -122,6 +124,8 @@ 'sources': [ '<(SHARED_INTERMEDIATE_DIR)/cloud_print/cloud_print_service_config_exe_version.rc', 'win/cloud_print_service_config.cc', + 'win/installer.cc', + 'win/installer.h', ], 'includes': [ 'win/service_resources.gypi' diff --git a/cloud_print/service/service_constants.cc b/cloud_print/service/service_constants.cc new file mode 100644 index 0000000..74b51f8 --- /dev/null +++ b/cloud_print/service/service_constants.cc @@ -0,0 +1,10 @@ +// Copyright 2013 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 "cloud_print/service/service_constants.h" + +const wchar_t kGoogleUpdateId[] = L"{86206CC6-CD3C-427D-9A34-77B34819CDD3}"; + +const wchar_t kSubDirectory[] = L"Google\\Cloud Print Service"; + diff --git a/cloud_print/service/service_constants.h b/cloud_print/service/service_constants.h new file mode 100644 index 0000000..75dfec2 --- /dev/null +++ b/cloud_print/service/service_constants.h @@ -0,0 +1,13 @@ +// Copyright 2013 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 CLOUD_PRINT_SERVICE_SERVICE_CONSTANTS_H_ +#define CLOUD_PRINT_SERVICE_SERVICE_CONSTANTS_H_ + +extern const wchar_t kGoogleUpdateId[]; + +extern const wchar_t kSubDirectory[]; + +#endif // CLOUD_PRINT_SERVICE_SERVICE_CONSTANTS_H_ + diff --git a/cloud_print/service/service_switches.cc b/cloud_print/service/service_switches.cc index 9386777..72afdd0 100644 --- a/cloud_print/service/service_switches.cc +++ b/cloud_print/service/service_switches.cc @@ -5,6 +5,7 @@ #include "cloud_print/service/service_switches.h" const char kConsoleSwitch[] = "console"; +const char kDeleteSwitch[] = "delete"; const char kInstallSwitch[] = "install"; const char kRequirementsSwitch[] = "requirements"; const char kServiceSwitch[] = "service"; diff --git a/cloud_print/service/service_switches.h b/cloud_print/service/service_switches.h index ae92fed..b6e11e4 100644 --- a/cloud_print/service/service_switches.h +++ b/cloud_print/service/service_switches.h @@ -6,6 +6,7 @@ #define CLOUD_PRINT_SERVICE_SERVICE_SWITCHES_H_ extern const char kConsoleSwitch[]; +extern const char kDeleteSwitch[]; extern const char kInstallSwitch[]; extern const char kRequirementsSwitch[]; extern const char kServiceSwitch[]; diff --git a/cloud_print/service/win/chrome_launcher.cc b/cloud_print/service/win/chrome_launcher.cc index 831af6f..6c5bbc5 100644 --- a/cloud_print/service/win/chrome_launcher.cc +++ b/cloud_print/service/win/chrome_launcher.cc @@ -20,6 +20,8 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/installer/launcher_support/chrome_launcher_support.h" +#include "cloud_print/common/win/cloud_print_utils.h" +#include "cloud_print/service/service_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "googleurl/src/gurl.h" #include "net/base/url_util.h" @@ -27,6 +29,7 @@ namespace { const int kShutdownTimeoutMs = 30 * 1000; +const int kUsageUpdateTimeoutMs = 6 * 3600 * 1000; // 6 hours. static const char16 kAutoRunKeyPath[] = L"Software\\Microsoft\\Windows\\CurrentVersion\\Run"; @@ -202,10 +205,14 @@ void ChromeLauncher::Run() { base::Time started = base::Time::Now(); DWORD thread_id = 0; LaunchProcess(cmd, chrome_handle.Receive(), &thread_id); - int exit_code = 0; + HANDLE handles[] = {stop_event_.handle(), chrome_handle}; - DWORD wait_result = ::WaitForMultipleObjects(arraysize(handles), handles, - FALSE, INFINITE); + DWORD wait_result = WAIT_TIMEOUT; + while (wait_result == WAIT_TIMEOUT) { + cloud_print::SetGoogleUpdateUsage(kGoogleUpdateId); + wait_result = ::WaitForMultipleObjects(arraysize(handles), handles, + FALSE, kUsageUpdateTimeoutMs); + } if (wait_result == WAIT_OBJECT_0) { ShutdownChrome(chrome_handle, thread_id); break; @@ -289,7 +296,6 @@ std::string ChromeLauncher::CreateServiceStateFile( return result; } - int exit_code = 0; DWORD wait_result = ::WaitForSingleObject(chrome_handle, INFINITE); if (wait_result != WAIT_OBJECT_0) { LOG(ERROR) << "Chrome launch failed."; diff --git a/cloud_print/service/win/cloud_print_service.cc b/cloud_print/service/win/cloud_print_service.cc index dd20668..f29d4e7 100644 --- a/cloud_print/service/win/cloud_print_service.cc +++ b/cloud_print/service/win/cloud_print_service.cc @@ -20,6 +20,7 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "cloud_print/common/win/cloud_print_utils.h" +#include "cloud_print/service/service_constants.h" #include "cloud_print/service/service_state.h" #include "cloud_print/service/service_switches.h" #include "cloud_print/service/win/chrome_launcher.h" @@ -30,8 +31,6 @@ namespace { -const wchar_t kAppDataSubDir[] = L"Google\\Cloud Print Service"; - void InvalidUsage() { base::FilePath service_path; CHECK(PathService::Get(base::FILE_EXE, &service_path)); @@ -367,7 +366,7 @@ class CloudPrintServiceModule return user_data_dir_switch_; base::FilePath result; CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &result)); - return result.Append(kAppDataSubDir); + return result.Append(kSubDirectory); } static BOOL WINAPI ConsoleCtrlHandler(DWORD type); diff --git a/cloud_print/service/win/cloud_print_service_config.cc b/cloud_print/service/win/cloud_print_service_config.cc index eff74f7..84598b5 100644 --- a/cloud_print/service/win/cloud_print_service_config.cc +++ b/cloud_print/service/win/cloud_print_service_config.cc @@ -17,6 +17,7 @@ #include "cloud_print/resources.h" #include "cloud_print/service/service_state.h" #include "cloud_print/service/win/chrome_launcher.h" +#include "cloud_print/service/win/installer.h" #include "cloud_print/service/win/service_controller.h" #include "cloud_print/service/win/service_utils.h" #include "cloud_print/service/win/setup_listener.h" @@ -201,8 +202,7 @@ LRESULT SetupDialog::OnInitDialog(UINT message, WPARAM wparam, LPARAM lparam, ATLVERIFY(CenterWindow()); WTL::CIcon icon; - if (icon.LoadIcon(MAKEINTRESOURCE(IDI_ICON), GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON), 0)) { + if (icon.LoadIcon(MAKEINTRESOURCE(IDI_ICON))) { SetIcon(icon); } @@ -428,6 +428,10 @@ int WINAPI WinMain(__in HINSTANCE hInstance, base::AtExitManager at_exit; CommandLine::Init(0, NULL); + HRESULT result = ProcessInstallerSwitches(); + if (result != S_FALSE) + return result; + MessageLoopForUI loop; scoped_refptr dialog(new SetupDialog()); dialog->Create(NULL); diff --git a/cloud_print/service/win/installer.cc b/cloud_print/service/win/installer.cc new file mode 100644 index 0000000..c82b1c8 --- /dev/null +++ b/cloud_print/service/win/installer.cc @@ -0,0 +1,125 @@ +// Copyright 2013 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 "cloud_print/service/win/installer.h" + +#include + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/shortcut.h" +#include "cloud_print/common/win/cloud_print_utils.h" +#include "cloud_print/common/win/install_utils.h" +#include "cloud_print/service/service_constants.h" +#include "cloud_print/service/service_switches.h" +#include "cloud_print/service/win/service_controller.h" + +namespace { + +base::FilePath GetShortcutPath(int dir_key, bool with_subdir) { + base::FilePath path; + if (!PathService::Get(dir_key, &path)) + return base::FilePath(); + path = path.Append(cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME)); + if (with_subdir) + path = path.Append(cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME)); + return path.InsertBeforeExtension(L".lnk"); +} + +void CreateShortcut(int dir_key, bool with_subdir, + base::win::ShortcutOperation operation) { + base::FilePath path = GetShortcutPath(dir_key, with_subdir); + if (path.empty()) + return; + file_util::CreateDirectory(path.DirName()); + base::win::ShortcutProperties properties; + + base::FilePath exe_path; + if (!PathService::Get(base::FILE_EXE, &exe_path)) + return; + properties.set_target(exe_path); + properties.set_working_dir(exe_path.DirName()); + CreateOrUpdateShortcutLink(path, properties, operation); +} + +void CreateShortcuts(bool create_always) { + base::win::ScopedCOMInitializer co_init; + base::win::ShortcutOperation operation = + create_always ? base::win::SHORTCUT_CREATE_ALWAYS : + base::win::SHORTCUT_REPLACE_EXISTING; + CreateShortcut(base::DIR_COMMON_START_MENU, true, operation); + CreateShortcut(base::DIR_COMMON_DESKTOP, false, operation); +} + +void DeleteShortcut(int dir_key, bool with_subdir) { + base::FilePath path = GetShortcutPath(dir_key, with_subdir); + if (path.empty()) + return; + if (with_subdir) + file_util::Delete(path.DirName(), true); + else + file_util::Delete(path, false); +} + +void DeleteShortcuts() { + DeleteShortcut(base::DIR_COMMON_START_MENU, true); + DeleteShortcut(base::DIR_COMMON_DESKTOP, false); +} + +} // namespace + +HRESULT ProcessInstallerSwitches() { + const CommandLine& command_line(*CommandLine::ForCurrentProcess()); + + if (command_line.HasSwitch(kInstallSwitch)) { + base::FilePath old_location = + cloud_print::GetInstallLocation(kGoogleUpdateId); + + cloud_print::CreateUninstallKey( + kGoogleUpdateId, cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME), + kUninstallSwitch); + + ServiceController controller(cloud_print::LoadLocalString(IDS_SERVICENAME)); + HRESULT hr = controller.UpdateBinaryPath(); + if (FAILED(hr)) + return hr; + + if (!old_location.empty() && + cloud_print::IsProgramsFilesParent(old_location) && + old_location != cloud_print::GetInstallLocation(kGoogleUpdateId)) { + file_util::Delete(old_location, true); + } + + cloud_print::SetGoogleUpdateKeys( + kGoogleUpdateId, cloud_print::LoadLocalString(IDS_FULL_PRODUCT_NAME)); + + CreateShortcuts(old_location.empty()); + + return S_OK; + } else if (command_line.HasSwitch(kUninstallSwitch)) { + ServiceController controller(cloud_print::LoadLocalString(IDS_SERVICENAME)); + HRESULT hr = controller.UninstallService(); + if (FAILED(hr)) + return hr; + + DeleteShortcuts(); + + cloud_print::DeleteGoogleUpdateKeys(kGoogleUpdateId); + cloud_print::DeleteUninstallKey(kGoogleUpdateId); + cloud_print::DeleteProgramDir(kDeleteSwitch); + return S_OK; + } else if (command_line.HasSwitch(kDeleteSwitch)) { + base::FilePath delete_path = command_line.GetSwitchValuePath(kDeleteSwitch); + if (!delete_path.empty() && + cloud_print::IsProgramsFilesParent(delete_path)) { + file_util::Delete(delete_path, true); + } + return S_OK; + } + + return S_FALSE; +} + diff --git a/cloud_print/service/win/installer.h b/cloud_print/service/win/installer.h new file mode 100644 index 0000000..027e97b --- /dev/null +++ b/cloud_print/service/win/installer.h @@ -0,0 +1,15 @@ +// Copyright 2013 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 CLOUD_PRINT_SERVICE_INSTALLER_H_ +#define CLOUD_PRINT_SERVICE_INSTALLER_H_ + +#include + +// Installs/uninstalls product if related switches provided. +// Returns S_OK on success or error code. +// Returns S_FALE if no related switches were provided. +HRESULT ProcessInstallerSwitches(); + +#endif // CLOUD_PRINT_SERVICE_INSTALLER_H_ \ No newline at end of file diff --git a/cloud_print/service/win/service_controller.cc b/cloud_print/service/win/service_controller.cc index dd643d1..3b748d1 100644 --- a/cloud_print/service/win/service_controller.cc +++ b/cloud_print/service/win/service_controller.cc @@ -15,6 +15,7 @@ #include "base/win/scoped_handle.h" #include "chrome/common/chrome_switches.h" #include "cloud_print/common/win/cloud_print_utils.h" +#include "cloud_print/service/service_constants.h" #include "cloud_print/service/service_switches.h" #include "cloud_print/service/win/chrome_launcher.h" #include "cloud_print/service/win/local_security_policy.h" @@ -80,7 +81,7 @@ HRESULT OpenService(const string16& name, DWORD access, } // namespace ServiceController::ServiceController(const string16& name) - : name_(name) { + : name_(name), command_line_(CommandLine::NO_PROGRAM) { } ServiceController::~ServiceController() { @@ -119,6 +120,12 @@ HRESULT ServiceController::StopService() { return S_OK; } +base::FilePath ServiceController::GetBinary() const { + base::FilePath service_path; + CHECK(PathService::Get(base::FILE_EXE, &service_path)); + return service_path.DirName().Append(base::FilePath(kServiceExeName)); +} + HRESULT ServiceController::InstallConnectorService( const string16& user, const string16& password, @@ -152,9 +159,7 @@ HRESULT ServiceController::InstallService(const string16& user, if (FAILED(hr)) return hr; - base::FilePath service_path; - CHECK(PathService::Get(base::FILE_EXE, &service_path)); - service_path = service_path.DirName().Append(base::FilePath(kServiceExeName)); + base::FilePath service_path = GetBinary(); if (!file_util::PathExists(service_path)) return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); CommandLine command_line(service_path); @@ -218,12 +223,49 @@ HRESULT ServiceController::UninstallService() { return hr; } -void ServiceController::UpdateState() { +HRESULT ServiceController::UpdateBinaryPath() { + UpdateState(); + ServiceController::State origina_state = state(); + if (origina_state < ServiceController::STATE_STOPPED) + return S_FALSE; + ServiceHandle service; + HRESULT hr = OpenService(name_, SERVICE_CHANGE_CONFIG, &service); + if (FAILED(hr)) + return hr; + + base::FilePath service_path = GetBinary(); + if (!file_util::PathExists(service_path)) + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + command_line_.SetProgram(service_path); + if (!::ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, + SERVICE_NO_CHANGE, + command_line_.GetCommandLineString().c_str(), NULL, + NULL, NULL, NULL, NULL, NULL)) { + return cloud_print::GetLastHResult(); + } + + if (origina_state != ServiceController::STATE_RUNNING) + return S_OK; + + hr = StopService(); + if (FAILED(hr)) + return hr; + + hr = StartService(); + if (FAILED(hr)) + return hr; + + return S_OK; +} + +void ServiceController::UpdateState() { state_ = STATE_NOT_FOUND; user_.clear(); is_logging_enabled_ = false; + ServiceHandle service; HRESULT hr = OpenService(name_, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG, &service); if (FAILED(hr)) @@ -249,11 +291,15 @@ void ServiceController::UpdateState() { return; } - CommandLine command_line(CommandLine::FromString(config->lpBinaryPathName)); - if (!command_line.HasSwitch(kServiceSwitch)) { + command_line_ = CommandLine::FromString(config->lpBinaryPathName); + if (!command_line_.HasSwitch(kServiceSwitch)) { state_ = STATE_NOT_FOUND; return; } - is_logging_enabled_ = command_line.HasSwitch(switches::kEnableLogging); + is_logging_enabled_ = command_line_.HasSwitch(switches::kEnableLogging); user_ = config->lpServiceStartName; } + +bool ServiceController::is_logging_enabled() const { + return command_line_.HasSwitch(switches::kEnableLogging); +} diff --git a/cloud_print/service/win/service_controller.h b/cloud_print/service/win/service_controller.h index d703c0a..ab95396 100644 --- a/cloud_print/service/win/service_controller.h +++ b/cloud_print/service/win/service_controller.h @@ -8,6 +8,7 @@ #include #include +#include "base/command_line.h" #include "base/string16.h" #include "cloud_print/resources.h" @@ -46,11 +47,15 @@ class ServiceController { HRESULT StartService(); HRESULT StopService(); + HRESULT UpdateBinaryPath(); + // Query service status and options. Results accessible with getters below. void UpdateState(); State state() const { return state_; } const string16& user() const { return user_; } - bool is_logging_enabled() const { return is_logging_enabled_; } + bool is_logging_enabled() const; + + base::FilePath GetBinary() const; private: HRESULT InstallService(const string16& user, @@ -64,6 +69,7 @@ class ServiceController { State state_; string16 user_; bool is_logging_enabled_; + CommandLine command_line_; }; #endif // CLOUD_PRINT_SERVICE_SERVICE_CONTROLLER_H_ diff --git a/cloud_print/service/win/service_resources.grd b/cloud_print/service/win/service_resources.grd index 765f48f..a804e42 100644 --- a/cloud_print/service/win/service_resources.grd +++ b/cloud_print/service/win/service_resources.grd @@ -21,6 +21,9 @@ file. CloudPrintService + + Google Cloud Print Service + Service Options -- cgit v1.1