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