OLD | NEW |
| (Empty) |
1 // Copyright 2006-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 // The service is a bootstrap for a local system process to start | |
17 // when the computer starts. The service shuts itself down in 30 seconds. | |
18 // | |
19 // The service can be started in one of two modes: | |
20 // * As a regular service, typically at system startup. | |
21 // * As a COM service, typically by an Omaha client using IGoogleUpdateCore. | |
22 // The COM case is distinguished from the regular service case by registering a | |
23 // ServiceParameters command line in the AppID registration. | |
24 // | |
25 // In all cases, the service initializes COM, and allows for IGoogleUpdateCore | |
26 // clients to connect to the service. In the regular service case, the service | |
27 // shuts down after a small idle check timeout, provided that there are no COM | |
28 // clients connected. In the COM server case, and in the case where there are | |
29 // COM clients connected in the regular service case, the service will shut down | |
30 // when the last client disconnects. | |
31 // | |
32 // To be exact, the service will initiate shutdown in all cases when the ATL | |
33 // module count drops to zero. | |
34 // | |
35 // ATL does not allow for directly reusing the delayed COM shutdown mechanism | |
36 // available for Local servers. The assumption likely being that services run | |
37 // forever. Since we do not want our service to run forever, we override some | |
38 // of the functions to get the same effect. | |
39 | |
40 #ifndef OMAHA_SERVICE_SERVICE_MAIN_H_ | |
41 #define OMAHA_SERVICE_SERVICE_MAIN_H_ | |
42 | |
43 #include <atlbase.h> | |
44 #include <atlcom.h> | |
45 #include "base/basictypes.h" | |
46 #include "base/scoped_ptr.h" | |
47 #include "omaha/base/atlregmapex.h" | |
48 #include "omaha/base/debug.h" | |
49 // Use client/resource.h because it does not use StringFormatter and some of the | |
50 // strings are only used during Setup, which is part of the client. | |
51 // TODO(omaha3): It is a little unexpected to access strings in a header. It | |
52 // would be nice to avoid that. Also, this file is included by both client | |
53 // (setup_service.cc) and server (goopdate.cc) code. | |
54 #include "omaha/client/resource.h" | |
55 #include "omaha/common/command_line_builder.h" | |
56 #include "omaha/common/const_cmd_line.h" | |
57 #include "omaha/common/const_goopdate.h" | |
58 #include "omaha/common/goopdate_utils.h" | |
59 #include "omaha/core/google_update_core.h" | |
60 #include "omaha/goopdate/google_update3.h" | |
61 #include "omaha/goopdate/non_localized_resource.h" | |
62 #include "omaha/goopdate/ondemand.h" | |
63 #include "omaha/goopdate/update3web.h" | |
64 #include "omaha/goopdate/worker.h" | |
65 #include "omaha/net/network_config.h" | |
66 | |
67 namespace omaha { | |
68 | |
69 class Update3ServiceMode { | |
70 public: | |
71 static CommandLineMode commandline_mode(); | |
72 static CString reg_name(); | |
73 static CString default_name(); | |
74 static DWORD service_start_type(); | |
75 static _ATL_OBJMAP_ENTRY* object_map(); | |
76 static bool allow_access_from_medium(); | |
77 static CString app_id_string(); | |
78 static CString GetCurrentServiceName(); | |
79 static HRESULT PreMessageLoop(); | |
80 }; | |
81 | |
82 class UpdateMediumServiceMode { | |
83 public: | |
84 static CommandLineMode commandline_mode(); | |
85 static CString reg_name(); | |
86 static CString default_name(); | |
87 static DWORD service_start_type(); | |
88 static _ATL_OBJMAP_ENTRY* object_map(); | |
89 static bool allow_access_from_medium(); | |
90 static CString app_id_string(); | |
91 static CString GetCurrentServiceName(); | |
92 static HRESULT PreMessageLoop(); | |
93 }; | |
94 | |
95 #pragma warning(push) | |
96 // C4640: construction of local static object is not thread-safe | |
97 #pragma warning(disable : 4640) | |
98 | |
99 template <typename T> | |
100 class ServiceModule | |
101 : public CAtlServiceModuleT<ServiceModule<T>, IDS_SERVICE_NAME> { | |
102 public: | |
103 typedef CAtlServiceModuleT<ServiceModule, IDS_SERVICE_NAME> Base; | |
104 | |
105 DECLARE_REGISTRY_APPID_RESOURCEID_EX(IDR_GOOGLE_UPDATE3_SERVICE_APPID, | |
106 T::app_id_string()) | |
107 | |
108 BEGIN_REGISTRY_MAP() | |
109 REGMAP_ENTRY(_T("DESCRIPTION"), _T("ServiceModule")) | |
110 REGMAP_ENTRY(_T("FILENAME"), kServiceFileName) | |
111 END_REGISTRY_MAP() | |
112 | |
113 ServiceModule() | |
114 : service_thread_(NULL), | |
115 is_service_com_server_(false) { | |
116 SERVICE_LOG(L1, (_T("[ServiceModule]"))); | |
117 _tcscpy(m_szServiceName, T::GetCurrentServiceName()); | |
118 } | |
119 | |
120 ~ServiceModule() { | |
121 SERVICE_LOG(L1, (_T("[~ServiceModule]"))); | |
122 ASSERT1(!service_thread_); | |
123 | |
124 // ServiceModule is typically created on the stack. We cannot reset the | |
125 // _pAtlModule here, because objects are destroyed at destructor unwind | |
126 // time, and require access to the module. | |
127 } | |
128 | |
129 HRESULT InitializeSecurity() throw() { | |
130 SERVICE_LOG(L3, (_T("[InitializeSecurity]"))); | |
131 | |
132 return InitializeServerSecurity(T::allow_access_from_medium()); | |
133 } | |
134 | |
135 void ServiceMain(DWORD argc, LPTSTR* argv) throw() { | |
136 ASSERT1(argc <= 2); | |
137 is_service_com_server_ = | |
138 argc == 2 && !CString(argv[1]).CompareNoCase(kCmdLineServiceComServer); | |
139 SERVICE_LOG(L3, (_T("[ServiceMain][is_service_com_server_][%d]"), | |
140 is_service_com_server_)); | |
141 Base::ServiceMain(argc, argv); | |
142 } | |
143 | |
144 HRESULT RegisterClassObjects(DWORD class_context, | |
145 DWORD flags) throw() { | |
146 SERVICE_LOG(L3, (_T("[RegisterClassObjects]"))); | |
147 for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map(); | |
148 objmap_entry->pclsid != NULL; | |
149 objmap_entry++) { | |
150 HRESULT hr = objmap_entry->RegisterClassObject(class_context, flags); | |
151 if (FAILED(hr)) { | |
152 SERVICE_LOG(LE, (_T("[RegisterClassObject failed][%s][0x%x]"), | |
153 GuidToString(*objmap_entry->pclsid), hr)); | |
154 return hr; | |
155 } | |
156 } | |
157 | |
158 return S_OK; | |
159 } | |
160 | |
161 HRESULT RevokeClassObjects() throw() { | |
162 SERVICE_LOG(L3, (_T("[RevokeClassObjects]"))); | |
163 for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map(); | |
164 objmap_entry->pclsid != NULL; | |
165 objmap_entry++) { | |
166 HRESULT hr = objmap_entry->RevokeClassObject(); | |
167 if (FAILED(hr)) { | |
168 SERVICE_LOG(LE, (_T("[RevokeClassObject failed][%s][0x%x]"), | |
169 GuidToString(*objmap_entry->pclsid), hr)); | |
170 return hr; | |
171 } | |
172 } | |
173 | |
174 return S_OK; | |
175 } | |
176 | |
177 HRESULT RegisterServer(BOOL, const CLSID* = NULL) throw() { | |
178 SERVICE_LOG(L3, (_T("[RegisterServer]"))); | |
179 for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map(); | |
180 objmap_entry->pclsid != NULL; | |
181 objmap_entry++) { | |
182 HRESULT hr = objmap_entry->pfnUpdateRegistry(TRUE); | |
183 if (FAILED(hr)) { | |
184 SERVICE_LOG(LE, (_T("[pfnUpdateRegistry failed][%s][0x%x]"), | |
185 GuidToString(*objmap_entry->pclsid), hr)); | |
186 return hr; | |
187 } | |
188 | |
189 hr = AtlRegisterClassCategoriesHelper(*objmap_entry->pclsid, | |
190 objmap_entry->pfnGetCategoryMap(), | |
191 TRUE); | |
192 if (FAILED(hr)) { | |
193 SERVICE_LOG(LE, (_T("[RegisterServer fail][%s][0x%x]"), | |
194 GuidToString(*objmap_entry->pclsid), hr)); | |
195 return hr; | |
196 } | |
197 } | |
198 | |
199 return S_OK; | |
200 } | |
201 | |
202 HRESULT UnregisterServer(BOOL, const CLSID* = NULL) throw() { | |
203 SERVICE_LOG(L3, (_T("[UnregisterServer]"))); | |
204 for (_ATL_OBJMAP_ENTRY* objmap_entry = T::object_map(); | |
205 objmap_entry->pclsid != NULL; | |
206 objmap_entry++) { | |
207 HRESULT hr = AtlRegisterClassCategoriesHelper(*objmap_entry->pclsid, | |
208 objmap_entry->pfnGetCategoryMap(), | |
209 FALSE); | |
210 if (FAILED(hr)) { | |
211 SERVICE_LOG(LE, (_T("[RegisterServer fail][%s][0x%x]"), | |
212 GuidToString(*objmap_entry->pclsid), hr)); | |
213 return hr; | |
214 } | |
215 | |
216 hr = objmap_entry->pfnUpdateRegistry(FALSE); | |
217 if (FAILED(hr)) { | |
218 SERVICE_LOG(LE, (_T("[pfnUpdateRegistry failed][%s][0x%x]"), | |
219 GuidToString(*objmap_entry->pclsid), hr)); | |
220 return hr; | |
221 } | |
222 } | |
223 | |
224 return S_OK; | |
225 } | |
226 | |
227 HRESULT RegisterCOMService() { | |
228 SERVICE_LOG(L3, (_T("[RegisterCOMService]"))); | |
229 HRESULT hr = UpdateRegistryAppId(TRUE); | |
230 if (FAILED(hr)) { | |
231 SERVICE_LOG(LE, (_T("[UpdateRegistryAppId failed][0x%x]"), hr)); | |
232 return hr; | |
233 } | |
234 | |
235 RegKey key_app_id; | |
236 hr = key_app_id.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE); | |
237 if (FAILED(hr)) { | |
238 SERVICE_LOG(LE, (_T("[Could not open HKLM\\AppID][0x%x]"), hr)); | |
239 return hr; | |
240 } | |
241 | |
242 RegKey key; | |
243 hr = key.Create(key_app_id.Key(), GetAppIdT()); | |
244 if (FAILED(hr)) { | |
245 SERVICE_LOG(LE, (_T("[Fail open HKLM-AppID-%s][0x%x]"), GetAppId(), hr)); | |
246 return hr; | |
247 } | |
248 | |
249 // m_szServiceName is set in the constructor. | |
250 hr = key.SetValue(_T("LocalService"), m_szServiceName); | |
251 if (FAILED(hr)) { | |
252 SERVICE_LOG(LE, (_T("[Could not set LocalService value][0x%x]"), hr)); | |
253 return hr; | |
254 } | |
255 | |
256 // The SCM will pass this switch to ServiceMain() during COM activation. | |
257 hr = key.SetValue(_T("ServiceParameters"), kCmdLineServiceComServer); | |
258 if (FAILED(hr)) { | |
259 SERVICE_LOG(LE, (_T("[Set ServiceParameters value failed][0x%x]"), hr)); | |
260 return hr; | |
261 } | |
262 | |
263 return RegisterServer(FALSE); | |
264 } | |
265 | |
266 HRESULT UnregisterCOMService() { | |
267 SERVICE_LOG(L3, (_T("[UnregisterCOMService]"))); | |
268 HRESULT hr = UnregisterServer(FALSE); | |
269 if (FAILED(hr)) { | |
270 SERVICE_LOG(LE, (_T("[UnregisterServer failed][0x%x]"), hr)); | |
271 return hr; | |
272 } | |
273 | |
274 return UpdateRegistryAppId(FALSE); | |
275 } | |
276 | |
277 HRESULT PreMessageLoop(int show_cmd) { | |
278 UNREFERENCED_PARAMETER(show_cmd); | |
279 | |
280 SERVICE_LOG(L1, (_T("[PreMessageLoop]"))); | |
281 | |
282 m_dwThreadID = ::GetCurrentThreadId(); | |
283 service_thread_ = ::OpenThread(SYNCHRONIZE, false, m_dwThreadID); | |
284 | |
285 if (is_service_com_server_) { | |
286 return InitializeCOMServer(); | |
287 } | |
288 | |
289 // This is the regular service case. Call T::PreMessageLoop() and exit. | |
290 SetServiceStatus(SERVICE_RUNNING); | |
291 | |
292 HRESULT hr = T::PreMessageLoop(); | |
293 if (FAILED(hr)) { | |
294 SERVICE_LOG(LE, (_T("[T::PreMessageLoop() failed][0x%x]"), hr)); | |
295 } | |
296 | |
297 SetServiceStatus(SERVICE_STOP_PENDING); | |
298 | |
299 // S_FALSE is returned to exit the service immediately. | |
300 return S_FALSE; | |
301 } | |
302 | |
303 HRESULT PostMessageLoop() { | |
304 SERVICE_LOG(L1, (_T("[PostMessageLoop]"))); | |
305 | |
306 if (!is_service_com_server_) { | |
307 return S_OK; | |
308 } | |
309 | |
310 return Base::PostMessageLoop(); | |
311 } | |
312 | |
313 int Main(int show_cmd) { | |
314 if (CAtlBaseModule::m_bInitFailed) { | |
315 SERVICE_LOG(LE, (_T("[CAtlBaseModule init failed]"))); | |
316 return -1; | |
317 } | |
318 | |
319 return static_cast<int>(Start(show_cmd)); | |
320 } | |
321 | |
322 // This is cloned from CAtlExeModuleT.Lock(). The one difference is the call | |
323 // to ::CoAddRefServerProcess(). See the description for Unlock() below for | |
324 // further information. | |
325 virtual LONG Lock() throw() { | |
326 ::CoAddRefServerProcess(); | |
327 LONG retval = CComGlobalsThreadModel::Increment(&m_nLockCnt); | |
328 SERVICE_LOG(L3, (_T("[ServiceModule::Lock][%d]"), retval)); | |
329 return retval; | |
330 } | |
331 | |
332 // This is cloned from CAtlExeModuleT.Unlock(). The differences are: | |
333 // | |
334 // * the call to ::CoReleaseServerProcess(), to ensure that the class | |
335 // factories are suspended once the lock count drops to zero. This fixes a | |
336 // a race condition where an activation request could come in in the middle | |
337 // of shutting down. This shutdown mechanism works with free threaded servers. | |
338 // | |
339 // There are race issues with the ATL delayed shutdown mechanism, hence the | |
340 // associated code has been eliminated, and we have an assert to make sure | |
341 // m_bDelayShutdown is not set. | |
342 // | |
343 // * the call to "OnStop()" instead of the "::PostThreadMessage(m_dwMainThre". | |
344 // OnStop() correctly sets the service status to SERVICE_STOP_PENDING, and | |
345 // posts a WM_QUIT to the service thread. | |
346 virtual LONG Unlock() throw() { | |
347 ASSERT1(!m_bDelayShutdown); | |
348 | |
349 ::CoReleaseServerProcess(); | |
350 LONG retval = CComGlobalsThreadModel::Decrement(&m_nLockCnt); | |
351 SERVICE_LOG(L3, (_T("[ServiceModule::Unlock][%d]"), retval)); | |
352 | |
353 if (retval == 0) { | |
354 OnStop(); | |
355 } | |
356 | |
357 return retval; | |
358 } | |
359 | |
360 // This is cloned from CAtlExeModuleT.MonitorShutdown(). The only difference | |
361 // is the call to "OnStop()" instead of the | |
362 // "::PostThreadMessage(m_dwMainThreadID". | |
363 void MonitorShutdown() throw() { | |
364 SERVICE_LOG(L3, (_T("[MonitorShutdown]"))); | |
365 | |
366 while (true) { | |
367 ::WaitForSingleObject(m_hEventShutdown, INFINITE); | |
368 SERVICE_LOG(L4, (_T("[Infinite Wait][%d][%d]"), m_bActivity, m_nLockCnt)); | |
369 | |
370 DWORD wait = 0; | |
371 do { | |
372 m_bActivity = false; | |
373 wait = ::WaitForSingleObject(m_hEventShutdown, m_dwTimeOut); | |
374 } while (wait == WAIT_OBJECT_0); | |
375 | |
376 SERVICE_LOG(L4, (_T("[MonitorShutdown][%d][%d]"), | |
377 m_bActivity, m_nLockCnt)); | |
378 if (!m_bActivity && m_nLockCnt == 0) { | |
379 ::CoSuspendClassObjects(); | |
380 if (m_nLockCnt == 0) { | |
381 break; | |
382 } | |
383 } | |
384 } | |
385 | |
386 ::CloseHandle(m_hEventShutdown); | |
387 OnStop(); | |
388 } | |
389 | |
390 // This is cloned from CAtlExeModuleT.StartMonitor(). | |
391 HANDLE StartMonitor() throw() { | |
392 SERVICE_LOG(L3, (_T("[StartMonitor]"))); | |
393 m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL); | |
394 if (m_hEventShutdown == NULL) { | |
395 return NULL; | |
396 } | |
397 | |
398 DWORD thread_id(0); | |
399 HANDLE monitor = ::CreateThread(NULL, 0, MonitorProc, this, 0, &thread_id); | |
400 if (monitor == NULL) { | |
401 ::CloseHandle(m_hEventShutdown); | |
402 } | |
403 | |
404 return monitor; | |
405 } | |
406 | |
407 // This is cloned from CAtlExeModuleT.MonitorProc(). | |
408 static DWORD WINAPI MonitorProc(void* pv) throw() { | |
409 SERVICE_LOG(L3, (_T("[MonitorProc]"))); | |
410 ServiceModule* service_module = static_cast<ServiceModule*>(pv); | |
411 ASSERT1(service_module); | |
412 | |
413 service_module->MonitorShutdown(); | |
414 return 0; | |
415 } | |
416 | |
417 private: | |
418 // Should only be called from the client, such as during Setup, since it only | |
419 // supports one language. | |
420 // Assumes the resources have been loaded. | |
421 static CString GetServiceDescription() { | |
422 CString company_name; | |
423 VERIFY1(company_name.LoadString(IDS_FRIENDLY_COMPANY_NAME)); | |
424 CString description; | |
425 description.FormatMessage(IDS_SERVICE_DESCRIPTION, company_name); | |
426 return description; | |
427 } | |
428 | |
429 HRESULT InitializeCOMServer() { | |
430 SERVICE_LOG(L1, (_T("[InitializeCOMServer]"))); | |
431 | |
432 // Initialize COM security right at the beginning, because Worker | |
433 // initialization can cause interface marshaling. The first few lines below | |
434 // are adapted from the beginning of CAtlServiceModuleT::PreMessageLoop(). | |
435 ASSERT1(m_bService); | |
436 HRESULT hr = InitializeSecurity(); | |
437 if (FAILED(hr)) { | |
438 return hr; | |
439 } | |
440 | |
441 DisableCOMExceptionHandling(); | |
442 | |
443 NetworkConfigManager::set_is_machine(true); | |
444 | |
445 // Create NetworkConfigManager singleton by referencing it. | |
446 NetworkConfigManager::Instance(); | |
447 | |
448 // Register and resume the COM class objects. We call the CAtlExeModuleT | |
449 // member instead of CAtlServiceModuleT, because the latter also tries to | |
450 // initialize security, which we have already done above. | |
451 return CAtlExeModuleT<ServiceModule>::PreMessageLoop(SW_HIDE); | |
452 } | |
453 | |
454 // When Start executes, it blocks on StartServiceCtrlDispatcher. | |
455 // Internally, the SCM creates a service thread which starts executing code | |
456 // specified by SERVICE_TABLE_ENTRY. The ATL code then calls PreMessageLoop | |
457 // and PostMessageLoop on this thread. When the service stops, the execution | |
458 // flow returns from StartServiceCtrlDispatcher and the main thread blocks | |
459 // waiting on the service thread to exit. | |
460 // Before synchronizing the main thread and the service thread, race condition | |
461 // resulted in http://b/1134747. | |
462 HRESULT Start(int show_cmd) { | |
463 SERVICE_LOG(L1, (_T("[Start]"))); | |
464 UNREFERENCED_PARAMETER(show_cmd); | |
465 | |
466 SERVICE_TABLE_ENTRY st[] = { | |
467 { m_szServiceName, Base::_ServiceMain }, | |
468 { NULL, NULL } | |
469 }; | |
470 | |
471 m_status.dwWin32ExitCode = 0; | |
472 if (!::StartServiceCtrlDispatcher(st)) { | |
473 m_status.dwWin32ExitCode = ::GetLastError(); | |
474 } | |
475 | |
476 if (service_thread_) { | |
477 DWORD result(::WaitForSingleObject(service_thread_, kShutdownIntervalMs)); | |
478 ASSERT1(result == WAIT_OBJECT_0); | |
479 ::CloseHandle(service_thread_); | |
480 service_thread_ = NULL; | |
481 } | |
482 | |
483 return m_status.dwWin32ExitCode; | |
484 } | |
485 | |
486 HANDLE service_thread_; // The service thread provided by the SCM. | |
487 bool is_service_com_server_; // True if the service is being invoked by COM. | |
488 | |
489 // Service shut down wait timeout. The main thread waits for the service | |
490 // thread to exit, after the service stops. | |
491 static const DWORD kShutdownIntervalMs = 1000L * 30; // 30 seconds. | |
492 | |
493 // Service idle check timeout. The service shuts down itself after startup. | |
494 static const DWORD kIdleCheckIntervalMs = 1000L * 30; // 30 seconds. | |
495 | |
496 // Service failover constants. | |
497 // | |
498 // Time after which the SCM resets the failure count to zero if there are | |
499 // no failures. | |
500 static const DWORD kResetPeriodSec = 60 * 60 * 24; // 1 day. | |
501 | |
502 // Time to wait before performing the specified action. | |
503 static const DWORD kActionDelayMs = 1000L * 60 * 15; // 15 minutes. | |
504 | |
505 DISALLOW_EVIL_CONSTRUCTORS(ServiceModule); | |
506 }; | |
507 | |
508 #pragma warning(pop) | |
509 | |
510 typedef ServiceModule<Update3ServiceMode> Update3ServiceModule; | |
511 typedef ServiceModule<UpdateMediumServiceMode> UpdateMediumServiceModule; | |
512 | |
513 } // namespace omaha | |
514 | |
515 #endif // OMAHA_SERVICE_SERVICE_MAIN_H_ | |
OLD | NEW |