OLD | NEW |
| (Empty) |
1 // Copyright 2007-2010 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 // ======================================================================== | |
15 // | |
16 // Sets up and controls the Google Update service. | |
17 | |
18 // TODO(omaha3): Consolidate all the service related code into one file in base | |
19 // and one file in service. | |
20 | |
21 #ifndef OMAHA_SETUP_SETUP_SERVICE_H_ | |
22 #define OMAHA_SETUP_SETUP_SERVICE_H_ | |
23 | |
24 #include <windows.h> | |
25 #include "base/basictypes.h" | |
26 #include "omaha/base/constants.h" | |
27 #include "omaha/base/debug.h" | |
28 #include "omaha/base/error.h" | |
29 #include "omaha/base/logging.h" | |
30 #include "omaha/base/path.h" | |
31 #include "omaha/base/safe_format.h" | |
32 #include "omaha/base/scoped_any.h" | |
33 #include "omaha/base/service_utils.h" | |
34 #include "omaha/base/system_info.h" | |
35 #include "omaha/client/resource.h" | |
36 #include "omaha/common/command_line_builder.h" | |
37 #include "omaha/common/const_cmd_line.h" | |
38 #include "omaha/common/const_goopdate.h" | |
39 #include "omaha/service/service_main.h" | |
40 | |
41 namespace omaha { | |
42 | |
43 const uint32 kMaxQueryConfigBufferBytes = 8 * 1024; | |
44 | |
45 template <typename T> | |
46 class SetupService { | |
47 public: | |
48 static HRESULT StartService() { | |
49 OPT_LOG(L1, (_T("[StartService]"))); | |
50 | |
51 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
52 if (!scm) { | |
53 return HRESULTFromLastError(); | |
54 } | |
55 | |
56 CString service_name(T::GetCurrentServiceName()); | |
57 scoped_service service(::OpenService(get(scm), | |
58 service_name, | |
59 SERVICE_QUERY_STATUS | SERVICE_START)); | |
60 if (!service) { | |
61 return HRESULTFromLastError(); | |
62 } | |
63 | |
64 SERVICE_STATUS status = {0}; | |
65 if (::QueryServiceStatus(get(service), &status)) { | |
66 if (status.dwCurrentState == SERVICE_RUNNING || | |
67 status.dwCurrentState == SERVICE_START_PENDING) { | |
68 SETUP_LOG(L1, (_T("[QueryServiceStatus][Service already running][%u]"), | |
69 status.dwCurrentState)); | |
70 return S_OK; | |
71 } | |
72 } | |
73 | |
74 // Start the service. | |
75 if (::StartService(get(service), 0, NULL)) { | |
76 SETUP_LOG(L1, (_T("[StartService][started]"))); | |
77 return S_OK; | |
78 } | |
79 | |
80 HRESULT hr = HRESULTFromLastError(); | |
81 ASSERT1(hr != HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING)); | |
82 if (hr == HRESULT_FROM_WIN32(ERROR_SERVICE_ALREADY_RUNNING)) { | |
83 SETUP_LOG(L1, (_T("[StartService][ERROR_SERVICE_ALREADY_RUNNING]"))); | |
84 return S_OK; | |
85 } | |
86 | |
87 return hr; | |
88 } | |
89 | |
90 static HRESULT StopService() { | |
91 SETUP_LOG(L1, (_T("[StopService]"))); | |
92 | |
93 CString service_name(T::GetCurrentServiceName()); | |
94 return ServiceInstall::StopService(service_name); | |
95 } | |
96 | |
97 static HRESULT InstallService(const TCHAR* file_path) { | |
98 ASSERT1(file_path); | |
99 | |
100 // Append the arguments to be passed to the service entry point. | |
101 | |
102 CString service_cmd_line(file_path); | |
103 CommandLineBuilder builder(T::commandline_mode()); | |
104 SafeCStringAppendFormat(&service_cmd_line, _T(" %s"), | |
105 builder.GetCommandLineArgs()); | |
106 | |
107 SETUP_LOG(L2, (_T("[service command line][%s]"), service_cmd_line)); | |
108 | |
109 HRESULT hr = DoInstallService(service_cmd_line); | |
110 if (FAILED(hr)) { | |
111 SETUP_LOG(LE, (_T("[DoInstallService failed][0x%08x]"), hr)); | |
112 return hr; | |
113 } | |
114 | |
115 VERIFY1(SUCCEEDED(SetDescription(GetServiceDescription()))); | |
116 VERIFY1(SUCCEEDED(SetDelayedAutoStart())); | |
117 | |
118 return InstallCOMService(); | |
119 } | |
120 | |
121 // Uninstalls the service by: | |
122 // 1. unregistering it | |
123 // 2. deleting it from SCM if needed. | |
124 static HRESULT UninstallService() { | |
125 SETUP_LOG(L3, (_T("[UninstallService][%s]"), T::GetCurrentServiceName())); | |
126 | |
127 HRESULT hr = StopService(); | |
128 if (FAILED(hr)) { | |
129 SETUP_LOG(LW, (_T("[StopService failed][0x%08x]"), hr)); | |
130 ASSERT1(HRESULT_FROM_WIN32(ERROR_SERVICE_DOES_NOT_EXIST) == hr); | |
131 } | |
132 | |
133 VERIFY1(SUCCEEDED(UninstallCOMService())); | |
134 | |
135 hr = DeleteService(); | |
136 if (FAILED(hr)) { | |
137 OPT_LOG(LEVEL_ERROR, (_T("[Can't delete the service][0x%08x]"), hr)); | |
138 return hr; | |
139 } | |
140 | |
141 return S_OK; | |
142 } | |
143 | |
144 static bool IsServiceInstalled() { | |
145 return ServiceInstall::IsServiceInstalled(T::GetCurrentServiceName()); | |
146 } | |
147 | |
148 private: | |
149 static HRESULT InstallCOMService() { | |
150 SETUP_LOG(L1, (_T("[InstallCOMService]"))); | |
151 | |
152 HRESULT hr = ServiceModule<T>().RegisterCOMService(); | |
153 | |
154 // We reset the _pAtlModule to allow for the case where multiple instances | |
155 // of ServiceModule are installed serially. | |
156 _pAtlModule = NULL; | |
157 | |
158 return hr; | |
159 } | |
160 | |
161 static HRESULT UninstallCOMService() { | |
162 SETUP_LOG(L1, (_T("[UninstallCOMService]"))); | |
163 | |
164 HRESULT hr = ServiceModule<T>().UnregisterCOMService(); | |
165 | |
166 // We reset the _pAtlModule to allow for the case where multiple instances | |
167 // of ServiceModule are uninstalled serially. | |
168 _pAtlModule = NULL; | |
169 | |
170 return hr; | |
171 } | |
172 | |
173 static HRESULT DoInstallService(const TCHAR* service_cmd_line) { | |
174 SETUP_LOG(L1, (_T("[DoInstallService][%s]"), service_cmd_line)); | |
175 | |
176 ASSERT1(service_cmd_line); | |
177 | |
178 if (IsServiceInstalled()) { | |
179 // Lightweight upgrade of existing service. | |
180 HRESULT hr = UpgradeService(service_cmd_line); | |
181 ASSERT(SUCCEEDED(hr), (_T("[UpgradeService failed][0x%x]"), hr)); | |
182 if (SUCCEEDED(hr)) { | |
183 return hr; | |
184 } | |
185 | |
186 // Delete the previous version of the service. Then create a new service | |
187 // name, and fall through to install that. | |
188 VERIFY1(SUCCEEDED(DeleteService())); | |
189 VERIFY1(SUCCEEDED(CreateAndSetVersionedServiceNameInRegistry())); | |
190 ASSERT1(!IsServiceInstalled()); | |
191 } | |
192 | |
193 return DoInstallNewService(service_cmd_line); | |
194 } | |
195 | |
196 static HRESULT DoInstallNewService(const TCHAR* service_cmd_line) { | |
197 ASSERT1(service_cmd_line); | |
198 | |
199 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
200 if (!scm) { | |
201 const DWORD error = ::GetLastError(); | |
202 SETUP_LOG(LE, (_T("[Failed to open SC Manager][%u]"), error)); | |
203 return HRESULT_FROM_WIN32(error); | |
204 } | |
205 | |
206 CString service_name(T::GetCurrentServiceName()); | |
207 CString service_display_name(GetCurrentServiceDisplayName()); | |
208 scoped_service service(::CreateService(get(scm), | |
209 service_name, | |
210 service_display_name, | |
211 SERVICE_ALL_ACCESS, | |
212 SERVICE_WIN32_OWN_PROCESS, | |
213 T::service_start_type(), | |
214 SERVICE_ERROR_NORMAL, | |
215 service_cmd_line, | |
216 NULL, | |
217 NULL, | |
218 _T("RPCSS\0"), | |
219 NULL, | |
220 NULL)); | |
221 if (!service) { | |
222 const DWORD error = ::GetLastError(); | |
223 SETUP_LOG(LE, (_T("[CreateService failed][%u]"), error)); | |
224 return HRESULT_FROM_WIN32(error); | |
225 } | |
226 SETUP_LOG(L1, (_T("[DoInstallNewService][service installed]"))); | |
227 return S_OK; | |
228 } | |
229 | |
230 static bool IsServiceCorrectlyConfigured(const TCHAR* service_cmd_line) { | |
231 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)); | |
232 if (!scm) { | |
233 HRESULT hr = HRESULTFromLastError(); | |
234 SETUP_LOG(LE, (_T("[Failed to open SC Manager][0x%x]"), hr)); | |
235 return false; | |
236 } | |
237 | |
238 CString service_name(T::GetCurrentServiceName()); | |
239 scoped_service service(::OpenService(get(scm), | |
240 service_name, | |
241 SERVICE_QUERY_CONFIG)); | |
242 if (!service) { | |
243 HRESULT hr = HRESULTFromLastError(); | |
244 SETUP_LOG(LE, (_T("[OpenService SERVICE_QUERY_CONFIG fail][0x%x]"), hr)); | |
245 return false; | |
246 } | |
247 | |
248 // ::QueryServiceConfig expects a buffer of at most 8K bytes, according to | |
249 // documentation. While the size of the buffer can be dynamically computed, | |
250 // we just assume the maximum size for simplicity. | |
251 uint8 buffer[kMaxQueryConfigBufferBytes] = { 0 }; | |
252 DWORD bytes_needed_ignored = 0; | |
253 QUERY_SERVICE_CONFIG* service_config = | |
254 reinterpret_cast<QUERY_SERVICE_CONFIG*>(buffer); | |
255 if (!::QueryServiceConfig(get(service), | |
256 service_config, | |
257 sizeof(buffer), | |
258 &bytes_needed_ignored)) { | |
259 HRESULT hr = HRESULTFromLastError(); | |
260 SETUP_LOG(LE, (_T("[QueryServiceConfig failed][0x%x]"), hr)); | |
261 return false; | |
262 } | |
263 | |
264 bool does_service_cmd_line_match = false; | |
265 if (service_config->lpBinaryPathName == NULL || service_cmd_line == NULL) { | |
266 does_service_cmd_line_match = | |
267 (service_config->lpBinaryPathName == service_cmd_line); | |
268 } else { | |
269 does_service_cmd_line_match = | |
270 !_tcsicmp(service_config->lpBinaryPathName, service_cmd_line); | |
271 } | |
272 return service_config->dwServiceType == SERVICE_WIN32_OWN_PROCESS && | |
273 service_config->dwStartType == T::service_start_type() && | |
274 service_config->dwErrorControl == SERVICE_ERROR_NORMAL && | |
275 does_service_cmd_line_match; | |
276 } | |
277 | |
278 static HRESULT UpgradeService(const TCHAR* service_cmd_line) { | |
279 ASSERT1(service_cmd_line); | |
280 ASSERT1(IsServiceInstalled()); | |
281 | |
282 if (IsServiceCorrectlyConfigured(service_cmd_line)) { | |
283 return S_OK; | |
284 } | |
285 | |
286 // Modify the configuration of the existing service. | |
287 HRESULT hr(StopService()); | |
288 if (FAILED(hr)) { | |
289 SETUP_LOG(LE, (_T("[Can't stop the service][0x%08x]"), hr)); | |
290 return hr; | |
291 } | |
292 | |
293 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
294 if (!scm) { | |
295 const DWORD error = ::GetLastError(); | |
296 SETUP_LOG(LE, (_T("[Failed to open SC Manager][%u]"), error)); | |
297 return HRESULT_FROM_WIN32(error); | |
298 } | |
299 | |
300 CString service_name(T::GetCurrentServiceName()); | |
301 scoped_service service(::OpenService(get(scm), | |
302 service_name, | |
303 SERVICE_CHANGE_CONFIG)); | |
304 if (!service) { | |
305 const DWORD error = ::GetLastError(); | |
306 SETUP_LOG(LE, (_T("[Failed to open service for update][%u]"), error)); | |
307 return HRESULT_FROM_WIN32(error); | |
308 } | |
309 | |
310 if (!::ChangeServiceConfig(get(service), | |
311 SERVICE_WIN32_OWN_PROCESS, | |
312 T::service_start_type(), | |
313 SERVICE_ERROR_NORMAL, | |
314 service_cmd_line, | |
315 NULL, | |
316 NULL, | |
317 NULL, | |
318 NULL, | |
319 NULL, | |
320 NULL)) { | |
321 const DWORD error = ::GetLastError(); | |
322 SETUP_LOG(LE, (_T("[Failed to change service config][%u]"), error)); | |
323 return HRESULT_FROM_WIN32(error); | |
324 } | |
325 | |
326 SETUP_LOG(L3, (_T("[ChangeServiceConfig succeeded]"))); | |
327 return S_OK; | |
328 } | |
329 | |
330 static bool IsDelayedAutoStart() { | |
331 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)); | |
332 if (!scm) { | |
333 HRESULT hr = HRESULTFromLastError(); | |
334 SETUP_LOG(LE, (_T("[SC_MANAGER_CONNECT failed][0x%x]"), hr)); | |
335 return false; | |
336 } | |
337 | |
338 CString name(T::GetCurrentServiceName()); | |
339 scoped_service service(::OpenService(get(scm), name, SERVICE_QUERY_CONFIG)); | |
340 if (!service) { | |
341 HRESULT hr = HRESULTFromLastError(); | |
342 SETUP_LOG(LE, (_T("[SERVICE_QUERY_CONFIG failed][%s][0x%x]"), name, hr)); | |
343 return false; | |
344 } | |
345 | |
346 uint8 buffer[kMaxQueryConfigBufferBytes] = { 0 }; | |
347 DWORD bytes_needed_ignored = 0; | |
348 SERVICE_DELAYED_AUTO_START_INFO* service_config = | |
349 reinterpret_cast<SERVICE_DELAYED_AUTO_START_INFO*>(buffer); | |
350 if (!::QueryServiceConfig2(get(service), | |
351 SERVICE_CONFIG_DELAYED_AUTO_START_INFO, | |
352 buffer, | |
353 sizeof(buffer), | |
354 &bytes_needed_ignored)) { | |
355 HRESULT hr = HRESULTFromLastError(); | |
356 SETUP_LOG(LE, (_T("[Query SERVICE_CONFIG_DELAYED_AUTO_START_INFO failed]") | |
357 _T("[0x%x]"), hr)); | |
358 return false; | |
359 } | |
360 | |
361 return !!service_config->fDelayedAutostart; | |
362 } | |
363 | |
364 static HRESULT SetDelayedAutoStart() { | |
365 if (!SystemInfo::IsRunningOnVistaOrLater()) { | |
366 return S_OK; | |
367 } | |
368 | |
369 ASSERT1(IsServiceInstalled()); | |
370 | |
371 if (IsDelayedAutoStart()) { | |
372 return S_OK; | |
373 } | |
374 | |
375 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
376 if (!scm) { | |
377 HRESULT hr = HRESULTFromLastError(); | |
378 SETUP_LOG(LE, (_T("[OpenSCManager failed][0x%x]"), hr)); | |
379 return hr; | |
380 } | |
381 | |
382 CString service_name(T::GetCurrentServiceName()); | |
383 scoped_service service(::OpenService(get(scm), | |
384 service_name, | |
385 SERVICE_CHANGE_CONFIG)); | |
386 if (!service) { | |
387 HRESULT hr = HRESULTFromLastError(); | |
388 SETUP_LOG(LE, (_T("[OpenService failed][0x%x]"), hr)); | |
389 return hr; | |
390 } | |
391 | |
392 SERVICE_DELAYED_AUTO_START_INFO auto_start_info = {TRUE}; | |
393 if (!::ChangeServiceConfig2(get(service), | |
394 SERVICE_CONFIG_DELAYED_AUTO_START_INFO, | |
395 &auto_start_info)) { | |
396 HRESULT hr = HRESULTFromLastError(); | |
397 SETUP_LOG(LE, (_T("[ChangeServiceConfig2 failed][0x%x]"), hr)); | |
398 return hr; | |
399 } | |
400 | |
401 SETUP_LOG(L3, (_T("[SetDelayedAutoStart succeeded]"))); | |
402 return S_OK; | |
403 } | |
404 | |
405 static HRESULT DeleteService() { | |
406 SETUP_LOG(L3, (_T("[DeleteService][%s]"), T::GetCurrentServiceName())); | |
407 | |
408 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
409 if (!scm) { | |
410 return HRESULTFromLastError(); | |
411 } | |
412 scoped_service service(::OpenService(get(scm), | |
413 T::GetCurrentServiceName(), | |
414 SERVICE_CHANGE_CONFIG | DELETE)); | |
415 if (!service) { | |
416 return HRESULTFromLastError(); | |
417 } | |
418 | |
419 if (!::DeleteService(get(service))) { | |
420 HRESULT hr = HRESULTFromLastError(); | |
421 SETUP_LOG(LW, (_T("[DeleteService failed][0x%x]"), hr)); | |
422 if (hr != HRESULT_FROM_WIN32(ERROR_SERVICE_MARKED_FOR_DELETE)) { | |
423 return hr; | |
424 } | |
425 } | |
426 | |
427 return S_OK; | |
428 } | |
429 | |
430 static bool DoesDescriptionMatch(const TCHAR* description) { | |
431 ASSERT1(description); | |
432 | |
433 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT)); | |
434 if (!scm) { | |
435 HRESULT hr = HRESULTFromLastError(); | |
436 SETUP_LOG(LE, (_T("[SC_MANAGER_CONNECT failed][0x%x]"), hr)); | |
437 return false; | |
438 } | |
439 | |
440 CString name(T::GetCurrentServiceName()); | |
441 scoped_service service(::OpenService(get(scm), name, SERVICE_QUERY_CONFIG)); | |
442 if (!service) { | |
443 HRESULT hr = HRESULTFromLastError(); | |
444 SETUP_LOG(LE, (_T("[SERVICE_QUERY_CONFIG failed][%s][0x%x]"), name, hr)); | |
445 return false; | |
446 } | |
447 | |
448 uint8 buffer[kMaxQueryConfigBufferBytes] = { 0 }; | |
449 DWORD bytes_needed_ignored = 0; | |
450 SERVICE_DESCRIPTION* service_config = | |
451 reinterpret_cast<SERVICE_DESCRIPTION*>(buffer); | |
452 if (!::QueryServiceConfig2(get(service), | |
453 SERVICE_CONFIG_DESCRIPTION, | |
454 buffer, | |
455 sizeof(buffer), | |
456 &bytes_needed_ignored)) { | |
457 HRESULT hr = HRESULTFromLastError(); | |
458 SETUP_LOG(LE, (_T("[QuerySERVICE_CONFIG_DESCRIPTION failed][0x%x]"), hr)); | |
459 return false; | |
460 } | |
461 | |
462 if (service_config->lpDescription == NULL || description == NULL) { | |
463 return (service_config->lpDescription == description); | |
464 } | |
465 | |
466 return !_tcsicmp(service_config->lpDescription, description); | |
467 } | |
468 | |
469 static HRESULT SetDescription(const TCHAR* description) { | |
470 ASSERT1(description); | |
471 | |
472 if (DoesDescriptionMatch(description)) { | |
473 return S_OK; | |
474 } | |
475 | |
476 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)); | |
477 if (!scm) { | |
478 return HRESULTFromLastError(); | |
479 } | |
480 | |
481 CString name(T::GetCurrentServiceName()); | |
482 | |
483 // Opening the service with less rights fails the ChangeServiceConfig2 call | |
484 // with E_ACCESSDENIED. | |
485 scoped_service service(::OpenService(get(scm), name, SERVICE_ALL_ACCESS)); | |
486 if (!service) { | |
487 return HRESULTFromLastError(); | |
488 } | |
489 SERVICE_DESCRIPTION info = { const_cast<TCHAR*>(description) }; | |
490 if (!::ChangeServiceConfig2(get(service), | |
491 SERVICE_CONFIG_DESCRIPTION, | |
492 &info)) { | |
493 return HRESULTFromLastError(); | |
494 } | |
495 SETUP_LOG(L3, (_T("[service description changed successfully]"))); | |
496 return S_OK; | |
497 } | |
498 | |
499 static CString GetCurrentServiceDisplayName() { | |
500 CORE_LOG(L3, (_T("[GetCurrentServiceDisplayName]"))); | |
501 | |
502 CString product_name; | |
503 VERIFY1(product_name.LoadString(IDS_PRODUCT_DISPLAY_NAME)); | |
504 | |
505 CString display_name; | |
506 display_name.FormatMessage(IDS_SERVICE_DISPLAY_NAME, product_name); | |
507 display_name.AppendFormat(_T(" (%s)"), T::GetCurrentServiceName()); | |
508 return display_name; | |
509 } | |
510 | |
511 static CString GetServiceDescription() { | |
512 // TODO(omaha3): Do we need a different service description for the medium | |
513 // service? | |
514 | |
515 CString company_name; | |
516 VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME)); | |
517 | |
518 CString service_description; | |
519 service_description.FormatMessage(IDS_SERVICE_DESCRIPTION, company_name); | |
520 return service_description; | |
521 } | |
522 | |
523 static HRESULT CreateAndSetVersionedServiceNameInRegistry() { | |
524 CORE_LOG(L3, (_T("CreateAndSetVersionedServiceNameInRegistry"))); | |
525 return goopdate_utils::CreateAndSetVersionedNameInRegistry(true, | |
526 T::default_name(), T::reg_name()); | |
527 } | |
528 | |
529 friend class SetupServiceTest; | |
530 friend class CoreUtilsTest; | |
531 | |
532 DISALLOW_IMPLICIT_CONSTRUCTORS(SetupService); | |
533 }; | |
534 | |
535 typedef SetupService<Update3ServiceMode> SetupUpdate3Service; | |
536 typedef SetupService<UpdateMediumServiceMode> SetupUpdateMediumService; | |
537 | |
538 } // namespace omaha | |
539 | |
540 #endif // OMAHA_SETUP_SETUP_SERVICE_H_ | |
OLD | NEW |