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