Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include <stdlib.h>
8 : #include <stdio.h>
9 : #include "nsUpdateDriver.h"
10 : #include "nsXULAppAPI.h"
11 : #include "nsAppRunner.h"
12 : #include "nsIWritablePropertyBag.h"
13 : #include "nsIFile.h"
14 : #include "nsVariant.h"
15 : #include "nsCOMPtr.h"
16 : #include "nsString.h"
17 : #include "prproces.h"
18 : #include "mozilla/Logging.h"
19 : #include "prenv.h"
20 : #include "nsVersionComparator.h"
21 : #include "nsDirectoryServiceDefs.h"
22 : #include "nsThreadUtils.h"
23 : #include "nsIXULAppInfo.h"
24 : #include "mozilla/Preferences.h"
25 : #include "nsPrintfCString.h"
26 : #include "mozilla/DebugOnly.h"
27 : #include "mozilla/Printf.h"
28 :
29 : #ifdef XP_MACOSX
30 : #include "nsILocalFileMac.h"
31 : #include "nsCommandLineServiceMac.h"
32 : #include "MacLaunchHelper.h"
33 : #include "updaterfileutils_osx.h"
34 : #include "mozilla/Monitor.h"
35 : #endif
36 :
37 : #if defined(XP_WIN)
38 : # include <direct.h>
39 : # include <process.h>
40 : # include <windows.h>
41 : # include <shlwapi.h>
42 : # define getcwd(path, size) _getcwd(path, size)
43 : # define getpid() GetCurrentProcessId()
44 : #elif defined(XP_UNIX)
45 : # include <unistd.h>
46 : # include <sys/wait.h>
47 : #endif
48 :
49 : using namespace mozilla;
50 :
51 : static LazyLogModule sUpdateLog("updatedriver");
52 : #define LOG(args) MOZ_LOG(sUpdateLog, mozilla::LogLevel::Debug, args)
53 :
54 : #ifdef XP_WIN
55 : #define UPDATER_BIN "updater.exe"
56 : #elif XP_MACOSX
57 : #define UPDATER_BIN "org.mozilla.updater"
58 : #else
59 : #define UPDATER_BIN "updater"
60 : #endif
61 : #define UPDATER_INI "updater.ini"
62 : #ifdef XP_MACOSX
63 : #define UPDATER_APP "updater.app"
64 : #endif
65 : #if defined(XP_UNIX) && !defined(XP_MACOSX)
66 : #define UPDATER_PNG "updater.png"
67 : #endif
68 :
69 : #ifdef XP_MACOSX
70 : static void
71 : UpdateDriverSetupMacCommandLine(int& argc, char**& argv, bool restart)
72 : {
73 : if (NS_IsMainThread()) {
74 : CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart);
75 : return;
76 : }
77 : // Bug 1335916: SetupMacCommandLine calls a CoreFoundation function that
78 : // asserts that it was called from the main thread, so if we are not the main
79 : // thread, we have to dispatch that call to there. But we also have to get the
80 : // result from it, so we can't just dispatch and return, we have to wait
81 : // until the dispatched operation actually completes. So we also set up a
82 : // monitor to signal us when that happens, and block until then.
83 : Monitor monitor("nsUpdateDriver SetupMacCommandLine");
84 :
85 : nsresult rv = NS_DispatchToMainThread(
86 : NS_NewRunnableFunction("UpdateDriverSetupMacCommandLine",
87 : [&argc, &argv, restart, &monitor]() -> void
88 : {
89 : CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart);
90 : MonitorAutoLock(monitor).Notify();
91 : }));
92 :
93 : if (NS_FAILED(rv)) {
94 : LOG(("Update driver error dispatching SetupMacCommandLine to main thread: %d\n", rv));
95 : return;
96 : }
97 :
98 : // The length of this wait is arbitrary, but should be long enough that having
99 : // it expire means something is seriously wrong.
100 : CVStatus status = MonitorAutoLock(monitor).Wait(TimeDuration::FromSeconds(60));
101 : if (status == CVStatus::Timeout) {
102 : LOG(("Update driver timed out waiting for SetupMacCommandLine\n"));
103 : }
104 : }
105 : #endif
106 :
107 : static nsresult
108 0 : GetCurrentWorkingDir(char *buf, size_t size)
109 : {
110 : // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
111 : // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
112 :
113 : #if defined(XP_WIN)
114 : wchar_t wpath[MAX_PATH];
115 : if (!_wgetcwd(wpath, size))
116 : return NS_ERROR_FAILURE;
117 : NS_ConvertUTF16toUTF8 path(wpath);
118 : strncpy(buf, path.get(), size);
119 : #else
120 0 : if(!getcwd(buf, size))
121 : return NS_ERROR_FAILURE;
122 : #endif
123 0 : return NS_OK;
124 : }
125 :
126 : /**
127 : * Get the path to the installation directory. For Mac OS X this will be the
128 : * bundle directory.
129 : *
130 : * @param appDir the application directory file object
131 : * @param installDirPath the path to the installation directory
132 : */
133 : static nsresult
134 0 : GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath)
135 : {
136 : nsresult rv;
137 : #ifdef XP_MACOSX
138 : nsCOMPtr<nsIFile> parentDir1, parentDir2;
139 : rv = appDir->GetParent(getter_AddRefs(parentDir1));
140 : if (NS_FAILED(rv)) {
141 : return rv;
142 : }
143 : rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
144 : if (NS_FAILED(rv)) {
145 : return rv;
146 : }
147 : rv = parentDir2->GetNativePath(installDirPath);
148 : #elif XP_WIN
149 : nsAutoString installDirPathW;
150 : rv = appDir->GetPath(installDirPathW);
151 : if (NS_FAILED(rv)) {
152 : return rv;
153 : }
154 : installDirPath = NS_ConvertUTF16toUTF8(installDirPathW);
155 : #else
156 0 : rv = appDir->GetNativePath(installDirPath);
157 : #endif
158 0 : if (NS_FAILED(rv)) {
159 : return rv;
160 : }
161 0 : return NS_OK;
162 : }
163 :
164 : static bool
165 0 : GetFile(nsIFile* dir, const nsACString& name, nsCOMPtr<nsIFile>& result)
166 : {
167 : nsresult rv;
168 :
169 0 : nsCOMPtr<nsIFile> file;
170 0 : rv = dir->Clone(getter_AddRefs(file));
171 0 : if (NS_FAILED(rv))
172 : return false;
173 :
174 0 : rv = file->AppendNative(name);
175 0 : if (NS_FAILED(rv))
176 : return false;
177 :
178 0 : result = do_QueryInterface(file, &rv);
179 0 : return NS_SUCCEEDED(rv);
180 : }
181 :
182 : static bool
183 0 : GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
184 : {
185 0 : return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
186 : }
187 :
188 : /**
189 : * Get the contents of the update.status file.
190 : *
191 : * @param statusFile the status file object.
192 : * @param buf the buffer holding the file contents
193 : *
194 : * @return true if successful, false otherwise.
195 : */
196 : template <size_t Size>
197 : static bool
198 0 : GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
199 : {
200 : static_assert(Size > 16, "Buffer needs to be large enough to hold the known status codes");
201 :
202 0 : PRFileDesc *fd = nullptr;
203 0 : nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
204 0 : if (NS_FAILED(rv))
205 : return false;
206 :
207 0 : const int32_t n = PR_Read(fd, buf, Size);
208 0 : PR_Close(fd);
209 :
210 0 : return (n >= 0);
211 : }
212 :
213 : typedef enum {
214 : eNoUpdateAction,
215 : ePendingUpdate,
216 : ePendingService,
217 : ePendingElevate,
218 : eAppliedUpdate,
219 : eAppliedService,
220 : } UpdateStatus;
221 :
222 : /**
223 : * Returns a value indicating what needs to be done in order to handle an update.
224 : *
225 : * @param dir the directory in which we should look for an update.status file.
226 : * @param statusFile the update.status file found in the directory.
227 : *
228 : * @return the update action to be performed.
229 : */
230 : static UpdateStatus
231 0 : GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
232 : {
233 0 : if (GetStatusFile(dir, statusFile)) {
234 : char buf[32];
235 0 : if (GetStatusFileContents(statusFile, buf)) {
236 0 : const char kPending[] = "pending";
237 0 : const char kPendingService[] = "pending-service";
238 0 : const char kPendingElevate[] = "pending-elevate";
239 0 : const char kApplied[] = "applied";
240 0 : const char kAppliedService[] = "applied-service";
241 0 : if (!strncmp(buf, kPendingElevate, sizeof(kPendingElevate) - 1)) {
242 0 : return ePendingElevate;
243 : }
244 0 : if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
245 : return ePendingService;
246 : }
247 0 : if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
248 : return ePendingUpdate;
249 : }
250 0 : if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
251 : return eAppliedService;
252 : }
253 0 : if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
254 : return eAppliedUpdate;
255 : }
256 : }
257 : }
258 : return eNoUpdateAction;
259 : }
260 :
261 : static bool
262 0 : GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
263 : {
264 0 : return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
265 : }
266 :
267 : // Compares the current application version with the update's application
268 : // version.
269 : static bool
270 0 : IsOlderVersion(nsIFile *versionFile, const char *appVersion)
271 : {
272 0 : PRFileDesc *fd = nullptr;
273 0 : nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
274 0 : if (NS_FAILED(rv))
275 : return true;
276 :
277 : char buf[32];
278 0 : const int32_t n = PR_Read(fd, buf, sizeof(buf));
279 0 : PR_Close(fd);
280 :
281 0 : if (n < 0)
282 : return false;
283 :
284 : // Trim off the trailing newline
285 0 : if (buf[n - 1] == '\n')
286 0 : buf[n - 1] = '\0';
287 :
288 : // If the update xml doesn't provide the application version the file will
289 : // contain the string "null" and it is assumed that the update is not older.
290 0 : const char kNull[] = "null";
291 0 : if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
292 : return false;
293 :
294 0 : if (mozilla::Version(appVersion) > buf)
295 : return true;
296 :
297 0 : return false;
298 : }
299 :
300 : static bool
301 0 : CopyFileIntoUpdateDir(nsIFile *parentDir, const nsACString& leaf, nsIFile *updateDir)
302 : {
303 0 : nsCOMPtr<nsIFile> file;
304 :
305 : // Make sure there is not an existing file in the target location.
306 0 : nsresult rv = updateDir->Clone(getter_AddRefs(file));
307 0 : if (NS_FAILED(rv))
308 : return false;
309 0 : rv = file->AppendNative(leaf);
310 0 : if (NS_FAILED(rv))
311 : return false;
312 0 : file->Remove(true);
313 :
314 : // Now, copy into the target location.
315 0 : rv = parentDir->Clone(getter_AddRefs(file));
316 0 : if (NS_FAILED(rv))
317 : return false;
318 0 : rv = file->AppendNative(leaf);
319 0 : if (NS_FAILED(rv))
320 : return false;
321 0 : rv = file->CopyToNative(updateDir, EmptyCString());
322 0 : if (NS_FAILED(rv))
323 : return false;
324 :
325 0 : return true;
326 : }
327 :
328 : static bool
329 0 : CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
330 : nsCOMPtr<nsIFile> &updater)
331 : {
332 : // Copy the updater application from the GRE and the updater ini from the app.
333 : #if defined(XP_MACOSX)
334 : if (!CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_APP), updateDir))
335 : return false;
336 : CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
337 : #else
338 0 : if (!CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updateDir))
339 : return false;
340 0 : CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
341 : #endif
342 : #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
343 0 : nsCOMPtr<nsIFile> iconDir;
344 0 : appDir->Clone(getter_AddRefs(iconDir));
345 0 : iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
346 0 : if (!CopyFileIntoUpdateDir(iconDir, NS_LITERAL_CSTRING(UPDATER_PNG), updateDir))
347 : return false;
348 : #endif
349 : // Finally, return the location of the updater binary.
350 0 : nsresult rv = updateDir->Clone(getter_AddRefs(updater));
351 0 : if (NS_FAILED(rv))
352 : return false;
353 : #if defined(XP_MACOSX)
354 : rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_APP));
355 : nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
356 : if (NS_FAILED(tmp)) {
357 : rv = tmp;
358 : }
359 : tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
360 : if (NS_FAILED(tmp) || NS_FAILED(rv))
361 : return false;
362 : #endif
363 0 : rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
364 0 : return NS_SUCCEEDED(rv);
365 : }
366 :
367 : /**
368 : * Appends the specified path to the library path.
369 : * This is used so that updater can find libmozsqlite3.so and other shared libs.
370 : *
371 : * @param pathToAppend A new library path to prepend to LD_LIBRARY_PATH
372 : */
373 : #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
374 : #include "prprf.h"
375 : #define PATH_SEPARATOR ":"
376 : #define LD_LIBRARY_PATH_ENVVAR_NAME "LD_LIBRARY_PATH"
377 : static void
378 0 : AppendToLibPath(const char *pathToAppend)
379 : {
380 0 : char *pathValue = getenv(LD_LIBRARY_PATH_ENVVAR_NAME);
381 0 : if (nullptr == pathValue || '\0' == *pathValue) {
382 : // Leak the string because that is required by PR_SetEnv.
383 0 : char *s = Smprintf("%s=%s", LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend).release();
384 0 : PR_SetEnv(s);
385 0 : } else if (!strstr(pathValue, pathToAppend)) {
386 : // Leak the string because that is required by PR_SetEnv.
387 0 : char *s = Smprintf("%s=%s" PATH_SEPARATOR "%s",
388 0 : LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend, pathValue).release();
389 0 : PR_SetEnv(s);
390 : }
391 0 : }
392 : #endif
393 :
394 : /**
395 : * Applies, switches, or stages an update.
396 : *
397 : * @param greDir the GRE directory
398 : * @param updateDir the update root directory
399 : * @param appDir the application directory
400 : * @param appArgc the number of args passed to the application
401 : * @param appArgv the args passed to the application
402 : * (used for restarting the application when necessary)
403 : * @param restart true when a restart is necessary.
404 : * @param isStaged true when the update has already been staged
405 : * @param outpid (out) parameter holding the handle to the updater application
406 : * when staging updates
407 : */
408 : static void
409 0 : ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *appDir, int appArgc,
410 : char **appArgv, bool restart, bool isStaged, ProcessType *outpid)
411 : {
412 : // The following determines the update operation to perform.
413 : // 1. When restart is false the update will be staged.
414 : // 2. When restart is true and isStaged is false the update will apply the mar
415 : // file to the installation directory.
416 : // 3. When restart is true and isStaged is true the update will switch the
417 : // staged update with the installation directory.
418 :
419 : nsresult rv;
420 :
421 0 : nsCOMPtr<nsIFile> updater;
422 0 : nsAutoCString updaterPath;
423 0 : nsAutoCString updateDirPath;
424 : #if defined(XP_WIN)
425 : // Get an nsIFile reference for the updater in the installation dir.
426 : if (!GetFile(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updater)) {
427 : return;
428 : }
429 :
430 : // Get the path to the updater.
431 : nsAutoString updaterPathW;
432 : rv = updater->GetPath(updaterPathW);
433 : if (NS_FAILED(rv)) {
434 : return;
435 : }
436 : updaterPath = NS_ConvertUTF16toUTF8(updaterPathW);
437 :
438 : // Get the path to the update dir.
439 : nsAutoString updateDirPathW;
440 : rv = updateDir->GetPath(updateDirPathW);
441 : if (NS_FAILED(rv)) {
442 : return;
443 : }
444 : updateDirPath = NS_ConvertUTF16toUTF8(updateDirPathW);
445 : #else
446 0 : if (isStaged) {
447 0 : nsCOMPtr<nsIFile> mozUpdaterDir;
448 0 : rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir));
449 0 : if (NS_FAILED(rv)) {
450 0 : LOG(("failed cloning update dir\n"));
451 0 : return;
452 : }
453 :
454 : // Create a new directory named MozUpdater in the updates/0 directory to copy
455 : // the updater files to that will be used to replace the installation with the
456 : // staged application that has been updated. Note that we don't check for
457 : // directory creation errors since the call to CopyUpdaterIntoUpdateDir will
458 : // fail if the creation of the directory fails. A unique directory is created
459 : // in MozUpdater in case a previous attempt locked the directory or files.
460 0 : mozUpdaterDir->Append(NS_LITERAL_STRING("MozUpdater"));
461 0 : mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate"));
462 0 : rv = mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
463 0 : if (NS_FAILED(rv)) {
464 0 : LOG(("failed creating unique dir\n"));
465 : return;
466 : }
467 :
468 : // Copy the updater and files needed to update into the MozUpdater/bgupdate
469 : // directory in the update dir and get an nsIFile reference to the copied
470 : // updater.
471 0 : if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) {
472 0 : LOG(("failed copying updater\n"));
473 : return;
474 : }
475 : } else {
476 : // Copy the updater and files needed to update into the update directory and
477 : // get an nsIFile reference to the copied updater.
478 0 : if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
479 0 : LOG(("failed copying updater\n"));
480 : return;
481 : }
482 : }
483 :
484 : // Get the path to the updater that will be used.
485 0 : rv = updater->GetNativePath(updaterPath);
486 0 : if (NS_FAILED(rv)) {
487 : return;
488 : }
489 :
490 : // Get the path to the update dir.
491 0 : rv = updateDir->GetNativePath(updateDirPath);
492 0 : if (NS_FAILED(rv)) {
493 : return;
494 : }
495 : #endif
496 :
497 : // appFilePath and workingDirPath are only used when the application will be
498 : // restarted.
499 0 : nsAutoCString appFilePath;
500 : char workingDirPath[MAXPATHLEN];
501 0 : if (restart) {
502 : // Get the path to the current working directory.
503 0 : rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
504 0 : if (NS_FAILED(rv)) {
505 0 : return;
506 : }
507 :
508 : // Get the application file path used by the updater to restart the
509 : // application after the update has finished.
510 0 : nsCOMPtr<nsIFile> appFile;
511 0 : XRE_GetBinaryPath(getter_AddRefs(appFile));
512 0 : if (!appFile) {
513 0 : return;
514 : }
515 :
516 : #if defined(XP_WIN)
517 : nsAutoString appFilePathW;
518 : rv = appFile->GetPath(appFilePathW);
519 : if (NS_FAILED(rv)) {
520 : return;
521 : }
522 : appFilePath = NS_ConvertUTF16toUTF8(appFilePathW);
523 : #else
524 0 : rv = appFile->GetNativePath(appFilePath);
525 0 : if (NS_FAILED(rv)) {
526 : return;
527 : }
528 : #endif
529 : }
530 :
531 : // Get the installation directory path.
532 0 : nsAutoCString installDirPath;
533 0 : rv = GetInstallDirPath(appDir, installDirPath);
534 0 : if (NS_FAILED(rv)) {
535 : return;
536 : }
537 :
538 0 : nsAutoCString applyToDirPath;
539 0 : nsCOMPtr<nsIFile> updatedDir;
540 0 : if (restart && !isStaged) {
541 : // The install directory is the same as the apply to directory.
542 0 : applyToDirPath.Assign(installDirPath);
543 : } else {
544 : // Get the directory where the update is staged or will be staged.
545 : #if defined(XP_MACOSX)
546 : if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) {
547 : #else
548 0 : if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
549 : #endif
550 0 : return;
551 : }
552 : #if defined(XP_WIN)
553 : nsAutoString applyToDirPathW;
554 : rv = updatedDir->GetPath(applyToDirPathW);
555 : if (NS_FAILED(rv)) {
556 : return;
557 : }
558 : applyToDirPath = NS_ConvertUTF16toUTF8(applyToDirPathW);
559 : #else
560 0 : rv = updatedDir->GetNativePath(applyToDirPath);
561 : #endif
562 : }
563 0 : if (NS_FAILED(rv)) {
564 : return;
565 : }
566 :
567 0 : if (restart && isStaged) {
568 : // When the update should already be staged make sure that the updated
569 : // directory exists.
570 0 : bool updatedDirExists = false;
571 0 : if (NS_FAILED(updatedDir->Exists(&updatedDirExists)) || !updatedDirExists) {
572 0 : return;
573 : }
574 : }
575 :
576 : // On platforms where we are not calling execv, we may need to make the
577 : // updater executable wait for the calling process to exit. Otherwise, the
578 : // updater may have trouble modifying our executable image (because it might
579 : // still be in use). This is accomplished by passing our PID to the updater so
580 : // that it can wait for us to exit. This is not perfect as there is a race
581 : // condition that could bite us. It's possible that the calling process could
582 : // exit before the updater waits on the specified PID, and in the meantime a
583 : // new process with the same PID could be created. This situation is unlikely,
584 : // however, given the way most operating systems recycle PIDs. We'll take our
585 : // chances ;-)
586 : // Construct the PID argument for this process to pass to the updater.
587 0 : nsAutoCString pid;
588 0 : if (restart) {
589 : #if defined(XP_UNIX) & !defined(XP_MACOSX)
590 : // When execv is used for an update that requires a restart 0 is passed
591 : // which is ignored by the updater.
592 0 : pid.AssignASCII("0");
593 : #else
594 : pid.AppendInt((int32_t) getpid());
595 : #endif
596 0 : if (isStaged) {
597 : // Append a special token to the PID in order to inform the updater that
598 : // it should replace install with the updated directory.
599 0 : pid.AppendLiteral("/replace");
600 : }
601 : } else {
602 : // Signal the updater application that it should stage the update.
603 0 : pid.AssignASCII("-1");
604 : }
605 :
606 0 : int argc = 5;
607 0 : if (restart) {
608 0 : argc = appArgc + 6;
609 0 : if (gRestartedByOS) {
610 0 : argc += 1;
611 : }
612 : }
613 0 : char **argv = new char*[argc + 1];
614 0 : if (!argv) {
615 : return;
616 : }
617 0 : argv[0] = (char*) updaterPath.get();
618 0 : argv[1] = (char*) updateDirPath.get();
619 0 : argv[2] = (char*) installDirPath.get();
620 0 : argv[3] = (char*) applyToDirPath.get();
621 0 : argv[4] = (char*) pid.get();
622 0 : if (restart && appArgc) {
623 0 : argv[5] = workingDirPath;
624 0 : argv[6] = (char*) appFilePath.get();
625 0 : for (int i = 1; i < appArgc; ++i) {
626 0 : argv[6 + i] = appArgv[i];
627 : }
628 0 : if (gRestartedByOS) {
629 : // We haven't truly started up, restore this argument so that we will have
630 : // it upon restart.
631 0 : argv[6 + appArgc] = const_cast<char*>("-os-restarted");
632 : }
633 : }
634 0 : argv[argc] = nullptr;
635 :
636 0 : if (restart && gSafeMode) {
637 0 : PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
638 : }
639 :
640 : #if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && !defined(XP_MACOSX)
641 0 : AppendToLibPath(installDirPath.get());
642 : #endif
643 :
644 0 : LOG(("spawning updater process [%s]\n", updaterPath.get()));
645 :
646 : #if defined(XP_UNIX) && !defined(XP_MACOSX)
647 : // We use execv to spawn the updater process on all UNIX systems except Mac OSX
648 : // since it is known to cause problems on the Mac. Windows has execv, but it
649 : // is a faked implementation that doesn't really replace the current process.
650 : // Instead it spawns a new process, so we gain nothing from using execv on
651 : // Windows.
652 0 : if (restart) {
653 0 : exit(execv(updaterPath.get(), argv));
654 : }
655 0 : *outpid = fork();
656 0 : if (*outpid == -1) {
657 : return;
658 0 : } else if (*outpid == 0) {
659 0 : exit(execv(updaterPath.get(), argv));
660 : }
661 : #elif defined(XP_WIN)
662 : if (isStaged) {
663 : // Launch the updater to replace the installation with the staged updated.
664 : if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
665 : return;
666 : }
667 : } else {
668 : // Launch the updater to either stage or apply an update.
669 : if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
670 : return;
671 : }
672 : }
673 : #elif defined(XP_MACOSX)
674 : UpdateDriverSetupMacCommandLine(argc, argv, restart);
675 : // We need to detect whether elevation is required for this update. This can
676 : // occur when an admin user installs the application, but another admin
677 : // user attempts to update (see bug 394984).
678 : if (restart && !IsRecursivelyWritable(installDirPath.get())) {
679 : if (!LaunchElevatedUpdate(argc, argv, outpid)) {
680 : LOG(("Failed to launch elevated update!"));
681 : exit(1);
682 : }
683 : exit(0);
684 : }
685 :
686 : if (isStaged) {
687 : // Launch the updater to replace the installation with the staged updated.
688 : LaunchChildMac(argc, argv);
689 : } else {
690 : // Launch the updater to either stage or apply an update.
691 : LaunchChildMac(argc, argv, outpid);
692 : }
693 : if (restart) {
694 : exit(0);
695 : }
696 : #else
697 : if (isStaged) {
698 : // Launch the updater to replace the installation with the staged updated.
699 : PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
700 : } else {
701 : // Launch the updater to either stage or apply an update.
702 : *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
703 : }
704 : #endif
705 : #if !defined(USE_EXECV)
706 : if (restart) {
707 : exit(0);
708 : }
709 : #endif
710 : }
711 :
712 : /**
713 : * Wait briefly to see if a process terminates, then return true if it has.
714 : */
715 : static bool
716 0 : ProcessHasTerminated(ProcessType pt)
717 : {
718 : #if defined(XP_WIN)
719 : if (WaitForSingleObject(pt, 1000)) {
720 : return false;
721 : }
722 : CloseHandle(pt);
723 : return true;
724 : #elif defined(XP_MACOSX)
725 : // We're waiting for the process to terminate in LaunchChildMac.
726 : return true;
727 : #elif defined(XP_UNIX)
728 : int exitStatus;
729 0 : pid_t exited = waitpid(pt, &exitStatus, WNOHANG);
730 0 : if (exited == 0) {
731 : // Process is still running.
732 0 : sleep(1);
733 0 : return false;
734 : }
735 0 : if (exited == -1) {
736 0 : LOG(("Error while checking if the updater process is finished"));
737 : // This shouldn't happen, but if it does, the updater process is lost to us,
738 : // so the best we can do is pretend that it's exited.
739 : return true;
740 : }
741 : // If we get here, the process has exited; make sure it exited normally.
742 0 : if (WIFEXITED(exitStatus) && (WEXITSTATUS(exitStatus) != 0)) {
743 0 : LOG(("Error while running the updater process, check update.log"));
744 : }
745 : return true;
746 : #else
747 : // No way to have a non-blocking implementation on these platforms,
748 : // because we're using NSPR and it only provides a blocking wait.
749 : int32_t exitCode;
750 : PR_WaitProcess(pt, &exitCode);
751 : if (exitCode != 0) {
752 : LOG(("Error while running the updater process, check update.log"));
753 : }
754 : return true;
755 : #endif
756 : }
757 :
758 : nsresult
759 0 : ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
760 : int argc, char **argv, const char *appVersion,
761 : bool restart, ProcessType *pid)
762 : {
763 : nsresult rv;
764 :
765 0 : nsCOMPtr<nsIFile> updatesDir;
766 0 : rv = updRootDir->Clone(getter_AddRefs(updatesDir));
767 1 : if (NS_FAILED(rv)) {
768 : return rv;
769 : }
770 :
771 0 : rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
772 1 : if (NS_FAILED(rv)) {
773 : return rv;
774 : }
775 :
776 3 : rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
777 1 : if (NS_FAILED(rv)) {
778 : return rv;
779 : }
780 :
781 : // Return early since there isn't a valid update when the update application
782 : // version file doesn't exist or if the update's application version is less
783 : // than the current application version. The cleanup of the update will happen
784 : // during post update processing in nsUpdateService.js.
785 0 : nsCOMPtr<nsIFile> versionFile;
786 0 : if (!GetVersionFile(updatesDir, versionFile) ||
787 0 : IsOlderVersion(versionFile, appVersion)) {
788 : return NS_OK;
789 : }
790 :
791 0 : nsCOMPtr<nsIFile> statusFile;
792 0 : UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
793 0 : switch (status) {
794 : case ePendingElevate: {
795 0 : if (NS_IsMainThread()) {
796 : // Only do this if we're called from the main thread.
797 : nsCOMPtr<nsIUpdatePrompt> up =
798 0 : do_GetService("@mozilla.org/updates/update-prompt;1");
799 0 : if (up) {
800 0 : up->ShowUpdateElevationRequired();
801 : }
802 : break;
803 : }
804 : // Intentional fallthrough to ePendingUpdate and ePendingService.
805 : MOZ_FALLTHROUGH;
806 : }
807 : case ePendingUpdate:
808 : case ePendingService: {
809 0 : ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, false, pid);
810 0 : break;
811 : }
812 : case eAppliedUpdate:
813 : case eAppliedService:
814 : // An update was staged and needs to be switched so the updated application
815 : // is used.
816 0 : ApplyUpdate(greDir, updatesDir, appDir, argc, argv, restart, true, pid);
817 0 : break;
818 : case eNoUpdateAction:
819 : // We don't need to do any special processing here, we'll just continue to
820 : // startup the application.
821 : break;
822 : }
823 :
824 : return NS_OK;
825 : }
826 :
827 :
828 :
829 0 : NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor)
830 :
831 0 : nsUpdateProcessor::nsUpdateProcessor()
832 0 : : mUpdaterPID(0)
833 : {
834 0 : }
835 :
836 0 : nsUpdateProcessor::~nsUpdateProcessor()
837 : {
838 0 : }
839 :
840 : NS_IMETHODIMP
841 0 : nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
842 : {
843 : nsresult rv;
844 :
845 : nsCOMPtr<nsIProperties> ds =
846 0 : do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
847 0 : NS_ENSURE_SUCCESS(rv, rv);
848 :
849 0 : nsCOMPtr<nsIFile> exeFile;
850 0 : rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
851 0 : getter_AddRefs(exeFile));
852 0 : NS_ENSURE_SUCCESS(rv, rv);
853 :
854 0 : nsCOMPtr<nsIFile> appDir;
855 0 : rv = exeFile->GetParent(getter_AddRefs(appDir));
856 0 : NS_ENSURE_SUCCESS(rv, rv);
857 :
858 0 : nsCOMPtr<nsIFile> greDir;
859 0 : rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), getter_AddRefs(greDir));
860 0 : NS_ENSURE_SUCCESS(rv, rv);
861 :
862 0 : nsCOMPtr<nsIFile> updRoot;
863 0 : rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
864 0 : getter_AddRefs(updRoot));
865 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir");
866 :
867 : // XRE_UPDATE_ROOT_DIR should not fail but if it does fallback to the
868 : // application directory just to be safe.
869 0 : if (NS_FAILED(rv)) {
870 0 : rv = appDir->Clone(getter_AddRefs(updRoot));
871 0 : NS_ENSURE_SUCCESS(rv, rv);
872 : }
873 :
874 : nsCOMPtr<nsIXULAppInfo> appInfo =
875 0 : do_GetService("@mozilla.org/xre/app-info;1", &rv);
876 0 : NS_ENSURE_SUCCESS(rv, rv);
877 :
878 0 : nsAutoCString appVersion;
879 0 : rv = appInfo->GetVersion(appVersion);
880 0 : NS_ENSURE_SUCCESS(rv, rv);
881 :
882 : // Copy the parameters to the StagedUpdateInfo structure shared with the
883 : // watcher thread.
884 0 : mInfo.mGREDir = greDir;
885 0 : mInfo.mAppDir = appDir;
886 0 : mInfo.mUpdateRoot = updRoot;
887 0 : mInfo.mArgc = 0;
888 0 : mInfo.mArgv = nullptr;
889 0 : mInfo.mAppVersion = appVersion;
890 :
891 0 : MOZ_ASSERT(NS_IsMainThread(), "not main thread");
892 : nsCOMPtr<nsIRunnable> r =
893 0 : NewRunnableMethod("nsUpdateProcessor::StartStagedUpdate",
894 : this,
895 0 : &nsUpdateProcessor::StartStagedUpdate);
896 0 : return NS_NewNamedThread("Update Watcher", getter_AddRefs(mProcessWatcher),
897 0 : r);
898 : }
899 :
900 :
901 :
902 : void
903 0 : nsUpdateProcessor::StartStagedUpdate()
904 : {
905 0 : MOZ_ASSERT(!NS_IsMainThread(), "main thread");
906 :
907 0 : nsresult rv = ProcessUpdates(mInfo.mGREDir,
908 : mInfo.mAppDir,
909 : mInfo.mUpdateRoot,
910 : mInfo.mArgc,
911 : mInfo.mArgv,
912 : mInfo.mAppVersion.get(),
913 : false,
914 0 : &mUpdaterPID);
915 0 : NS_ENSURE_SUCCESS_VOID(rv);
916 :
917 0 : if (mUpdaterPID) {
918 : // Track the state of the updater process while it is staging an update.
919 0 : rv = NS_DispatchToCurrentThread(
920 0 : NewRunnableMethod("nsUpdateProcessor::WaitForProcess",
921 : this,
922 0 : &nsUpdateProcessor::WaitForProcess));
923 0 : NS_ENSURE_SUCCESS_VOID(rv);
924 : } else {
925 : // Failed to launch the updater process for some reason.
926 : // We need to shutdown the current thread as there isn't anything more for
927 : // us to do...
928 0 : rv = NS_DispatchToMainThread(
929 0 : NewRunnableMethod("nsUpdateProcessor::ShutdownWatcherThread",
930 : this,
931 0 : &nsUpdateProcessor::ShutdownWatcherThread));
932 0 : NS_ENSURE_SUCCESS_VOID(rv);
933 : }
934 : }
935 :
936 : void
937 0 : nsUpdateProcessor::ShutdownWatcherThread()
938 : {
939 0 : MOZ_ASSERT(NS_IsMainThread(), "not main thread");
940 0 : mProcessWatcher->Shutdown();
941 0 : mProcessWatcher = nullptr;
942 0 : }
943 :
944 : void
945 0 : nsUpdateProcessor::WaitForProcess()
946 : {
947 0 : MOZ_ASSERT(!NS_IsMainThread(), "main thread");
948 0 : if (ProcessHasTerminated(mUpdaterPID)) {
949 0 : NS_DispatchToMainThread(NewRunnableMethod(
950 0 : "nsUpdateProcessor::UpdateDone", this, &nsUpdateProcessor::UpdateDone));
951 : } else {
952 0 : NS_DispatchToCurrentThread(
953 0 : NewRunnableMethod("nsUpdateProcessor::WaitForProcess",
954 : this,
955 0 : &nsUpdateProcessor::WaitForProcess));
956 : }
957 0 : }
958 :
959 : void
960 0 : nsUpdateProcessor::UpdateDone()
961 : {
962 0 : MOZ_ASSERT(NS_IsMainThread(), "not main thread");
963 :
964 : nsCOMPtr<nsIUpdateManager> um =
965 0 : do_GetService("@mozilla.org/updates/update-manager;1");
966 : if (um) {
967 : um->RefreshUpdateStatus();
968 : }
969 :
970 : ShutdownWatcherThread();
971 : }
|