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/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"
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
, 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_(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
, 0, NULL
))
99 return cloud_print::GetLastHResult();
100 SERVICE_STATUS status
= {0};
101 while (::QueryServiceStatus(service
, &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
, SERVICE_CONTROL_STOP
, &status
))
116 return cloud_print::GetLastHResult();
117 while (::QueryServiceStatus(service
, &status
) &&
118 status
.dwCurrentState
> SERVICE_STOPPED
) {
120 ::ControlService(service
, 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 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
, 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
, SERVICE_CONFIG_DESCRIPTION
, &description
);
221 HRESULT
ServiceController::UninstallService() {
224 ServiceHandle service
;
225 OpenService(name_
, SERVICE_STOP
| DELETE
, &service
);
226 HRESULT hr
= S_FALSE
;
228 if (!::DeleteService(service
)) {
229 LOG(ERROR
) << "Failed to uninstall service";
230 hr
= cloud_print::GetLastHResult();
233 UpdateRegistryAppId(false);
237 HRESULT
ServiceController::UpdateBinaryPath() {
239 ServiceController::State origina_state
= state();
240 if (origina_state
< ServiceController::STATE_STOPPED
)
243 ServiceHandle service
;
244 HRESULT hr
= OpenService(name_
, SERVICE_CHANGE_CONFIG
, &service
);
248 base::FilePath service_path
= GetBinary();
249 if (!base::PathExists(service_path
))
250 return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
252 command_line_
.SetProgram(service_path
);
253 if (!::ChangeServiceConfig(service
, SERVICE_NO_CHANGE
, SERVICE_NO_CHANGE
,
255 command_line_
.GetCommandLineString().c_str(), NULL
,
256 NULL
, NULL
, NULL
, NULL
, NULL
)) {
257 return cloud_print::GetLastHResult();
260 if (origina_state
!= ServiceController::STATE_RUNNING
)
274 void ServiceController::UpdateState() {
275 state_
= STATE_NOT_FOUND
;
277 is_logging_enabled_
= false;
279 ServiceHandle service
;
280 HRESULT hr
= OpenService(name_
, SERVICE_QUERY_STATUS
| SERVICE_QUERY_CONFIG
,
285 state_
= STATE_STOPPED
;
286 SERVICE_STATUS status
= {0};
287 if (::QueryServiceStatus(service
, &status
) &&
288 status
.dwCurrentState
== SERVICE_RUNNING
) {
289 state_
= STATE_RUNNING
;
292 DWORD config_size
= 0;
293 ::QueryServiceConfig(service
, NULL
, 0, &config_size
);
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()) {
305 command_line_
= CommandLine::FromString(config
->lpBinaryPathName
);
306 if (!command_line_
.HasSwitch(kServiceSwitch
)) {
307 state_
= STATE_NOT_FOUND
;
310 is_logging_enabled_
= command_line_
.HasSwitch(switches::kEnableLogging
);
311 user_
= config
->lpServiceStartName
;
314 bool ServiceController::is_logging_enabled() const {
315 return command_line_
.HasSwitch(switches::kEnableLogging
);