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"
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"
26 const wchar_t kServiceExeName
[] = L
"cloud_print_service.exe";
28 // The traits class for Windows Service.
29 class ServiceHandleTraits
{
31 typedef SC_HANDLE 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() {
47 DISALLOW_IMPLICIT_CONSTRUCTORS(ServiceHandleTraits
);
50 typedef base::win::GenericScopedHandle
<
51 ServiceHandleTraits
, base::win::DummyVerifierTraits
> ServiceHandle
;
53 HRESULT
OpenServiceManager(ServiceHandle
* service_manager
) {
57 service_manager
->Set(::OpenSCManager(NULL
, NULL
, SC_MANAGER_ALL_ACCESS
));
58 if (!service_manager
->IsValid())
59 return cloud_print::GetLastHResult();
64 HRESULT
OpenService(const base::string16
& name
, DWORD access
,
65 ServiceHandle
* service
) {
70 HRESULT hr
= OpenServiceManager(&scm
);
74 service
->Set(::OpenService(scm
.Get(), name
.c_str(), access
));
76 if (!service
->IsValid())
77 return cloud_print::GetLastHResult();
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
,
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
) {
108 HRESULT
ServiceController::StopService() {
109 ServiceHandle service
;
110 HRESULT hr
= OpenService(name_
, SERVICE_STOP
| SERVICE_QUERY_STATUS
,
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
) {
120 ::ControlService(service
.Get(), SERVICE_CONTROL_STOP
, &status
);
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
,
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
,
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
156 HRESULT hr
= UninstallService();
160 hr
= UpdateRegistryAppId(true);
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
<< ".";
188 LOG(ERROR
) << "Failed to open security policy.";
192 hr
= OpenServiceManager(&scm
);
196 base::string16 display_name
=
197 cloud_print::LoadLocalString(IDS_SERVICE_DISPLAY_NAME
);
198 ServiceHandle
service(
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
,
222 HRESULT
ServiceController::UninstallService() {
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);
238 HRESULT
ServiceController::UpdateBinaryPath() {
240 ServiceController::State origina_state
= state();
241 if (origina_state
< ServiceController::STATE_STOPPED
)
244 ServiceHandle service
;
245 HRESULT hr
= OpenService(name_
, SERVICE_CHANGE_CONFIG
, &service
);
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
)
275 void ServiceController::UpdateState() {
276 state_
= STATE_NOT_FOUND
;
278 is_logging_enabled_
= false;
280 ServiceHandle service
;
281 HRESULT hr
= OpenService(name_
, SERVICE_QUERY_STATUS
| SERVICE_QUERY_CONFIG
,
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
);
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(),
303 config_size
!= buffer
.size()) {
307 command_line_
= base::CommandLine::FromString(config
->lpBinaryPathName
);
308 if (!command_line_
.HasSwitch(kServiceSwitch
)) {
309 state_
= STATE_NOT_FOUND
;
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
);