• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
CloseHandle(Handle handle)34   static bool CloseHandle(Handle handle) {
35     return ::CloseServiceHandle(handle) != FALSE;
36   }
37 
IsHandleValid(Handle handle)38   static bool IsHandleValid(Handle handle) {
39     return handle != NULL;
40   }
41 
NullHandle()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 
OpenServiceManager(ServiceHandle * service_manager)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 
OpenService(const base::string16 & name,DWORD access,ServiceHandle * service)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 
ServiceController()84 ServiceController::ServiceController()
85     : name_(cloud_print::LoadLocalString(IDS_SERVICE_NAME)),
86       command_line_(CommandLine::NO_PROGRAM) {
87 }
88 
~ServiceController()89 ServiceController::~ServiceController() {
90 }
91 
StartService()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 
StopService()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 
GetBinary() const125 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 
InstallConnectorService(const base::string16 & user,const base::string16 & password,const base::FilePath & user_data_dir,bool enable_logging)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 
InstallCheckService(const base::string16 & user,const base::string16 & password,const base::FilePath & 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);
146 }
147 
InstallService(const base::string16 & user,const base::string16 & password,bool auto_start,const std::string & run_switch,const base::FilePath & user_data_dir,bool enable_logging)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 
UninstallService()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 
UpdateBinaryPath()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 
UpdateState()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 
is_logging_enabled() const314 bool ServiceController::is_logging_enabled() const {
315   return command_line_.HasSwitch(switches::kEnableLogging);
316 }
317