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/file_util.h"
     13 #include "base/files/file_path.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, 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, 0, NULL))
     99     return cloud_print::GetLastHResult();
    100   SERVICE_STATUS status = {0};
    101   while (::QueryServiceStatus(service, &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, SERVICE_CONTROL_STOP, &status))
    116     return cloud_print::GetLastHResult();
    117   while (::QueryServiceStatus(service, &status) &&
    118           status.dwCurrentState > SERVICE_STOPPED) {
    119     Sleep(500);
    120     ::ControlService(service, 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, 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, SERVICE_CONFIG_DESCRIPTION, &description);
    217 
    218   return S_OK;
    219 }
    220 
    221 HRESULT ServiceController::UninstallService() {
    222   StopService();
    223 
    224   ServiceHandle service;
    225   OpenService(name_, SERVICE_STOP | DELETE, &service);
    226   HRESULT hr = S_FALSE;
    227   if (service) {
    228     if (!::DeleteService(service)) {
    229       LOG(ERROR) << "Failed to uninstall service";
    230       hr = cloud_print::GetLastHResult();
    231     }
    232   }
    233   UpdateRegistryAppId(false);
    234   return hr;
    235 }
    236 
    237 HRESULT ServiceController::UpdateBinaryPath() {
    238   UpdateState();
    239   ServiceController::State origina_state = state();
    240   if (origina_state < ServiceController::STATE_STOPPED)
    241     return S_FALSE;
    242 
    243   ServiceHandle service;
    244   HRESULT hr = OpenService(name_, SERVICE_CHANGE_CONFIG, &service);
    245   if (FAILED(hr))
    246     return hr;
    247 
    248   base::FilePath service_path = GetBinary();
    249   if (!base::PathExists(service_path))
    250     return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
    251 
    252   command_line_.SetProgram(service_path);
    253   if (!::ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
    254                              SERVICE_NO_CHANGE,
    255                              command_line_.GetCommandLineString().c_str(), NULL,
    256                              NULL, NULL, NULL, NULL, NULL)) {
    257     return cloud_print::GetLastHResult();
    258   }
    259 
    260   if (origina_state != ServiceController::STATE_RUNNING)
    261     return S_OK;
    262 
    263   hr = StopService();
    264   if (FAILED(hr))
    265     return hr;
    266 
    267   hr = StartService();
    268   if (FAILED(hr))
    269     return hr;
    270 
    271   return S_OK;
    272 }
    273 
    274 void ServiceController::UpdateState() {
    275   state_ = STATE_NOT_FOUND;
    276   user_.clear();
    277   is_logging_enabled_ = false;
    278 
    279   ServiceHandle service;
    280   HRESULT hr = OpenService(name_, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
    281                            &service);
    282   if (FAILED(hr))
    283     return;
    284 
    285   state_ = STATE_STOPPED;
    286   SERVICE_STATUS status = {0};
    287   if (::QueryServiceStatus(service, &status) &&
    288       status.dwCurrentState == SERVICE_RUNNING) {
    289     state_ = STATE_RUNNING;
    290   }
    291 
    292   DWORD config_size = 0;
    293   ::QueryServiceConfig(service, NULL, 0, &config_size);
    294   if (!config_size)
    295     return;
    296 
    297   std::vector<uint8> buffer(config_size, 0);
    298   QUERY_SERVICE_CONFIG* config =
    299       reinterpret_cast<QUERY_SERVICE_CONFIG*>(&buffer[0]);
    300   if (!::QueryServiceConfig(service, config, buffer.size(), &config_size) ||
    301       config_size != buffer.size()) {
    302     return;
    303   }
    304 
    305   command_line_ = CommandLine::FromString(config->lpBinaryPathName);
    306   if (!command_line_.HasSwitch(kServiceSwitch)) {
    307     state_ = STATE_NOT_FOUND;
    308     return;
    309   }
    310   is_logging_enabled_ = command_line_.HasSwitch(switches::kEnableLogging);
    311   user_ = config->lpServiceStartName;
    312 }
    313 
    314 bool ServiceController::is_logging_enabled() const {
    315   return command_line_.HasSwitch(switches::kEnableLogging);
    316 }
    317