Home | History | Annotate | Download | only in win
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "cloud_print/service/win/service_controller.h"
      6 
      7 #include <atlbase.h>
      8 #include <atlcom.h>
      9 #include <atlctl.h>
     10 
     11 #include "base/command_line.h"
     12 #include "base/files/file_path.h"
     13 #include "base/files/file_util.h"
     14 #include "base/path_service.h"
     15 #include "base/win/scoped_handle.h"
     16 #include "chrome/common/chrome_switches.h"
     17 #include "cloud_print/common/win/cloud_print_utils.h"
     18 #include "cloud_print/service/service_constants.h"
     19 #include "cloud_print/service/service_switches.h"
     20 #include "cloud_print/service/win/chrome_launcher.h"
     21 #include "cloud_print/service/win/local_security_policy.h"
     22 #include "cloud_print/service/win/service_utils.h"
     23 
     24 namespace {
     25 
     26 const wchar_t kServiceExeName[] = L"cloud_print_service.exe";
     27 
     28 // The traits class for Windows Service.
     29 class ServiceHandleTraits {
     30  public:
     31   typedef SC_HANDLE Handle;
     32 
     33   // Closes the handle.
     34   static bool CloseHandle(Handle handle) {
     35     return ::CloseServiceHandle(handle) != FALSE;
     36   }
     37 
     38   static bool IsHandleValid(Handle handle) {
     39     return handle != NULL;
     40   }
     41 
     42   static Handle NullHandle() {
     43     return NULL;
     44   }
     45 
     46  private:
     47   DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceHandleTraits);
     48 };
     49 
     50 typedef base::win::GenericScopedHandle<
     51     ServiceHandleTraits, base::win::DummyVerifierTraits> ServiceHandle;
     52 
     53 HRESULT OpenServiceManager(ServiceHandle* service_manager) {
     54   if (!service_manager)
     55     return E_POINTER;
     56 
     57   service_manager->Set(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
     58   if (!service_manager->IsValid())
     59     return cloud_print::GetLastHResult();
     60 
     61   return S_OK;
     62 }
     63 
     64 HRESULT OpenService(const base::string16& name, DWORD access,
     65                     ServiceHandle* service) {
     66   if (!service)
     67     return E_POINTER;
     68 
     69   ServiceHandle scm;
     70   HRESULT hr = OpenServiceManager(&scm);
     71   if (FAILED(hr))
     72     return hr;
     73 
     74   service->Set(::OpenService(scm.Get(), name.c_str(), access));
     75 
     76   if (!service->IsValid())
     77     return cloud_print::GetLastHResult();
     78 
     79   return S_OK;
     80 }
     81 
     82 }  // namespace
     83 
     84 ServiceController::ServiceController()
     85     : name_(cloud_print::LoadLocalString(IDS_SERVICE_NAME)),
     86       command_line_(CommandLine::NO_PROGRAM) {
     87 }
     88 
     89 ServiceController::~ServiceController() {
     90 }
     91 
     92 HRESULT ServiceController::StartService() {
     93   ServiceHandle service;
     94   HRESULT hr = OpenService(name_, SERVICE_START| SERVICE_QUERY_STATUS,
     95                            &service);
     96   if (FAILED(hr))
     97     return hr;
     98   if (!::StartService(service.Get(), 0, NULL))
     99     return cloud_print::GetLastHResult();
    100   SERVICE_STATUS status = {0};
    101   while (::QueryServiceStatus(service.Get(), &status) &&
    102           status.dwCurrentState == SERVICE_START_PENDING) {
    103     Sleep(100);
    104   }
    105   return S_OK;
    106 }
    107 
    108 HRESULT ServiceController::StopService() {
    109   ServiceHandle service;
    110   HRESULT hr = OpenService(name_, SERVICE_STOP | SERVICE_QUERY_STATUS,
    111                            &service);
    112   if (FAILED(hr))
    113     return hr;
    114   SERVICE_STATUS status = {0};
    115   if (!::ControlService(service.Get(), SERVICE_CONTROL_STOP, &status))
    116     return cloud_print::GetLastHResult();
    117   while (::QueryServiceStatus(service.Get(), &status) &&
    118           status.dwCurrentState > SERVICE_STOPPED) {
    119     Sleep(500);
    120     ::ControlService(service.Get(), SERVICE_CONTROL_STOP, &status);
    121   }
    122   return S_OK;
    123 }
    124 
    125 base::FilePath ServiceController::GetBinary() const {
    126   base::FilePath service_path;
    127   CHECK(PathService::Get(base::FILE_EXE, &service_path));
    128   return service_path.DirName().Append(base::FilePath(kServiceExeName));
    129 }
    130 
    131 HRESULT ServiceController::InstallConnectorService(
    132     const base::string16& user,
    133     const base::string16& password,
    134     const base::FilePath& user_data_dir,
    135     bool enable_logging) {
    136   return InstallService(user, password, true, kServiceSwitch, user_data_dir,
    137                         enable_logging);
    138 }
    139 
    140 HRESULT ServiceController::InstallCheckService(
    141     const base::string16& user,
    142     const base::string16& password,
    143     const base::FilePath& user_data_dir) {
    144   return InstallService(user, password, false, kRequirementsSwitch,
    145                         user_data_dir, true);
    146 }
    147 
    148 HRESULT ServiceController::InstallService(const base::string16& user,
    149                                           const base::string16& password,
    150                                           bool auto_start,
    151                                           const std::string& run_switch,
    152                                           const base::FilePath& user_data_dir,
    153                                           bool enable_logging) {
    154   // TODO(vitalybuka): consider "lite" version if we don't want unregister
    155   // printers here.
    156   HRESULT hr = UninstallService();
    157   if (FAILED(hr))
    158     return hr;
    159 
    160   hr = UpdateRegistryAppId(true);
    161   if (FAILED(hr))
    162     return hr;
    163 
    164   base::FilePath service_path = GetBinary();
    165   if (!base::PathExists(service_path))
    166     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    167   CommandLine command_line(service_path);
    168   command_line.AppendSwitch(run_switch);
    169   if (!user_data_dir.empty())
    170     command_line.AppendSwitchPath(switches::kUserDataDir, user_data_dir);
    171   if (enable_logging) {
    172     command_line.AppendSwitch(switches::kEnableLogging);
    173     command_line.AppendSwitchASCII(switches::kV, "1");
    174   }
    175 
    176   CopyChromeSwitchesFromCurrentProcess(&command_line);
    177 
    178   LocalSecurityPolicy local_security_policy;
    179   if (local_security_policy.Open()) {
    180     if (!local_security_policy.IsPrivilegeSet(user, kSeServiceLogonRight)) {
    181       LOG(WARNING) << "Setting " << kSeServiceLogonRight << " for " << user;
    182       if (!local_security_policy.SetPrivilege(user, kSeServiceLogonRight)) {
    183         LOG(ERROR) << "Failed to set" << kSeServiceLogonRight;
    184         LOG(ERROR) << "Make sure you can run the service as " << user << ".";
    185       }
    186     }
    187   } else {
    188     LOG(ERROR) << "Failed to open security policy.";
    189   }
    190 
    191   ServiceHandle scm;
    192   hr = OpenServiceManager(&scm);
    193   if (FAILED(hr))
    194     return hr;
    195 
    196   base::string16 display_name =
    197       cloud_print::LoadLocalString(IDS_SERVICE_DISPLAY_NAME);
    198   ServiceHandle service(
    199       ::CreateService(
    200           scm.Get(), name_.c_str(), display_name.c_str(), SERVICE_ALL_ACCESS,
    201           SERVICE_WIN32_OWN_PROCESS,
    202           auto_start ? SERVICE_AUTO_START : SERVICE_DEMAND_START,
    203           SERVICE_ERROR_NORMAL, command_line.GetCommandLineString().c_str(),
    204           NULL, NULL, NULL, user.empty() ? NULL : user.c_str(),
    205           password.empty() ? NULL : password.c_str()));
    206 
    207   if (!service.IsValid()) {
    208     LOG(ERROR) << "Failed to install service as " << user << ".";
    209     return cloud_print::GetLastHResult();
    210   }
    211 
    212   base::string16 description_string =
    213       cloud_print::LoadLocalString(IDS_SERVICE_DESCRIPTION);
    214   SERVICE_DESCRIPTION description = {0};
    215   description.lpDescription = const_cast<wchar_t*>(description_string.c_str());
    216   ::ChangeServiceConfig2(service.Get(), SERVICE_CONFIG_DESCRIPTION,
    217                          &description);
    218 
    219   return S_OK;
    220 }
    221 
    222 HRESULT ServiceController::UninstallService() {
    223   StopService();
    224 
    225   ServiceHandle service;
    226   OpenService(name_, SERVICE_STOP | DELETE, &service);
    227   HRESULT hr = S_FALSE;
    228   if (service.IsValid()) {
    229     if (!::DeleteService(service.Get())) {
    230       LOG(ERROR) << "Failed to uninstall service";
    231       hr = cloud_print::GetLastHResult();
    232     }
    233   }
    234   UpdateRegistryAppId(false);
    235   return hr;
    236 }
    237 
    238 HRESULT ServiceController::UpdateBinaryPath() {
    239   UpdateState();
    240   ServiceController::State origina_state = state();
    241   if (origina_state < ServiceController::STATE_STOPPED)
    242     return S_FALSE;
    243 
    244   ServiceHandle service;
    245   HRESULT hr = OpenService(name_, SERVICE_CHANGE_CONFIG, &service);
    246   if (FAILED(hr))
    247     return hr;
    248 
    249   base::FilePath service_path = GetBinary();
    250   if (!base::PathExists(service_path))
    251     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    252 
    253   command_line_.SetProgram(service_path);
    254   if (!::ChangeServiceConfig(service.Get(), SERVICE_NO_CHANGE,
    255                              SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
    256                              command_line_.GetCommandLineString().c_str(), NULL,
    257                              NULL, NULL, NULL, NULL, NULL)) {
    258     return cloud_print::GetLastHResult();
    259   }
    260 
    261   if (origina_state != ServiceController::STATE_RUNNING)
    262     return S_OK;
    263 
    264   hr = StopService();
    265   if (FAILED(hr))
    266     return hr;
    267 
    268   hr = StartService();
    269   if (FAILED(hr))
    270     return hr;
    271 
    272   return S_OK;
    273 }
    274 
    275 void ServiceController::UpdateState() {
    276   state_ = STATE_NOT_FOUND;
    277   user_.clear();
    278   is_logging_enabled_ = false;
    279 
    280   ServiceHandle service;
    281   HRESULT hr = OpenService(name_, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
    282                            &service);
    283   if (FAILED(hr))
    284     return;
    285 
    286   state_ = STATE_STOPPED;
    287   SERVICE_STATUS status = {0};
    288   if (::QueryServiceStatus(service.Get(), &status) &&
    289       status.dwCurrentState == SERVICE_RUNNING) {
    290     state_ = STATE_RUNNING;
    291   }
    292 
    293   DWORD config_size = 0;
    294   ::QueryServiceConfig(service.Get(), NULL, 0, &config_size);
    295   if (!config_size)
    296     return;
    297 
    298   std::vector<uint8> buffer(config_size, 0);
    299   QUERY_SERVICE_CONFIG* config =
    300       reinterpret_cast<QUERY_SERVICE_CONFIG*>(&buffer[0]);
    301   if (!::QueryServiceConfig(service.Get(), config, buffer.size(),
    302                             &config_size) ||
    303       config_size != buffer.size()) {
    304     return;
    305   }
    306 
    307   command_line_ = CommandLine::FromString(config->lpBinaryPathName);
    308   if (!command_line_.HasSwitch(kServiceSwitch)) {
    309     state_ = STATE_NOT_FOUND;
    310     return;
    311   }
    312   is_logging_enabled_ = command_line_.HasSwitch(switches::kEnableLogging);
    313   user_ = config->lpServiceStartName;
    314 }
    315 
    316 bool ServiceController::is_logging_enabled() const {
    317   return command_line_.HasSwitch(switches::kEnableLogging);
    318 }
    319