Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / cloud_print / service / win / service_controller.cc
blob102e9988b0adbdaff4276c3228c26b60f007ef23
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.
5 #include "cloud_print/service/win/service_controller.h"
7 #include <atlbase.h>
8 #include <atlcom.h>
9 #include <atlctl.h>
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"
24 namespace {
26 const wchar_t kServiceExeName[] = L"cloud_print_service.exe";
28 // The traits class for Windows Service.
29 class ServiceHandleTraits {
30 public:
31 typedef SC_HANDLE Handle;
33 // Closes the handle.
34 static bool CloseHandle(Handle handle) {
35 return ::CloseServiceHandle(handle) != FALSE;
38 static bool IsHandleValid(Handle handle) {
39 return handle != NULL;
42 static Handle NullHandle() {
43 return NULL;
46 private:
47 DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceHandleTraits);
50 typedef base::win::GenericScopedHandle<
51 ServiceHandleTraits, base::win::DummyVerifierTraits> ServiceHandle;
53 HRESULT OpenServiceManager(ServiceHandle* service_manager) {
54 if (!service_manager)
55 return E_POINTER;
57 service_manager->Set(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
58 if (!service_manager->IsValid())
59 return cloud_print::GetLastHResult();
61 return S_OK;
64 HRESULT OpenService(const base::string16& name, DWORD access,
65 ServiceHandle* service) {
66 if (!service)
67 return E_POINTER;
69 ServiceHandle scm;
70 HRESULT hr = OpenServiceManager(&scm);
71 if (FAILED(hr))
72 return hr;
74 service->Set(::OpenService(scm.Get(), name.c_str(), access));
76 if (!service->IsValid())
77 return cloud_print::GetLastHResult();
79 return S_OK;
82 } // namespace
84 ServiceController::ServiceController()
85 : name_(cloud_print::LoadLocalString(IDS_SERVICE_NAME)),
86 command_line_(base::CommandLine::NO_PROGRAM) {
89 ServiceController::~ServiceController() {
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);
105 return S_OK;
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);
122 return S_OK;
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));
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);
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);
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;
160 hr = UpdateRegistryAppId(true);
161 if (FAILED(hr))
162 return hr;
164 base::FilePath service_path = GetBinary();
165 if (!base::PathExists(service_path))
166 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
167 base::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");
176 CopyChromeSwitchesFromCurrentProcess(&command_line);
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 << ".";
187 } else {
188 LOG(ERROR) << "Failed to open security policy.";
191 ServiceHandle scm;
192 hr = OpenServiceManager(&scm);
193 if (FAILED(hr))
194 return hr;
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()));
207 if (!service.IsValid()) {
208 LOG(ERROR) << "Failed to install service as " << user << ".";
209 return cloud_print::GetLastHResult();
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);
219 return S_OK;
222 HRESULT ServiceController::UninstallService() {
223 StopService();
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();
234 UpdateRegistryAppId(false);
235 return hr;
238 HRESULT ServiceController::UpdateBinaryPath() {
239 UpdateState();
240 ServiceController::State origina_state = state();
241 if (origina_state < ServiceController::STATE_STOPPED)
242 return S_FALSE;
244 ServiceHandle service;
245 HRESULT hr = OpenService(name_, SERVICE_CHANGE_CONFIG, &service);
246 if (FAILED(hr))
247 return hr;
249 base::FilePath service_path = GetBinary();
250 if (!base::PathExists(service_path))
251 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
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();
261 if (origina_state != ServiceController::STATE_RUNNING)
262 return S_OK;
264 hr = StopService();
265 if (FAILED(hr))
266 return hr;
268 hr = StartService();
269 if (FAILED(hr))
270 return hr;
272 return S_OK;
275 void ServiceController::UpdateState() {
276 state_ = STATE_NOT_FOUND;
277 user_.clear();
278 is_logging_enabled_ = false;
280 ServiceHandle service;
281 HRESULT hr = OpenService(name_, SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
282 &service);
283 if (FAILED(hr))
284 return;
286 state_ = STATE_STOPPED;
287 SERVICE_STATUS status = {0};
288 if (::QueryServiceStatus(service.Get(), &status) &&
289 status.dwCurrentState == SERVICE_RUNNING) {
290 state_ = STATE_RUNNING;
293 DWORD config_size = 0;
294 ::QueryServiceConfig(service.Get(), NULL, 0, &config_size);
295 if (!config_size)
296 return;
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;
307 command_line_ = base::CommandLine::FromString(config->lpBinaryPathName);
308 if (!command_line_.HasSwitch(kServiceSwitch)) {
309 state_ = STATE_NOT_FOUND;
310 return;
312 is_logging_enabled_ = command_line_.HasSwitch(switches::kEnableLogging);
313 user_ = config->lpServiceStartName;
316 bool ServiceController::is_logging_enabled() const {
317 return command_line_.HasSwitch(switches::kEnableLogging);