toolkit/components/maintenanceservice/workmonitor.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:ce0b74dc3cae
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 #include <shlobj.h>
6 #include <shlwapi.h>
7 #include <wtsapi32.h>
8 #include <userenv.h>
9 #include <shellapi.h>
10
11 #pragma comment(lib, "wtsapi32.lib")
12 #pragma comment(lib, "userenv.lib")
13 #pragma comment(lib, "shlwapi.lib")
14 #pragma comment(lib, "ole32.lib")
15 #pragma comment(lib, "rpcrt4.lib")
16
17 #include "nsWindowsHelpers.h"
18
19 #include "workmonitor.h"
20 #include "serviceinstall.h"
21 #include "servicebase.h"
22 #include "registrycertificates.h"
23 #include "uachelper.h"
24 #include "updatehelper.h"
25 #include "errors.h"
26
27 // Wait 15 minutes for an update operation to run at most.
28 // Updates usually take less than a minute so this seems like a
29 // significantly large and safe amount of time to wait.
30 static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
31 char16_t* MakeCommandLine(int argc, char16_t **argv);
32 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
33 BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer, LPCWSTR siblingFilePath,
34 LPCWSTR newFileName);
35
36 /*
37 * Read the update.status file and sets isApplying to true if
38 * the status is set to applying
39 *
40 * @param updateDirPath The directory where update.status is stored
41 * @param isApplying Out parameter for specifying if the status
42 * is set to applying or not.
43 * @return TRUE if the information was filled.
44 */
45 static BOOL
46 IsStatusApplying(LPCWSTR updateDirPath, BOOL &isApplying)
47 {
48 isApplying = FALSE;
49 WCHAR updateStatusFilePath[MAX_PATH + 1] = {L'\0'};
50 wcsncpy(updateStatusFilePath, updateDirPath, MAX_PATH);
51 if (!PathAppendSafe(updateStatusFilePath, L"update.status")) {
52 LOG_WARN(("Could not append path for update.status file"));
53 return FALSE;
54 }
55
56 nsAutoHandle statusFile(CreateFileW(updateStatusFilePath, GENERIC_READ,
57 FILE_SHARE_READ |
58 FILE_SHARE_WRITE |
59 FILE_SHARE_DELETE,
60 nullptr, OPEN_EXISTING, 0, nullptr));
61
62 if (INVALID_HANDLE_VALUE == statusFile) {
63 LOG_WARN(("Could not open update.status file"));
64 return FALSE;
65 }
66
67 char buf[32] = { 0 };
68 DWORD read;
69 if (!ReadFile(statusFile, buf, sizeof(buf), &read, nullptr)) {
70 LOG_WARN(("Could not read from update.status file"));
71 return FALSE;
72 }
73
74 LOG(("updater.exe returned status: %s", buf));
75
76 const char kApplying[] = "applying";
77 isApplying = strncmp(buf, kApplying,
78 sizeof(kApplying) - 1) == 0;
79 return TRUE;
80 }
81
82 /**
83 * Determines whether we're staging an update.
84 *
85 * @param argc The argc value normally sent to updater.exe
86 * @param argv The argv value normally sent to updater.exe
87 * @param boolean True if we're staging an update
88 */
89 static bool
90 IsUpdateBeingStaged(int argc, LPWSTR *argv)
91 {
92 // PID will be set to -1 if we're supposed to stage an update.
93 return argc == 4 && !wcscmp(argv[3], L"-1");
94 }
95
96 /**
97 * Gets the installation directory from the arguments passed to updater.exe.
98 *
99 * @param argcTmp The argc value normally sent to updater.exe
100 * @param argvTmp The argv value normally sent to updater.exe
101 * @param aResultDir Buffer to hold the installation directory.
102 */
103 static BOOL
104 GetInstallationDir(int argcTmp, LPWSTR *argvTmp, WCHAR aResultDir[MAX_PATH + 1])
105 {
106 if (argcTmp < 2) {
107 return FALSE;
108 }
109 wcsncpy(aResultDir, argvTmp[2], MAX_PATH);
110 WCHAR* backSlash = wcsrchr(aResultDir, L'\\');
111 // Make sure that the path does not include trailing backslashes
112 if (backSlash && (backSlash[1] == L'\0')) {
113 *backSlash = L'\0';
114 }
115 bool backgroundUpdate = IsUpdateBeingStaged(argcTmp, argvTmp);
116 bool replaceRequest = (argcTmp >= 4 && wcsstr(argvTmp[3], L"/replace"));
117 if (backgroundUpdate || replaceRequest) {
118 return PathRemoveFileSpecW(aResultDir);
119 }
120 return TRUE;
121 }
122
123 /**
124 * Runs an update process as the service using the SYSTEM account.
125 *
126 * @param argc The number of arguments in argv
127 * @param argv The arguments normally passed to updater.exe
128 * argv[0] must be the path to updater.exe
129 * @param processStarted Set to TRUE if the process was started.
130 * @return TRUE if the update process was run had a return code of 0.
131 */
132 BOOL
133 StartUpdateProcess(int argc,
134 LPWSTR *argv,
135 LPCWSTR installDir,
136 BOOL &processStarted)
137 {
138 LOG(("Starting update process as the service in session 0."));
139 STARTUPINFO si = {0};
140 si.cb = sizeof(STARTUPINFO);
141 si.lpDesktop = L"winsta0\\Default";
142 PROCESS_INFORMATION pi = {0};
143
144 // The updater command line is of the form:
145 // updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
146 LPWSTR cmdLine = MakeCommandLine(argc, argv);
147
148 // If we're about to start the update process from session 0,
149 // then we should not show a GUI. This only really needs to be done
150 // on Vista and higher, but it's better to keep everything consistent
151 // across all OS if it's of no harm.
152 if (argc >= 2 ) {
153 // Setting the desktop to blank will ensure no GUI is displayed
154 si.lpDesktop = L"";
155 si.dwFlags |= STARTF_USESHOWWINDOW;
156 si.wShowWindow = SW_HIDE;
157 }
158
159 // We move the updater.ini file out of the way because we will handle
160 // executing PostUpdate through the service. We handle PostUpdate from
161 // the service because there are some per user things that happen that
162 // can't run in session 0 which we run updater.exe in.
163 // Once we are done running updater.exe we rename updater.ini back so
164 // that if there were any errors the next updater.exe will run correctly.
165 WCHAR updaterINI[MAX_PATH + 1];
166 WCHAR updaterINITemp[MAX_PATH + 1];
167 BOOL selfHandlePostUpdate = FALSE;
168 // We use the updater.ini from the same directory as the updater.exe
169 // because of background updates.
170 if (PathGetSiblingFilePath(updaterINI, argv[0], L"updater.ini") &&
171 PathGetSiblingFilePath(updaterINITemp, argv[0], L"updater.tmp")) {
172 selfHandlePostUpdate = MoveFileExW(updaterINI, updaterINITemp,
173 MOVEFILE_REPLACE_EXISTING);
174 }
175
176 // Add an env var for MOZ_USING_SERVICE so the updater.exe can
177 // do anything special that it needs to do for service updates.
178 // Search in updater.cpp for more info on MOZ_USING_SERVICE.
179 putenv(const_cast<char*>("MOZ_USING_SERVICE=1"));
180 LOG(("Starting service with cmdline: %ls", cmdLine));
181 processStarted = CreateProcessW(argv[0], cmdLine,
182 nullptr, nullptr, FALSE,
183 CREATE_DEFAULT_ERROR_MODE,
184 nullptr,
185 nullptr, &si, &pi);
186 // Empty value on putenv is how you remove an env variable in Windows
187 putenv(const_cast<char*>("MOZ_USING_SERVICE="));
188
189 BOOL updateWasSuccessful = FALSE;
190 if (processStarted) {
191 // Wait for the updater process to finish
192 LOG(("Process was started... waiting on result."));
193 DWORD waitRes = WaitForSingleObject(pi.hProcess, TIME_TO_WAIT_ON_UPDATER);
194 if (WAIT_TIMEOUT == waitRes) {
195 // We waited a long period of time for updater.exe and it never finished
196 // so kill it.
197 TerminateProcess(pi.hProcess, 1);
198 } else {
199 // Check the return code of updater.exe to make sure we get 0
200 DWORD returnCode;
201 if (GetExitCodeProcess(pi.hProcess, &returnCode)) {
202 LOG(("Process finished with return code %d.", returnCode));
203 // updater returns 0 if successful.
204 updateWasSuccessful = (returnCode == 0);
205 } else {
206 LOG_WARN(("Process finished but could not obtain return code."));
207 }
208 }
209 CloseHandle(pi.hProcess);
210 CloseHandle(pi.hThread);
211
212 // Check just in case updater.exe didn't change the status from
213 // applying. If this is the case we report an error.
214 BOOL isApplying = FALSE;
215 if (IsStatusApplying(argv[1], isApplying) && isApplying) {
216 if (updateWasSuccessful) {
217 LOG(("update.status is still applying even know update "
218 " was successful."));
219 if (!WriteStatusFailure(argv[1],
220 SERVICE_STILL_APPLYING_ON_SUCCESS)) {
221 LOG_WARN(("Could not write update.status still applying on"
222 " success error."));
223 }
224 // Since we still had applying we know updater.exe didn't do its
225 // job correctly.
226 updateWasSuccessful = FALSE;
227 } else {
228 LOG_WARN(("update.status is still applying and update was not successful."));
229 if (!WriteStatusFailure(argv[1],
230 SERVICE_STILL_APPLYING_ON_FAILURE)) {
231 LOG_WARN(("Could not write update.status still applying on"
232 " success error."));
233 }
234 }
235 }
236 } else {
237 DWORD lastError = GetLastError();
238 LOG_WARN(("Could not create process as current user, "
239 "updaterPath: %ls; cmdLine: %ls. (%d)",
240 argv[0], cmdLine, lastError));
241 }
242
243 // Now that we're done with the update, restore back the updater.ini file
244 // We use it ourselves, and also we want it back in case we had any type
245 // of error so that the normal update process can use it.
246 if (selfHandlePostUpdate) {
247 MoveFileExW(updaterINITemp, updaterINI, MOVEFILE_REPLACE_EXISTING);
248
249 // Only run the PostUpdate if the update was successful
250 if (updateWasSuccessful && argc > 2) {
251 LPCWSTR updateInfoDir = argv[1];
252 bool backgroundUpdate = IsUpdateBeingStaged(argc, argv);
253
254 // Launch the PostProcess with admin access in session 0. This is
255 // actually launching the post update process but it takes in the
256 // callback app path to figure out where to apply to.
257 // The PostUpdate process with user only access will be done inside
258 // the unelevated updater.exe after the update process is complete
259 // from the service. We don't know here which session to start
260 // the user PostUpdate process from.
261 // Note that we don't need to do this if we're just staging the
262 // update in the background, as the PostUpdate step runs when
263 // performing the replacing in that case.
264 if (!backgroundUpdate) {
265 LOG(("Launching post update process as the service in session 0."));
266 if (!LaunchWinPostProcess(installDir, updateInfoDir, true, nullptr)) {
267 LOG_WARN(("The post update process could not be launched."
268 " installDir: %ls, updateInfoDir: %ls",
269 installDir, updateInfoDir));
270 }
271 }
272 }
273 }
274
275 free(cmdLine);
276 return updateWasSuccessful;
277 }
278
279 /**
280 * Processes a software update command
281 *
282 * @param argc The number of arguments in argv
283 * @param argv The arguments normally passed to updater.exe
284 * argv[0] must be the path to updater.exe
285 * @return TRUE if the update was successful.
286 */
287 BOOL
288 ProcessSoftwareUpdateCommand(DWORD argc, LPWSTR *argv)
289 {
290 BOOL result = TRUE;
291 if (argc < 3) {
292 LOG_WARN(("Not enough command line parameters specified. "
293 "Updating update.status."));
294
295 // We can only update update.status if argv[1] exists. argv[1] is
296 // the directory where the update.status file exists.
297 if (argc < 2 ||
298 !WriteStatusFailure(argv[1],
299 SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS)) {
300 LOG_WARN(("Could not write update.status service update failure. (%d)",
301 GetLastError()));
302 }
303 return FALSE;
304 }
305
306 WCHAR installDir[MAX_PATH + 1] = {L'\0'};
307 if (!GetInstallationDir(argc, argv, installDir)) {
308 LOG_WARN(("Could not get the installation directory"));
309 if (!WriteStatusFailure(argv[1],
310 SERVICE_INSTALLDIR_ERROR)) {
311 LOG_WARN(("Could not write update.status for GetInstallationDir failure."));
312 }
313 return FALSE;
314 }
315
316 // Make sure the path to the updater to use for the update is local.
317 // We do this check to make sure that file locking is available for
318 // race condition security checks.
319 BOOL isLocal = FALSE;
320 if (!IsLocalFile(argv[0], isLocal) || !isLocal) {
321 LOG_WARN(("Filesystem in path %ls is not supported (%d)",
322 argv[0], GetLastError()));
323 if (!WriteStatusFailure(argv[1],
324 SERVICE_UPDATER_NOT_FIXED_DRIVE)) {
325 LOG_WARN(("Could not write update.status service update failure. (%d)",
326 GetLastError()));
327 }
328 return FALSE;
329 }
330
331 nsAutoHandle noWriteLock(CreateFileW(argv[0], GENERIC_READ, FILE_SHARE_READ,
332 nullptr, OPEN_EXISTING, 0, nullptr));
333 if (INVALID_HANDLE_VALUE == noWriteLock) {
334 LOG_WARN(("Could not set no write sharing access on file. (%d)",
335 GetLastError()));
336 if (!WriteStatusFailure(argv[1],
337 SERVICE_COULD_NOT_LOCK_UPDATER)) {
338 LOG_WARN(("Could not write update.status service update failure. (%d)",
339 GetLastError()));
340 }
341 return FALSE;
342 }
343
344 // Verify that the updater.exe that we are executing is the same
345 // as the one in the installation directory which we are updating.
346 // The installation dir that we are installing to is installDir.
347 WCHAR installDirUpdater[MAX_PATH + 1] = { L'\0' };
348 wcsncpy(installDirUpdater, installDir, MAX_PATH);
349 if (!PathAppendSafe(installDirUpdater, L"updater.exe")) {
350 LOG_WARN(("Install directory updater could not be determined."));
351 result = FALSE;
352 }
353
354 BOOL updaterIsCorrect;
355 if (result && !VerifySameFiles(argv[0], installDirUpdater,
356 updaterIsCorrect)) {
357 LOG_WARN(("Error checking if the updaters are the same.\n"
358 "Path 1: %ls\nPath 2: %ls", argv[0], installDirUpdater));
359 result = FALSE;
360 }
361
362 if (result && !updaterIsCorrect) {
363 LOG_WARN(("The updaters do not match, updater will not run."));
364 result = FALSE;
365 }
366
367 if (result) {
368 LOG(("updater.exe was compared successfully to the installation directory"
369 " updater.exe."));
370 } else {
371 if (!WriteStatusFailure(argv[1],
372 SERVICE_UPDATER_COMPARE_ERROR)) {
373 LOG_WARN(("Could not write update.status updater compare failure."));
374 }
375 return FALSE;
376 }
377
378 // Check to make sure the updater.exe module has the unique updater identity.
379 // This is a security measure to make sure that the signed executable that
380 // we will run is actually an updater.
381 HMODULE updaterModule = LoadLibraryEx(argv[0], nullptr,
382 LOAD_LIBRARY_AS_DATAFILE);
383 if (!updaterModule) {
384 LOG_WARN(("updater.exe module could not be loaded. (%d)", GetLastError()));
385 result = FALSE;
386 } else {
387 char updaterIdentity[64];
388 if (!LoadStringA(updaterModule, IDS_UPDATER_IDENTITY,
389 updaterIdentity, sizeof(updaterIdentity))) {
390 LOG_WARN(("The updater.exe application does not contain the Mozilla"
391 " updater identity."));
392 result = FALSE;
393 }
394
395 if (strcmp(updaterIdentity, UPDATER_IDENTITY_STRING)) {
396 LOG_WARN(("The updater.exe identity string is not valid."));
397 result = FALSE;
398 }
399 FreeLibrary(updaterModule);
400 }
401
402 if (result) {
403 LOG(("The updater.exe application contains the Mozilla"
404 " updater identity."));
405 } else {
406 if (!WriteStatusFailure(argv[1],
407 SERVICE_UPDATER_IDENTITY_ERROR)) {
408 LOG_WARN(("Could not write update.status no updater identity."));
409 }
410 return TRUE;
411 }
412
413 // Check for updater.exe sign problems
414 BOOL updaterSignProblem = FALSE;
415 #ifndef DISABLE_UPDATER_AUTHENTICODE_CHECK
416 updaterSignProblem = !DoesBinaryMatchAllowedCertificates(installDir,
417 argv[0]);
418 #endif
419
420 // Only proceed with the update if we have no signing problems
421 if (!updaterSignProblem) {
422 BOOL updateProcessWasStarted = FALSE;
423 if (StartUpdateProcess(argc, argv, installDir,
424 updateProcessWasStarted)) {
425 LOG(("updater.exe was launched and run successfully!"));
426 LogFlush();
427
428 // Don't attempt to update the service when the update is being staged.
429 if (!IsUpdateBeingStaged(argc, argv)) {
430 // We might not execute code after StartServiceUpdate because
431 // the service installer will stop the service if it is running.
432 StartServiceUpdate(installDir);
433 }
434 } else {
435 result = FALSE;
436 LOG_WARN(("Error running update process. Updating update.status (%d)",
437 GetLastError()));
438 LogFlush();
439
440 // If the update process was started, then updater.exe is responsible for
441 // setting the failure code. If it could not be started then we do the
442 // work. We set an error instead of directly setting status pending
443 // so that the app.update.service.errors pref can be updated when
444 // the callback app restarts.
445 if (!updateProcessWasStarted) {
446 if (!WriteStatusFailure(argv[1],
447 SERVICE_UPDATER_COULD_NOT_BE_STARTED)) {
448 LOG_WARN(("Could not write update.status service update failure. (%d)",
449 GetLastError()));
450 }
451 }
452 }
453 } else {
454 result = FALSE;
455 LOG_WARN(("Could not start process due to certificate check error on "
456 "updater.exe. Updating update.status. (%d)", GetLastError()));
457
458 // When there is a certificate check error on the updater.exe application,
459 // we want to write out the error.
460 if (!WriteStatusFailure(argv[1],
461 SERVICE_UPDATER_SIGN_ERROR)) {
462 LOG_WARN(("Could not write pending state to update.status. (%d)",
463 GetLastError()));
464 }
465 }
466
467 return result;
468 }
469
470 /**
471 * Obtains the updater path alongside a subdir of the service binary.
472 * The purpose of this function is to return a path that is likely high
473 * integrity and therefore more safe to execute code from.
474 *
475 * @param serviceUpdaterPath Out parameter for the path where the updater
476 * should be copied to.
477 * @return TRUE if a file path was obtained.
478 */
479 BOOL
480 GetSecureUpdaterPath(WCHAR serviceUpdaterPath[MAX_PATH + 1])
481 {
482 if (!GetModuleFileNameW(nullptr, serviceUpdaterPath, MAX_PATH)) {
483 LOG_WARN(("Could not obtain module filename when attempting to "
484 "use a secure updater path. (%d)", GetLastError()));
485 return FALSE;
486 }
487
488 if (!PathRemoveFileSpecW(serviceUpdaterPath)) {
489 LOG_WARN(("Couldn't remove file spec when attempting to use a secure "
490 "updater path. (%d)", GetLastError()));
491 return FALSE;
492 }
493
494 if (!PathAppendSafe(serviceUpdaterPath, L"update")) {
495 LOG_WARN(("Couldn't append file spec when attempting to use a secure "
496 "updater path. (%d)", GetLastError()));
497 return FALSE;
498 }
499
500 CreateDirectoryW(serviceUpdaterPath, nullptr);
501
502 if (!PathAppendSafe(serviceUpdaterPath, L"updater.exe")) {
503 LOG_WARN(("Couldn't append file spec when attempting to use a secure "
504 "updater path. (%d)", GetLastError()));
505 return FALSE;
506 }
507
508 return TRUE;
509 }
510
511 /**
512 * Deletes the passed in updater path and the associated updater.ini file.
513 *
514 * @param serviceUpdaterPath The path to delete.
515 * @return TRUE if a file was deleted.
516 */
517 BOOL
518 DeleteSecureUpdater(WCHAR serviceUpdaterPath[MAX_PATH + 1])
519 {
520 BOOL result = FALSE;
521 if (serviceUpdaterPath[0]) {
522 result = DeleteFileW(serviceUpdaterPath);
523 if (!result && GetLastError() != ERROR_PATH_NOT_FOUND &&
524 GetLastError() != ERROR_FILE_NOT_FOUND) {
525 LOG_WARN(("Could not delete service updater path: '%ls'.",
526 serviceUpdaterPath));
527 }
528
529 WCHAR updaterINIPath[MAX_PATH + 1] = { L'\0' };
530 if (PathGetSiblingFilePath(updaterINIPath, serviceUpdaterPath,
531 L"updater.ini")) {
532 result = DeleteFileW(updaterINIPath);
533 if (!result && GetLastError() != ERROR_PATH_NOT_FOUND &&
534 GetLastError() != ERROR_FILE_NOT_FOUND) {
535 LOG_WARN(("Could not delete service updater INI path: '%ls'.",
536 updaterINIPath));
537 }
538 }
539 }
540 return result;
541 }
542
543 /**
544 * Executes a service command.
545 *
546 * @param argc The number of arguments in argv
547 * @param argv The service command line arguments, argv[0] and argv[1]
548 * and automatically included by Windows. argv[2] is the
549 * service command.
550 *
551 * @return FALSE if there was an error executing the service command.
552 */
553 BOOL
554 ExecuteServiceCommand(int argc, LPWSTR *argv)
555 {
556 if (argc < 3) {
557 LOG_WARN(("Not enough command line arguments to execute a service command"));
558 return FALSE;
559 }
560
561 // The tests work by making sure the log has changed, so we put a
562 // unique ID in the log.
563 RPC_WSTR guidString = RPC_WSTR(L"");
564 GUID guid;
565 HRESULT hr = CoCreateGuid(&guid);
566 if (SUCCEEDED(hr)) {
567 UuidToString(&guid, &guidString);
568 }
569 LOG(("Executing service command %ls, ID: %ls",
570 argv[2], reinterpret_cast<LPCWSTR>(guidString)));
571 RpcStringFree(&guidString);
572
573 BOOL result = FALSE;
574 if (!lstrcmpi(argv[2], L"software-update")) {
575
576 // Use the passed in command line arguments for the update, except for the
577 // path to updater.exe. We copy updater.exe to a the directory of the
578 // MozillaMaintenance service so that a low integrity process cannot
579 // replace the updater.exe at any point and use that for the update.
580 // It also makes DLL injection attacks harder.
581 LPWSTR oldUpdaterPath = argv[3];
582 WCHAR secureUpdaterPath[MAX_PATH + 1] = { L'\0' };
583 result = GetSecureUpdaterPath(secureUpdaterPath); // Does its own logging
584 if (result) {
585 LOG(("Passed in path: '%ls'; Using this path for updating: '%ls'.",
586 oldUpdaterPath, secureUpdaterPath));
587 DeleteSecureUpdater(secureUpdaterPath);
588 result = CopyFileW(oldUpdaterPath, secureUpdaterPath, FALSE);
589 }
590
591 if (!result) {
592 LOG_WARN(("Could not copy path to secure location. (%d)",
593 GetLastError()));
594 if (argc > 4 && !WriteStatusFailure(argv[4],
595 SERVICE_COULD_NOT_COPY_UPDATER)) {
596 LOG_WARN(("Could not write update.status could not copy updater error"));
597 }
598 } else {
599
600 // We obtained the path and copied it successfully, update the path to
601 // use for the service update.
602 argv[3] = secureUpdaterPath;
603
604 WCHAR oldUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
605 WCHAR secureUpdaterINIPath[MAX_PATH + 1] = { L'\0' };
606 if (PathGetSiblingFilePath(secureUpdaterINIPath, secureUpdaterPath,
607 L"updater.ini") &&
608 PathGetSiblingFilePath(oldUpdaterINIPath, oldUpdaterPath,
609 L"updater.ini")) {
610 // This is non fatal if it fails there is no real harm
611 if (!CopyFileW(oldUpdaterINIPath, secureUpdaterINIPath, FALSE)) {
612 LOG_WARN(("Could not copy updater.ini from: '%ls' to '%ls'. (%d)",
613 oldUpdaterINIPath, secureUpdaterINIPath, GetLastError()));
614 }
615 }
616
617 result = ProcessSoftwareUpdateCommand(argc - 3, argv + 3);
618 DeleteSecureUpdater(secureUpdaterPath);
619 }
620
621 // We might not reach here if the service install succeeded
622 // because the service self updates itself and the service
623 // installer will stop the service.
624 LOG(("Service command %ls complete.", argv[2]));
625 } else {
626 LOG_WARN(("Service command not recognized: %ls.", argv[2]));
627 // result is already set to FALSE
628 }
629
630 LOG(("service command %ls complete with result: %ls.",
631 argv[1], (result ? L"Success" : L"Failure")));
632 return TRUE;
633 }

mercurial