Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(134)

Side by Side Diff: base/service_utils.cc

Issue 624713003: Keep only base/extractor.[cc|h]. (Closed) Base URL: https://chromium.googlesource.com/external/omaha.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/service_utils.h ('k') | base/service_utils_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2007-2009 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 //
17 // Service-related utilities.
18 //
19
20 #include "omaha/base/service_utils.h"
21
22 #include <windows.h>
23 #include "omaha/base/constants.h"
24 #include "omaha/base/debug.h"
25 #include "omaha/base/error.h"
26 #include "omaha/base/reg_key.h"
27 #include "omaha/base/smart_handle.h"
28 #include "omaha/base/string.h"
29 #include "omaha/base/timer.h"
30 #include "omaha/base/utils.h"
31
32 namespace omaha {
33
34 HRESULT ScmDatabase::EnumerateServices(
35 ScmDatabase::EnumerateServicesCallback callback,
36 void* callback_context) {
37 ASSERT1(callback);
38 if (!callback)
39 return E_POINTER;
40
41 const wchar_t* kServicesRegKeyFromRoot =
42 L"SYSTEM\\CurrentControlSet\\Services";
43
44 HRESULT hr = E_FAIL;
45
46 RegKey services_key;
47 if (FAILED(hr = services_key.Open(HKEY_LOCAL_MACHINE,
48 kServicesRegKeyFromRoot,
49 KEY_ENUMERATE_SUB_KEYS))) {
50 ASSERT1(false);
51 REPORT(false, R_ERROR, (L"Couldn't open services subkey, hr=0x%x", hr),
52 9834572);
53 return hr;
54 }
55
56 CString service_name;
57 int key_index = 0;
58 while (SUCCEEDED(hr = services_key.GetSubkeyNameAt(key_index++,
59 &service_name))) {
60 hr = callback(callback_context, service_name);
61 if (FAILED(hr) || hr == S_FALSE) {
62 // Callback asked to terminate enumeration.
63 return hr;
64 }
65 }
66
67 if (hr != HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
68 ASSERT1(false);
69 REPORT(false, R_ERROR, (L"Failed enumerating service subkeys: 0x%x", hr),
70 1499372);
71 return hr;
72 }
73
74 return S_OK;
75 }
76
77 bool ScmDatabase::IsServiceStateEqual(SC_HANDLE service, DWORD state) {
78 ASSERT1(service);
79
80 DWORD bytes_needed_ignored = 0;
81 byte buffer[8 * 1024] = { 0 };
82 QUERY_SERVICE_CONFIG* service_config =
83 reinterpret_cast<QUERY_SERVICE_CONFIG*>(buffer);
84 if (!::QueryServiceConfig(service, service_config, sizeof(buffer),
85 &bytes_needed_ignored)) {
86 ASSERT(false, (L"Failed to query service config, perhaps handle is missing "
87 L"SERVICE_QUERY_CONFIG rights?"));
88 return false;
89 }
90
91 return (service_config[0].dwStartType == state);
92 }
93
94 bool ScmDatabase::IsServiceMarkedDeleted(SC_HANDLE service) {
95 ASSERT1(service);
96
97 // Services that have been marked deleted are always in the
98 // SERVICE_DISABLED state. The converse is not true, and unfortunately
99 // there is no way to check if a service has been marked deleted except by
100 // attempting to change one of its configuration parameters, at which
101 // point you get a specific error indicating it has been marked deleted.
102 //
103 // The following call to ChangeServiceConfig does not actually change any
104 // of the service's configuration, but should hopefully return the
105 // specific error if the service has been marked deleted.
106 if (!::ChangeServiceConfig(service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
107 SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL,
108 NULL, NULL, NULL) &&
109 ::GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) {
110 ASSERT1(IsServiceStateEqual(service, SERVICE_DISABLED));
111 return true;
112 } else {
113 return false;
114 }
115 }
116
117 HRESULT ServiceInstall::UninstallByPrefix(void* context,
118 const wchar_t* service_name) {
119 ASSERT1(context != NULL);
120 if (!context)
121 return E_POINTER;
122
123 UninstallByPrefixParams* params =
124 reinterpret_cast<UninstallByPrefixParams*>(context);
125
126 if (String_StartsWith(service_name, params->prefix, true) &&
127 lstrcmpiW(service_name, params->unless_matches) != 0) {
128 // The service must be stopped before attempting to remove it from the
129 // database. Otherwise, the SCM database remains dirty and all service
130 // functions return ERROR_SERVICE_MARKED_FOR_DELETE until the system is
131 // restarted.
132 StopService(service_name);
133
134 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
135 if (!scm) {
136 HRESULT hr = HRESULTFromLastError();
137 ASSERT1(false);
138 REPORT(false, R_ERROR, (L"Failed to open SCM: 0x%x", hr), 77223399);
139 return hr;
140 }
141 scoped_service service(::OpenService(get(scm),
142 service_name,
143 SERVICE_CHANGE_CONFIG | DELETE));
144 if (service) {
145 // The service may not get deleted immediately; if there are handles to
146 // it open, it won't get deleted until the last one is closed. If the
147 // service is running, it won't get deleted immediately but rather will be
148 // marked for deletion (which happens on next reboot). Having to wait for
149 // a while and even until reboot doesn't matter much to us as our new
150 // service is installed under a new name and we are just cleaning up old
151 // ones.
152 if (!::DeleteService(get(service))) {
153 // We do not assert but just report so that we know if this happens
154 // abnormally often.
155 if (::GetLastError() == ERROR_SERVICE_MARKED_FOR_DELETE) {
156 REPORT(false, R_INFO,
157 (L"Failed to immediately delete service %s", service_name),
158 5440098);
159 } else {
160 ASSERT(false, (L"Failed to delete service %s, error %d",
161 service_name, ::GetLastError()));
162 }
163 // DO NOT return an error here; we want to keep going through all the
164 // services.
165 } else {
166 SERVICE_LOG(L1,
167 (L"Deleted old service %s", service_name));
168 }
169 } else {
170 // Per documentation of the EnumerateServicesCallback interface we can
171 // expect not to be able to open the service with one of the following two
172 // error codes, because of discrepancies between the registry and the SCM
173 // database in memory.
174 DWORD last_error = ::GetLastError();
175 ASSERT(last_error == ERROR_SERVICE_DOES_NOT_EXIST ||
176 last_error == ERROR_INVALID_NAME,
177 (L"Failed to open service %s, last error %d", service_name,
178 last_error));
179 REPORT(last_error == ERROR_SERVICE_DOES_NOT_EXIST ||
180 last_error == ERROR_INVALID_NAME, R_ERROR,
181 (L"Failed to open service %s, last error %d", service_name,
182 last_error), 5576234);
183 }
184 }
185
186 return S_OK;
187 }
188
189 CString ServiceInstall::GenerateServiceName(const TCHAR* service_prefix) {
190 FILETIME ft = {0};
191 ::GetSystemTimeAsFileTime(&ft);
192 CString versioned_service_name;
193 versioned_service_name.Format(_T("%s%x%x"),
194 service_prefix,
195 ft.dwHighDateTime,
196 ft.dwLowDateTime);
197
198 ASSERT1(!versioned_service_name.IsEmpty());
199 return versioned_service_name;
200 }
201
202 HRESULT ServiceInstall::UninstallServices(const TCHAR* service_prefix,
203 const TCHAR* exclude_service) {
204 SERVICE_LOG(L2, (L"ServiceInstall::UninstallServices"));
205
206 UninstallByPrefixParams params = {
207 service_prefix,
208 exclude_service,
209 };
210
211 return ScmDatabase::EnumerateServices(UninstallByPrefix, &params);
212 }
213
214 bool ServiceInstall::CanInstallWithoutReboot() {
215 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
216 if (!scm) {
217 ASSERT1(false);
218 REPORT(false, R_ERROR, (L"Failed to open SCM: %d", ::GetLastError()),
219 77224449);
220 return false; // request reboot just in case
221 }
222
223 scoped_service service(::OpenService(get(scm),
224 _T("gupdate"),
225 SERVICE_QUERY_CONFIG |
226 SERVICE_CHANGE_CONFIG));
227 if (!service) {
228 DWORD last_error = ::GetLastError();
229 if (last_error == ERROR_ACCESS_DENIED ||
230 last_error == ERROR_INVALID_HANDLE) {
231 // unable to verify the service is fully deleted, so request reboot
232 ASSERT(false, (L"Expected access and correct handle"));
233 return false;
234 } else {
235 // service does not exist
236 return true;
237 }
238 }
239
240 return !ScmDatabase::IsServiceMarkedDeleted(get(service));
241 }
242
243 HRESULT ServiceInstall::StopService(const CString& service_name) {
244 SERVICE_LOG(L1, (_T("[ServiceInstall::StopService]")));
245
246 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS));
247 if (!scm) {
248 return HRESULTFromLastError();
249 }
250 scoped_service service(::OpenService(get(scm),
251 service_name,
252 SERVICE_QUERY_STATUS | SERVICE_STOP));
253 if (!service) {
254 return HRESULTFromLastError();
255 }
256
257 SERVICE_STATUS status = {0};
258 if (::QueryServiceStatus(get(service), &status)) {
259 if (status.dwCurrentState != SERVICE_STOPPED &&
260 status.dwCurrentState != SERVICE_STOP_PENDING) {
261 // Stop the service.
262 SetZero(status);
263 if (!::ControlService(get(service), SERVICE_CONTROL_STOP, &status)) {
264 return HRESULTFromLastError();
265 }
266 }
267 }
268
269 if (status.dwCurrentState != SERVICE_STOPPED) {
270 SERVICE_LOG(L1, (_T("[Service is stopping...]")));
271
272 const int kWaitForServiceToStopMs = 8000;
273 LowResTimer t(true);
274
275 while (status.dwCurrentState != SERVICE_STOPPED &&
276 t.GetMilliseconds() < kWaitForServiceToStopMs) {
277 const int kSleepTimeMs = 50;
278 ::Sleep(kSleepTimeMs);
279 SetZero(status);
280 VERIFY1(::QueryServiceStatus(get(service), &status));
281 SERVICE_LOG(L1, (_T("[Waiting for service to stop][time elapsed: %d ms]"),
282 static_cast<int>(t.GetMilliseconds())));
283 }
284
285 if (status.dwCurrentState != SERVICE_STOPPED) {
286 SERVICE_LOG(LEVEL_WARNING, (_T("[Service did not stop! Not good...]")));
287 return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
288 }
289 }
290
291 ASSERT1(status.dwCurrentState == SERVICE_STOPPED);
292 SERVICE_LOG(L1, (_T("[ServiceInstall::StopService - service stopped]")));
293 return S_OK;
294 }
295
296 bool ServiceInstall::IsServiceInstalled(const TCHAR* service_name) {
297 ASSERT1(service_name);
298 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
299 if (!scm) {
300 return false;
301 }
302 scoped_service service(::OpenService(get(scm),
303 service_name,
304 SERVICE_QUERY_CONFIG));
305 return valid(service);
306 }
307
308 // TODO(Omaha): Move all functions under a common ServiceUtils namespace.
309 bool ServiceUtils::IsServiceRunning(const TCHAR* service_name) {
310 ASSERT1(service_name);
311
312 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
313 if (!scm) {
314 SERVICE_LOG(LE, (_T("[OpenSCManager fail][0x%x]"), HRESULTFromLastError()));
315 return false;
316 }
317
318 scoped_service service(::OpenService(get(scm),
319 service_name,
320 SERVICE_QUERY_STATUS));
321 if (!service) {
322 SERVICE_LOG(LE, (_T("[OpenService failed][%s][0x%x]"),
323 service_name, HRESULTFromLastError()));
324 return false;
325 }
326
327 SERVICE_STATUS status = {0};
328 if (!::QueryServiceStatus(get(service), &status)) {
329 SERVICE_LOG(LE, (_T("[QueryServiceStatus failed][%s][0x%x]"),
330 service_name, HRESULTFromLastError()));
331 return false;
332 }
333
334 return status.dwCurrentState == SERVICE_RUNNING ||
335 status.dwCurrentState == SERVICE_START_PENDING;
336 }
337
338 bool ServiceUtils::IsServiceDisabled(const TCHAR* service_name) {
339 ASSERT1(service_name);
340
341 scoped_service scm(::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT));
342 if (!scm) {
343 SERVICE_LOG(LE, (_T("[OpenSCManager fail][0x%x]"), HRESULTFromLastError()));
344 return false;
345 }
346
347 scoped_service service(::OpenService(get(scm),
348 service_name,
349 SERVICE_QUERY_CONFIG));
350 if (!service) {
351 SERVICE_LOG(LE, (_T("[OpenService failed][%s][0x%x]"),
352 service_name, HRESULTFromLastError()));
353 return false;
354 }
355
356 return ScmDatabase::IsServiceStateEqual(get(service), SERVICE_DISABLED);
357 }
358
359 } // namespace omaha
360
OLDNEW
« no previous file with comments | « base/service_utils.h ('k') | base/service_utils_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698