|
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 "nsIVariant.h" |
|
15 #include "nsCOMPtr.h" |
|
16 #include "nsString.h" |
|
17 #include "prproces.h" |
|
18 #include "prlog.h" |
|
19 #include "prenv.h" |
|
20 #include "nsVersionComparator.h" |
|
21 #include "nsXREDirProvider.h" |
|
22 #include "SpecialSystemDirectory.h" |
|
23 #include "nsDirectoryServiceDefs.h" |
|
24 #include "nsThreadUtils.h" |
|
25 #include "nsIXULAppInfo.h" |
|
26 #include "mozilla/Preferences.h" |
|
27 #include "nsPrintfCString.h" |
|
28 #include "mozilla/DebugOnly.h" |
|
29 |
|
30 #ifdef XP_MACOSX |
|
31 #include "nsILocalFileMac.h" |
|
32 #include "nsCommandLineServiceMac.h" |
|
33 #include "MacLaunchHelper.h" |
|
34 #endif |
|
35 |
|
36 #if defined(XP_WIN) |
|
37 # include <direct.h> |
|
38 # include <process.h> |
|
39 # include <windows.h> |
|
40 # include <shlwapi.h> |
|
41 # include "nsWindowsHelpers.h" |
|
42 # include "prprf.h" |
|
43 # define getcwd(path, size) _getcwd(path, size) |
|
44 # define getpid() GetCurrentProcessId() |
|
45 #elif defined(XP_UNIX) |
|
46 # include <unistd.h> |
|
47 #endif |
|
48 |
|
49 using namespace mozilla; |
|
50 |
|
51 // |
|
52 // We use execv to spawn the updater process on all UNIX systems except Mac OSX |
|
53 // since it is known to cause problems on the Mac. Windows has execv, but it |
|
54 // is a faked implementation that doesn't really replace the current process. |
|
55 // Instead it spawns a new process, so we gain nothing from using execv on |
|
56 // Windows. |
|
57 // |
|
58 // On platforms where we are not calling execv, we may need to make the |
|
59 // updater executable wait for the calling process to exit. Otherwise, the |
|
60 // updater may have trouble modifying our executable image (because it might |
|
61 // still be in use). This is accomplished by passing our PID to the updater so |
|
62 // that it can wait for us to exit. This is not perfect as there is a race |
|
63 // condition that could bite us. It's possible that the calling process could |
|
64 // exit before the updater waits on the specified PID, and in the meantime a |
|
65 // new process with the same PID could be created. This situation is unlikely, |
|
66 // however, given the way most operating systems recycle PIDs. We'll take our |
|
67 // chances ;-) |
|
68 // |
|
69 // A similar #define lives in updater.cpp and should be kept in sync with this. |
|
70 // |
|
71 #if defined(XP_UNIX) && !defined(XP_MACOSX) |
|
72 #define USE_EXECV |
|
73 #endif |
|
74 |
|
75 #ifdef PR_LOGGING |
|
76 static PRLogModuleInfo * |
|
77 GetUpdateLog() |
|
78 { |
|
79 static PRLogModuleInfo *sUpdateLog; |
|
80 if (!sUpdateLog) |
|
81 sUpdateLog = PR_NewLogModule("updatedriver"); |
|
82 return sUpdateLog; |
|
83 } |
|
84 #endif |
|
85 #define LOG(args) PR_LOG(GetUpdateLog(), PR_LOG_DEBUG, args) |
|
86 |
|
87 #ifdef XP_WIN |
|
88 static const char kUpdaterBin[] = "updater.exe"; |
|
89 #else |
|
90 static const char kUpdaterBin[] = "updater"; |
|
91 #endif |
|
92 static const char kUpdaterINI[] = "updater.ini"; |
|
93 #ifdef XP_MACOSX |
|
94 static const char kUpdaterApp[] = "updater.app"; |
|
95 #endif |
|
96 #if defined(XP_UNIX) && !defined(XP_MACOSX) |
|
97 static const char kUpdaterPNG[] = "updater.png"; |
|
98 #endif |
|
99 |
|
100 #if defined(MOZ_WIDGET_GONK) |
|
101 #include <linux/ioprio.h> |
|
102 |
|
103 static const int kB2GServiceArgc = 2; |
|
104 static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" }; |
|
105 |
|
106 static const char kAppUpdaterPrio[] = "app.update.updater.prio"; |
|
107 static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj"; |
|
108 static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class"; |
|
109 static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level"; |
|
110 |
|
111 static const int kAppUpdaterPrioDefault = 19; // -20..19 where 19 = lowest priority |
|
112 static const int kAppUpdaterOomScoreAdjDefault = -1000; // -1000 = Never kill |
|
113 static const int kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE; |
|
114 static const int kAppUpdaterIOPrioLevelDefault = 0; // Doesn't matter for CLASS IDLE |
|
115 #endif |
|
116 |
|
117 static nsresult |
|
118 GetCurrentWorkingDir(char *buf, size_t size) |
|
119 { |
|
120 // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized. |
|
121 // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp: |
|
122 |
|
123 #if defined(XP_WIN) |
|
124 wchar_t wpath[MAX_PATH]; |
|
125 if (!_wgetcwd(wpath, size)) |
|
126 return NS_ERROR_FAILURE; |
|
127 NS_ConvertUTF16toUTF8 path(wpath); |
|
128 strncpy(buf, path.get(), size); |
|
129 #else |
|
130 if(!getcwd(buf, size)) |
|
131 return NS_ERROR_FAILURE; |
|
132 #endif |
|
133 return NS_OK; |
|
134 } |
|
135 |
|
136 |
|
137 #if defined(XP_WIN) |
|
138 #define PATH_SEPARATOR ";" |
|
139 |
|
140 // In Tor Browser, updater.exe depends on some DLLs that are located in the |
|
141 // app directory. To allow the updater to run when it has been copied into |
|
142 // the update directory, we append the app directory to the PATH. |
|
143 static nsresult |
|
144 AdjustPathForUpdater(nsIFile *appDir) |
|
145 { |
|
146 nsAutoCString appPath; |
|
147 nsresult rv = appDir->GetNativePath(appPath); |
|
148 NS_ENSURE_SUCCESS(rv, rv); |
|
149 |
|
150 char *s = nullptr; |
|
151 char *pathValue = PR_GetEnv("PATH"); |
|
152 if ((nullptr == pathValue) || ('\0' == *pathValue)) { |
|
153 s = PR_smprintf("PATH=%s", appPath.get()); |
|
154 } else { |
|
155 s = PR_smprintf("PATH=%s" PATH_SEPARATOR "%s", pathValue, appPath.get()); |
|
156 } |
|
157 |
|
158 // We intentionally leak the value that is passed into PR_SetEnv() because |
|
159 // the environment will hold a pointer to it. |
|
160 if ((nullptr == s) || (PR_SUCCESS != PR_SetEnv(s))) |
|
161 return NS_ERROR_FAILURE; |
|
162 |
|
163 return NS_OK; |
|
164 } |
|
165 #endif |
|
166 |
|
167 #ifdef DEBUG |
|
168 static void |
|
169 dump_argv(const char *aPrefix, char **argv, int argc) |
|
170 { |
|
171 printf("%s - %d args\n", aPrefix, argc); |
|
172 for (int i = 0; i < argc; ++i) |
|
173 printf(" %d: %s\n", i, argv[i]); |
|
174 } |
|
175 #endif |
|
176 |
|
177 |
|
178 #if defined(XP_MACOSX) |
|
179 // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the |
|
180 // gBinaryPath check removed so that the updater can reload the stub executable |
|
181 // instead of xulrunner-bin. See bug 349737. |
|
182 static nsresult |
|
183 GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult) |
|
184 { |
|
185 // Works even if we're not bundled. |
|
186 CFBundleRef appBundle = ::CFBundleGetMainBundle(); |
|
187 if (!appBundle) |
|
188 return NS_ERROR_FAILURE; |
|
189 |
|
190 CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle); |
|
191 if (!bundleURL) |
|
192 return NS_ERROR_FAILURE; |
|
193 |
|
194 nsCOMPtr<nsILocalFileMac> lfm; |
|
195 nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm)); |
|
196 |
|
197 ::CFRelease(bundleURL); |
|
198 |
|
199 if (NS_FAILED(rv)) |
|
200 return rv; |
|
201 |
|
202 NS_ADDREF(*aResult = static_cast<nsIFile*>(lfm.get())); |
|
203 return NS_OK; |
|
204 } |
|
205 #endif /* XP_MACOSX */ |
|
206 |
|
207 static bool |
|
208 GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result) |
|
209 { |
|
210 nsresult rv; |
|
211 |
|
212 nsCOMPtr<nsIFile> file; |
|
213 rv = dir->Clone(getter_AddRefs(file)); |
|
214 if (NS_FAILED(rv)) |
|
215 return false; |
|
216 |
|
217 rv = file->AppendNative(name); |
|
218 if (NS_FAILED(rv)) |
|
219 return false; |
|
220 |
|
221 result = do_QueryInterface(file, &rv); |
|
222 return NS_SUCCEEDED(rv); |
|
223 } |
|
224 |
|
225 static bool |
|
226 GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result) |
|
227 { |
|
228 return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result); |
|
229 } |
|
230 |
|
231 /** |
|
232 * Get the contents of the update.status file. |
|
233 * |
|
234 * @param statusFile the status file object. |
|
235 * @param buf the buffer holding the file contents |
|
236 * |
|
237 * @return true if successful, false otherwise. |
|
238 */ |
|
239 template <size_t Size> |
|
240 static bool |
|
241 GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size]) |
|
242 { |
|
243 // The buffer needs to be large enough to hold the known status codes |
|
244 PR_STATIC_ASSERT(Size > 16); |
|
245 |
|
246 PRFileDesc *fd = nullptr; |
|
247 nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd); |
|
248 if (NS_FAILED(rv)) |
|
249 return false; |
|
250 |
|
251 const int32_t n = PR_Read(fd, buf, Size); |
|
252 PR_Close(fd); |
|
253 |
|
254 return (n >= 0); |
|
255 } |
|
256 |
|
257 typedef enum { |
|
258 eNoUpdateAction, |
|
259 ePendingUpdate, |
|
260 ePendingService, |
|
261 eAppliedUpdate, |
|
262 eAppliedService |
|
263 } UpdateStatus; |
|
264 |
|
265 /** |
|
266 * Returns a value indicating what needs to be done in order to handle an update. |
|
267 * |
|
268 * @param dir the directory in which we should look for an update.status file. |
|
269 * @param statusFile the update.status file found in the directory. |
|
270 * |
|
271 * @return the update action to be performed. |
|
272 */ |
|
273 static UpdateStatus |
|
274 GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile) |
|
275 { |
|
276 if (GetStatusFile(dir, statusFile)) { |
|
277 char buf[32]; |
|
278 if (GetStatusFileContents(statusFile, buf)) { |
|
279 const char kPending[] = "pending"; |
|
280 const char kPendingService[] = "pending-service"; |
|
281 const char kApplied[] = "applied"; |
|
282 const char kAppliedService[] = "applied-service"; |
|
283 if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) { |
|
284 return ePendingService; |
|
285 } |
|
286 if (!strncmp(buf, kPending, sizeof(kPending) - 1)) { |
|
287 return ePendingUpdate; |
|
288 } |
|
289 if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) { |
|
290 return eAppliedService; |
|
291 } |
|
292 if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) { |
|
293 return eAppliedUpdate; |
|
294 } |
|
295 } |
|
296 } |
|
297 return eNoUpdateAction; |
|
298 } |
|
299 |
|
300 static bool |
|
301 GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result) |
|
302 { |
|
303 return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result); |
|
304 } |
|
305 |
|
306 // Compares the current application version with the update's application |
|
307 // version. |
|
308 static bool |
|
309 IsOlderVersion(nsIFile *versionFile, const char *appVersion) |
|
310 { |
|
311 PRFileDesc *fd = nullptr; |
|
312 nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd); |
|
313 if (NS_FAILED(rv)) |
|
314 return true; |
|
315 |
|
316 char buf[32]; |
|
317 const int32_t n = PR_Read(fd, buf, sizeof(buf)); |
|
318 PR_Close(fd); |
|
319 |
|
320 if (n < 0) |
|
321 return false; |
|
322 |
|
323 // Trim off the trailing newline |
|
324 if (buf[n - 1] == '\n') |
|
325 buf[n - 1] = '\0'; |
|
326 |
|
327 // If the update xml doesn't provide the application version the file will |
|
328 // contain the string "null" and it is assumed that the update is not older. |
|
329 const char kNull[] = "null"; |
|
330 if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0) |
|
331 return false; |
|
332 |
|
333 #ifdef DEBUG |
|
334 printf("IsOlderVersion checking appVersion %s against updateVersion %s\n", |
|
335 appVersion, buf); |
|
336 #endif |
|
337 if (mozilla::Version(appVersion) > buf) |
|
338 return true; |
|
339 |
|
340 return false; |
|
341 } |
|
342 |
|
343 #if defined(XP_WIN) && defined(MOZ_METRO) |
|
344 static bool |
|
345 IsWindowsMetroUpdateRequest(int appArgc, char **appArgv) |
|
346 { |
|
347 for (int index = 0; index < appArgc; index++) { |
|
348 if (!strcmp(appArgv[index], "--metro-update")) { |
|
349 return true; |
|
350 } |
|
351 } |
|
352 return false; |
|
353 } |
|
354 #endif |
|
355 |
|
356 static bool |
|
357 CopyFileIntoUpdateDir(nsIFile *parentDir, const char *leafName, nsIFile *updateDir) |
|
358 { |
|
359 nsDependentCString leaf(leafName); |
|
360 nsCOMPtr<nsIFile> file; |
|
361 |
|
362 // Make sure there is not an existing file in the target location. |
|
363 nsresult rv = updateDir->Clone(getter_AddRefs(file)); |
|
364 if (NS_FAILED(rv)) |
|
365 return false; |
|
366 rv = file->AppendNative(leaf); |
|
367 if (NS_FAILED(rv)) |
|
368 return false; |
|
369 file->Remove(true); |
|
370 |
|
371 // Now, copy into the target location. |
|
372 rv = parentDir->Clone(getter_AddRefs(file)); |
|
373 if (NS_FAILED(rv)) |
|
374 return false; |
|
375 rv = file->AppendNative(leaf); |
|
376 if (NS_FAILED(rv)) |
|
377 return false; |
|
378 rv = file->CopyToNative(updateDir, EmptyCString()); |
|
379 if (NS_FAILED(rv)) |
|
380 return false; |
|
381 |
|
382 return true; |
|
383 } |
|
384 |
|
385 static bool |
|
386 CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir, |
|
387 nsCOMPtr<nsIFile> &updater) |
|
388 { |
|
389 // Copy the updater application from the GRE and the updater ini from the app |
|
390 #if defined(XP_MACOSX) |
|
391 if (!CopyFileIntoUpdateDir(greDir, kUpdaterApp, updateDir)) |
|
392 return false; |
|
393 #else |
|
394 if (!CopyFileIntoUpdateDir(greDir, kUpdaterBin, updateDir)) |
|
395 return false; |
|
396 #endif |
|
397 CopyFileIntoUpdateDir(appDir, kUpdaterINI, updateDir); |
|
398 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID) |
|
399 nsCOMPtr<nsIFile> iconDir; |
|
400 appDir->Clone(getter_AddRefs(iconDir)); |
|
401 iconDir->AppendNative(NS_LITERAL_CSTRING("icons")); |
|
402 if (!CopyFileIntoUpdateDir(iconDir, kUpdaterPNG, updateDir)) |
|
403 return false; |
|
404 #endif |
|
405 // Finally, return the location of the updater binary. |
|
406 nsresult rv = updateDir->Clone(getter_AddRefs(updater)); |
|
407 if (NS_FAILED(rv)) |
|
408 return false; |
|
409 #if defined(XP_MACOSX) |
|
410 rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterApp)); |
|
411 nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents")); |
|
412 if (NS_FAILED(tmp)) { |
|
413 rv = tmp; |
|
414 } |
|
415 tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS")); |
|
416 if (NS_FAILED(tmp) || NS_FAILED(rv)) |
|
417 return false; |
|
418 #endif |
|
419 rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin)); |
|
420 return NS_SUCCEEDED(rv); |
|
421 } |
|
422 |
|
423 /** |
|
424 * Switch an existing application directory to an updated version that has been |
|
425 * staged. |
|
426 * |
|
427 * @param greDir the GRE dir |
|
428 * @param updateDir the update root dir |
|
429 * @param statusFile the update.status file |
|
430 * @param appDir the app dir |
|
431 * @param appArgc the number of args to the application |
|
432 * @param appArgv the args to the application, used for restarting if needed |
|
433 */ |
|
434 static void |
|
435 SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, |
|
436 nsIFile *appDir, int appArgc, char **appArgv) |
|
437 { |
|
438 nsresult rv; |
|
439 |
|
440 // Steps: |
|
441 // - copy updater into temp dir |
|
442 // - run updater with the correct arguments |
|
443 |
|
444 nsCOMPtr<nsIFile> tmpDir; |
|
445 GetSpecialSystemDirectory(OS_TemporaryDirectory, |
|
446 getter_AddRefs(tmpDir)); |
|
447 if (!tmpDir) { |
|
448 LOG(("failed getting a temp dir\n")); |
|
449 return; |
|
450 } |
|
451 |
|
452 // Try to create our own new temp directory in case there is already an |
|
453 // updater binary in the OS temporary location which we cannot write to. |
|
454 // Note that we don't check for errors here, as if this directory can't |
|
455 // be created, the following CopyUpdaterIntoUpdateDir call will fail. |
|
456 // We create the unique directory inside a subfolder of MozUpdater instead |
|
457 // of directly in the temp directory so we can efficiently delete everything |
|
458 // after updates. |
|
459 tmpDir->Append(NS_LITERAL_STRING("MozUpdater")); |
|
460 tmpDir->Append(NS_LITERAL_STRING("bgupdate")); |
|
461 tmpDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755); |
|
462 |
|
463 nsCOMPtr<nsIFile> updater; |
|
464 if (!CopyUpdaterIntoUpdateDir(greDir, appDir, tmpDir, updater)) { |
|
465 LOG(("failed copying updater\n")); |
|
466 return; |
|
467 } |
|
468 |
|
469 // We need to use the value returned from XRE_GetBinaryPath when attempting |
|
470 // to restart the running application. |
|
471 nsCOMPtr<nsIFile> appFile; |
|
472 |
|
473 #if defined(XP_MACOSX) |
|
474 // On OS X we need to pass the location of the xulrunner-stub executable |
|
475 // rather than xulrunner-bin. See bug 349737. |
|
476 GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); |
|
477 #else |
|
478 XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); |
|
479 #endif |
|
480 |
|
481 if (!appFile) |
|
482 return; |
|
483 |
|
484 #ifdef XP_WIN |
|
485 nsAutoString appFilePathW; |
|
486 rv = appFile->GetPath(appFilePathW); |
|
487 if (NS_FAILED(rv)) |
|
488 return; |
|
489 NS_ConvertUTF16toUTF8 appFilePath(appFilePathW); |
|
490 |
|
491 nsAutoString updaterPathW; |
|
492 rv = updater->GetPath(updaterPathW); |
|
493 if (NS_FAILED(rv)) |
|
494 return; |
|
495 |
|
496 NS_ConvertUTF16toUTF8 updaterPath(updaterPathW); |
|
497 #else |
|
498 |
|
499 nsAutoCString appFilePath; |
|
500 #if defined(MOZ_WIDGET_GONK) |
|
501 appFilePath.Assign(kB2GServiceArgv[0]); |
|
502 appArgc = kB2GServiceArgc; |
|
503 appArgv = const_cast<char**>(kB2GServiceArgv); |
|
504 #else |
|
505 rv = appFile->GetNativePath(appFilePath); |
|
506 if (NS_FAILED(rv)) |
|
507 return; |
|
508 #endif |
|
509 |
|
510 nsAutoCString updaterPath; |
|
511 rv = updater->GetNativePath(updaterPath); |
|
512 if (NS_FAILED(rv)) |
|
513 return; |
|
514 #endif |
|
515 |
|
516 // Get the directory to which the update will be applied. On Mac OSX we need |
|
517 // to apply the update to the Updated.app directory under the Foo.app |
|
518 // directory which is the parent of the parent of the appDir. On other |
|
519 // platforms we will just apply to the appDir/updated. |
|
520 nsCOMPtr<nsIFile> updatedDir; |
|
521 #if defined(XP_MACOSX) |
|
522 nsAutoCString applyToDir; |
|
523 { |
|
524 nsCOMPtr<nsIFile> parentDir1, parentDir2; |
|
525 rv = appDir->GetParent(getter_AddRefs(parentDir1)); |
|
526 if (NS_FAILED(rv)) |
|
527 return; |
|
528 rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); |
|
529 if (NS_FAILED(rv)) |
|
530 return; |
|
531 if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) |
|
532 return; |
|
533 rv = updatedDir->GetNativePath(applyToDir); |
|
534 } |
|
535 #else |
|
536 if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) |
|
537 return; |
|
538 #if defined(XP_WIN) |
|
539 nsAutoString applyToDirW; |
|
540 rv = updatedDir->GetPath(applyToDirW); |
|
541 |
|
542 NS_ConvertUTF16toUTF8 applyToDir(applyToDirW); |
|
543 #else |
|
544 nsAutoCString applyToDir; |
|
545 rv = updatedDir->GetNativePath(applyToDir); |
|
546 #endif |
|
547 #endif |
|
548 if (NS_FAILED(rv)) |
|
549 return; |
|
550 |
|
551 // Make sure that the updated directory exists |
|
552 bool updatedDirExists = false; |
|
553 updatedDir->Exists(&updatedDirExists); |
|
554 if (!updatedDirExists) { |
|
555 return; |
|
556 } |
|
557 |
|
558 #if defined(XP_WIN) |
|
559 nsAutoString updateDirPathW; |
|
560 rv = updateDir->GetPath(updateDirPathW); |
|
561 |
|
562 NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW); |
|
563 #else |
|
564 nsAutoCString updateDirPath; |
|
565 rv = updateDir->GetNativePath(updateDirPath); |
|
566 #endif |
|
567 |
|
568 if (NS_FAILED(rv)) |
|
569 return; |
|
570 |
|
571 // Get the current working directory. |
|
572 char workingDirPath[MAXPATHLEN]; |
|
573 rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); |
|
574 if (NS_FAILED(rv)) |
|
575 return; |
|
576 |
|
577 // Construct the PID argument for this process. If we are using execv, then |
|
578 // we pass "0" which is then ignored by the updater. |
|
579 #if defined(USE_EXECV) |
|
580 nsAutoCString pid("0"); |
|
581 #else |
|
582 nsAutoCString pid; |
|
583 pid.AppendInt((int32_t) getpid()); |
|
584 #endif |
|
585 |
|
586 // Append a special token to the PID in order to let the updater know that it |
|
587 // just needs to replace the update directory. |
|
588 pid.AppendLiteral("/replace"); |
|
589 |
|
590 int immersiveArgc = 0; |
|
591 #if defined(XP_WIN) && defined(MOZ_METRO) |
|
592 // If this is desktop doing an update for metro, or if we're the metro browser |
|
593 // we want to launch the metro browser after we're finished. |
|
594 if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) { |
|
595 immersiveArgc = 1; |
|
596 } |
|
597 #endif |
|
598 int argc = appArgc + 5 + immersiveArgc; |
|
599 char **argv = new char*[argc + 1]; |
|
600 if (!argv) |
|
601 return; |
|
602 argv[0] = (char*) updaterPath.get(); |
|
603 argv[1] = (char*) updateDirPath.get(); |
|
604 argv[2] = (char*) applyToDir.get(); |
|
605 argv[3] = (char*) pid.get(); |
|
606 if (appArgc) { |
|
607 argv[4] = workingDirPath; |
|
608 argv[5] = (char*) appFilePath.get(); |
|
609 for (int i = 1; i < appArgc; ++i) |
|
610 argv[5 + i] = appArgv[i]; |
|
611 #ifdef XP_WIN |
|
612 if (immersiveArgc) { |
|
613 argv[argc - 1] = "-ServerName:DefaultBrowserServer"; |
|
614 } |
|
615 #endif |
|
616 argv[argc] = nullptr; |
|
617 } else { |
|
618 argc = 4; |
|
619 argv[4] = nullptr; |
|
620 } |
|
621 |
|
622 if (gSafeMode) { |
|
623 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); |
|
624 } |
|
625 |
|
626 #if defined(XP_WIN) |
|
627 nsresult rv2 = AdjustPathForUpdater(appDir); |
|
628 if (NS_FAILED(rv2)) { |
|
629 LOG(("SwitchToUpdatedApp -- AdjustPathForUpdater failed (0x%x)\n", rv2)); |
|
630 } |
|
631 #endif |
|
632 |
|
633 LOG(("spawning updater process for replacing [%s]\n", updaterPath.get())); |
|
634 |
|
635 #if defined(USE_EXECV) |
|
636 # if defined(MOZ_WIDGET_GONK) |
|
637 // In Gonk, we preload libmozglue, which the updater process doesn't need. |
|
638 // Since the updater will move and delete libmozglue.so, this can actually |
|
639 // stop the /system mount from correctly being remounted as read-only. |
|
640 unsetenv("LD_PRELOAD"); |
|
641 # endif |
|
642 execv(updaterPath.get(), argv); |
|
643 #elif defined(XP_WIN) |
|
644 // Switch the application using updater.exe |
|
645 if (!WinLaunchChild(updaterPathW.get(), argc, argv)) { |
|
646 return; |
|
647 } |
|
648 _exit(0); |
|
649 #elif defined(XP_MACOSX) |
|
650 CommandLineServiceMac::SetupMacCommandLine(argc, argv, true); |
|
651 // LaunchChildMac uses posix_spawnp and prefers the current |
|
652 // architecture when launching. It doesn't require a |
|
653 // null-terminated string but it doesn't matter if we pass one. |
|
654 LaunchChildMac(argc, argv); |
|
655 exit(0); |
|
656 #else |
|
657 PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr); |
|
658 exit(0); |
|
659 #endif |
|
660 } |
|
661 |
|
662 #if defined(MOZ_WIDGET_GONK) |
|
663 static nsresult |
|
664 GetOSApplyToDir(nsACString& applyToDir) |
|
665 { |
|
666 nsCOMPtr<nsIProperties> ds = |
|
667 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); |
|
668 NS_ASSERTION(ds, "Can't get directory service"); |
|
669 |
|
670 nsCOMPtr<nsIFile> osApplyToDir; |
|
671 nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile), |
|
672 getter_AddRefs(osApplyToDir)); |
|
673 if (NS_FAILED(rv)) { |
|
674 LOG(("Can't get the OS applyTo dir")); |
|
675 return rv; |
|
676 } |
|
677 |
|
678 return osApplyToDir->GetNativePath(applyToDir); |
|
679 } |
|
680 |
|
681 static void |
|
682 SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir) |
|
683 { |
|
684 nsresult rv; |
|
685 nsCOMPtr<nsIWritablePropertyBag> updateProperties = |
|
686 do_QueryInterface(update, &rv); |
|
687 |
|
688 if (NS_FAILED(rv)) { |
|
689 return; |
|
690 } |
|
691 |
|
692 nsCOMPtr<nsIWritableVariant> variant = |
|
693 do_CreateInstance("@mozilla.org/variant;1", &rv); |
|
694 if (NS_FAILED(rv)) { |
|
695 return; |
|
696 } |
|
697 |
|
698 rv = variant->SetAsACString(osApplyToDir); |
|
699 if (NS_FAILED(rv)) { |
|
700 return; |
|
701 } |
|
702 |
|
703 updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant); |
|
704 } |
|
705 #endif |
|
706 |
|
707 /** |
|
708 * Apply an update. This applies to both normal and staged updates. |
|
709 * |
|
710 * @param greDir the GRE dir |
|
711 * @param updateDir the update root dir |
|
712 * @param statusFile the update.status file |
|
713 * @param appDir the app dir |
|
714 * @param appArgc the number of args to the application |
|
715 * @param appArgv the args to the application, used for restarting if needed |
|
716 * @param restart if true, apply the update in the foreground and restart the |
|
717 * application when done. otherwise, stage the update and don't |
|
718 * restart the application. |
|
719 * @param outpid out parameter holding the handle to the updater application for |
|
720 * staging updates. |
|
721 */ |
|
722 static void |
|
723 ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile, |
|
724 nsIFile *appDir, int appArgc, char **appArgv, |
|
725 bool restart, bool isOSUpdate, nsIFile *osApplyToDir, |
|
726 ProcessType *outpid) |
|
727 { |
|
728 nsresult rv; |
|
729 |
|
730 // Steps: |
|
731 // - mark update as 'applying' |
|
732 // - copy updater into update dir |
|
733 // - run updater w/ appDir as the current working dir |
|
734 |
|
735 nsCOMPtr<nsIFile> updater; |
|
736 if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) { |
|
737 LOG(("failed copying updater\n")); |
|
738 return; |
|
739 } |
|
740 |
|
741 // We need to use the value returned from XRE_GetBinaryPath when attempting |
|
742 // to restart the running application. |
|
743 nsCOMPtr<nsIFile> appFile; |
|
744 |
|
745 #if defined(XP_MACOSX) |
|
746 // On OS X we need to pass the location of the xulrunner-stub executable |
|
747 // rather than xulrunner-bin. See bug 349737. |
|
748 GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile)); |
|
749 #else |
|
750 XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile)); |
|
751 #endif |
|
752 |
|
753 if (!appFile) |
|
754 return; |
|
755 |
|
756 #ifdef XP_WIN |
|
757 nsAutoString appFilePathW; |
|
758 rv = appFile->GetPath(appFilePathW); |
|
759 if (NS_FAILED(rv)) |
|
760 return; |
|
761 NS_ConvertUTF16toUTF8 appFilePath(appFilePathW); |
|
762 |
|
763 nsAutoString updaterPathW; |
|
764 rv = updater->GetPath(updaterPathW); |
|
765 if (NS_FAILED(rv)) |
|
766 return; |
|
767 |
|
768 NS_ConvertUTF16toUTF8 updaterPath(updaterPathW); |
|
769 |
|
770 #else |
|
771 nsAutoCString appFilePath; |
|
772 rv = appFile->GetNativePath(appFilePath); |
|
773 if (NS_FAILED(rv)) |
|
774 return; |
|
775 |
|
776 nsAutoCString updaterPath; |
|
777 rv = updater->GetNativePath(updaterPath); |
|
778 if (NS_FAILED(rv)) |
|
779 return; |
|
780 |
|
781 #endif |
|
782 |
|
783 // Get the directory to which the update will be applied. On Mac OSX we need |
|
784 // to apply the update to the Updated.app directory under the Foo.app |
|
785 // directory which is the parent of the parent of the appDir. On other |
|
786 // platforms we will just apply to the appDir/updated. |
|
787 nsCOMPtr<nsIFile> updatedDir; |
|
788 #if defined(XP_MACOSX) |
|
789 nsAutoCString applyToDir; |
|
790 { |
|
791 nsCOMPtr<nsIFile> parentDir1, parentDir2; |
|
792 rv = appDir->GetParent(getter_AddRefs(parentDir1)); |
|
793 if (NS_FAILED(rv)) |
|
794 return; |
|
795 rv = parentDir1->GetParent(getter_AddRefs(parentDir2)); |
|
796 if (NS_FAILED(rv)) |
|
797 return; |
|
798 if (restart) { |
|
799 // Use the correct directory if we're not staging the update. |
|
800 rv = parentDir2->GetNativePath(applyToDir); |
|
801 } else { |
|
802 if (!GetFile(parentDir2, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) |
|
803 return; |
|
804 rv = updatedDir->GetNativePath(applyToDir); |
|
805 } |
|
806 } |
|
807 #else |
|
808 if (restart) { |
|
809 // Use the correct directory if we're not staging the update. |
|
810 updatedDir = do_QueryInterface(appDir); |
|
811 } else if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) { |
|
812 return; |
|
813 } |
|
814 #if defined(XP_WIN) |
|
815 nsAutoString applyToDirW; |
|
816 rv = updatedDir->GetPath(applyToDirW); |
|
817 |
|
818 NS_ConvertUTF16toUTF8 applyToDir(applyToDirW); |
|
819 #else |
|
820 nsAutoCString applyToDir; |
|
821 |
|
822 #if defined(MOZ_WIDGET_GONK) |
|
823 if (isOSUpdate) { |
|
824 if (!osApplyToDir) { |
|
825 return; |
|
826 } |
|
827 |
|
828 rv = osApplyToDir->GetNativePath(applyToDir); |
|
829 } else { |
|
830 #endif // defined(MOZ_WIDGET_GONK) |
|
831 |
|
832 rv = updatedDir->GetNativePath(applyToDir); |
|
833 |
|
834 #if defined(MOZ_WIDGET_GONK) |
|
835 } |
|
836 #endif // defined(MOZ_WIDGET_GONK) |
|
837 |
|
838 #endif // defined(XP_WIN) |
|
839 #endif |
|
840 |
|
841 if (NS_FAILED(rv)) |
|
842 return; |
|
843 |
|
844 #if defined(XP_WIN) |
|
845 nsAutoString updateDirPathW; |
|
846 rv = updateDir->GetPath(updateDirPathW); |
|
847 |
|
848 NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW); |
|
849 #else |
|
850 nsAutoCString updateDirPath; |
|
851 rv = updateDir->GetNativePath(updateDirPath); |
|
852 #endif |
|
853 |
|
854 if (NS_FAILED(rv)) |
|
855 return; |
|
856 |
|
857 // Get the current working directory. |
|
858 char workingDirPath[MAXPATHLEN]; |
|
859 rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath)); |
|
860 if (NS_FAILED(rv)) |
|
861 return; |
|
862 |
|
863 // We used to write out "Applying" to the update.status file here. |
|
864 // Instead we do this from within the updater application now. |
|
865 // This is so that we don't overwrite the status of pending-service |
|
866 // in the Windows case. This change was made for all platforms so |
|
867 // that it stays consistent across all OS. |
|
868 |
|
869 // Construct the PID argument for this process. If we are using execv, then |
|
870 // we pass "0" which is then ignored by the updater. |
|
871 nsAutoCString pid; |
|
872 if (!restart) { |
|
873 // Signal the updater application that it should stage the update. |
|
874 pid.AssignASCII("-1"); |
|
875 } else { |
|
876 #if defined(USE_EXECV) |
|
877 pid.AssignASCII("0"); |
|
878 #else |
|
879 pid.AppendInt((int32_t) getpid()); |
|
880 #endif |
|
881 } |
|
882 |
|
883 int immersiveArgc = 0; |
|
884 #if defined(XP_WIN) && defined(MOZ_METRO) |
|
885 // If this is desktop doing an update for metro, or if we're the metro browser |
|
886 // we want to launch the metro browser after we're finished. |
|
887 if (IsWindowsMetroUpdateRequest(appArgc, appArgv) || IsRunningInWindowsMetro()) { |
|
888 immersiveArgc = 1; |
|
889 } |
|
890 #endif |
|
891 int argc = appArgc + 5 + immersiveArgc; |
|
892 char **argv = new char*[argc + 1 ]; |
|
893 if (!argv) |
|
894 return; |
|
895 argv[0] = (char*) updaterPath.get(); |
|
896 argv[1] = (char*) updateDirPath.get(); |
|
897 argv[2] = (char*) applyToDir.get(); |
|
898 argv[3] = (char*) pid.get(); |
|
899 if (restart && appArgc) { |
|
900 argv[4] = workingDirPath; |
|
901 argv[5] = (char*) appFilePath.get(); |
|
902 for (int i = 1; i < appArgc; ++i) |
|
903 argv[5 + i] = appArgv[i]; |
|
904 #ifdef XP_WIN |
|
905 if (immersiveArgc) { |
|
906 argv[argc - 1] = "-ServerName:DefaultBrowserServer"; |
|
907 } |
|
908 #endif |
|
909 argv[argc] = nullptr; |
|
910 } else { |
|
911 argc = 4; |
|
912 argv[4] = nullptr; |
|
913 } |
|
914 |
|
915 if (gSafeMode) { |
|
916 PR_SetEnv("MOZ_SAFE_MODE_RESTART=1"); |
|
917 } |
|
918 |
|
919 if (isOSUpdate) { |
|
920 PR_SetEnv("MOZ_OS_UPDATE=1"); |
|
921 } |
|
922 |
|
923 #if defined(XP_WIN) |
|
924 nsresult rv2 = AdjustPathForUpdater(appDir); |
|
925 if (NS_FAILED(rv2)) { |
|
926 LOG(("ApplyUpdate -- AdjustPathForUpdater failed (0x%x)\n", rv2)); |
|
927 } |
|
928 #endif |
|
929 |
|
930 #if defined(MOZ_WIDGET_GONK) |
|
931 // We want the updater to be CPU friendly and not subject to being killed by |
|
932 // the low memory killer, so we pass in some preferences to allow it to |
|
933 // adjust its priority. |
|
934 |
|
935 int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio, |
|
936 kAppUpdaterPrioDefault); |
|
937 int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj, |
|
938 kAppUpdaterOomScoreAdjDefault); |
|
939 int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass, |
|
940 kAppUpdaterIOPrioClassDefault); |
|
941 int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel, |
|
942 kAppUpdaterIOPrioLevelDefault); |
|
943 nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d", |
|
944 prioVal, oomScoreAdj, ioprioClass, ioprioLevel); |
|
945 PR_SetEnv(prioEnv.get()); |
|
946 #endif |
|
947 |
|
948 LOG(("spawning updater process [%s]\n", updaterPath.get())); |
|
949 #ifdef DEBUG |
|
950 dump_argv("ApplyUpdate updater", argv, argc); |
|
951 #endif |
|
952 |
|
953 #if defined(USE_EXECV) |
|
954 // Don't use execv when staging updates. |
|
955 if (restart) { |
|
956 execv(updaterPath.get(), argv); |
|
957 } else { |
|
958 *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); |
|
959 } |
|
960 #elif defined(XP_WIN) |
|
961 // Launch the update using updater.exe |
|
962 if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) { |
|
963 return; |
|
964 } |
|
965 |
|
966 if (restart) { |
|
967 // We are going to process an update so we should exit now |
|
968 _exit(0); |
|
969 } |
|
970 #elif defined(XP_MACOSX) |
|
971 CommandLineServiceMac::SetupMacCommandLine(argc, argv, true); |
|
972 // LaunchChildMac uses posix_spawnp and prefers the current |
|
973 // architecture when launching. It doesn't require a |
|
974 // null-terminated string but it doesn't matter if we pass one. |
|
975 #ifdef DEBUG |
|
976 dump_argv("ApplyUpdate after SetupMacCommandLine", argv, argc); |
|
977 #endif |
|
978 LaunchChildMac(argc, argv, 0, outpid); |
|
979 if (restart) { |
|
980 exit(0); |
|
981 } |
|
982 #else |
|
983 *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr); |
|
984 if (restart) { |
|
985 exit(0); |
|
986 } |
|
987 #endif |
|
988 } |
|
989 |
|
990 /** |
|
991 * Wait for a process until it terminates. This call is blocking. |
|
992 */ |
|
993 static void |
|
994 WaitForProcess(ProcessType pt) |
|
995 { |
|
996 #if defined(XP_WIN) |
|
997 WaitForSingleObject(pt, INFINITE); |
|
998 CloseHandle(pt); |
|
999 #elif defined(XP_MACOSX) |
|
1000 waitpid(pt, 0, 0); |
|
1001 #else |
|
1002 int32_t exitCode; |
|
1003 PR_WaitProcess(pt, &exitCode); |
|
1004 if (exitCode != 0) { |
|
1005 LOG(("Error while running the updater process, check update.log")); |
|
1006 } |
|
1007 #endif |
|
1008 } |
|
1009 |
|
1010 nsresult |
|
1011 ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir, |
|
1012 int argc, char **argv, const char *appVersion, |
|
1013 bool restart, bool isOSUpdate, nsIFile *osApplyToDir, |
|
1014 ProcessType *pid) |
|
1015 { |
|
1016 nsresult rv; |
|
1017 |
|
1018 nsCOMPtr<nsIFile> updatesDir; |
|
1019 #ifdef DEBUG |
|
1020 nsAutoCString path; |
|
1021 updRootDir->GetNativePath(path); |
|
1022 printf("ProcessUpdates updateRootDir: %s appVersion: %s\n", |
|
1023 path.get(), appVersion); |
|
1024 #endif |
|
1025 rv = updRootDir->Clone(getter_AddRefs(updatesDir)); |
|
1026 if (NS_FAILED(rv)) |
|
1027 return rv; |
|
1028 |
|
1029 rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates")); |
|
1030 if (NS_FAILED(rv)) |
|
1031 return rv; |
|
1032 |
|
1033 rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0")); |
|
1034 if (NS_FAILED(rv)) |
|
1035 return rv; |
|
1036 |
|
1037 ProcessType dummyPID; // this will only be used for MOZ_UPDATE_STAGING |
|
1038 const char *processingUpdates = PR_GetEnv("MOZ_PROCESS_UPDATES"); |
|
1039 if (processingUpdates && *processingUpdates) { |
|
1040 // Enable the tests to request an update to be staged. |
|
1041 const char *stagingUpdate = PR_GetEnv("MOZ_UPDATE_STAGING"); |
|
1042 if (stagingUpdate && *stagingUpdate) { |
|
1043 restart = false; |
|
1044 pid = &dummyPID; |
|
1045 } |
|
1046 } |
|
1047 |
|
1048 nsCOMPtr<nsIFile> statusFile; |
|
1049 UpdateStatus status = GetUpdateStatus(updatesDir, statusFile); |
|
1050 #ifdef DEBUG |
|
1051 printf("ProcessUpdates status: %d\n", status); |
|
1052 updatesDir->GetNativePath(path); |
|
1053 printf("ProcessUpdates updatesDir: %s\n", path.get()); |
|
1054 #endif |
|
1055 switch (status) { |
|
1056 case ePendingUpdate: |
|
1057 case ePendingService: { |
|
1058 nsCOMPtr<nsIFile> versionFile; |
|
1059 // Remove the update if the update application version file doesn't exist |
|
1060 // or if the update's application version is less than the current |
|
1061 // application version. |
|
1062 if (!GetVersionFile(updatesDir, versionFile) || |
|
1063 IsOlderVersion(versionFile, appVersion)) { |
|
1064 updatesDir->Remove(true); |
|
1065 } else { |
|
1066 ApplyUpdate(greDir, updatesDir, statusFile, |
|
1067 appDir, argc, argv, restart, isOSUpdate, osApplyToDir, pid); |
|
1068 } |
|
1069 break; |
|
1070 } |
|
1071 case eAppliedUpdate: |
|
1072 case eAppliedService: |
|
1073 // An update was staged and needs to be switched so the updated application |
|
1074 // is used. |
|
1075 SwitchToUpdatedApp(greDir, updatesDir, statusFile, |
|
1076 appDir, argc, argv); |
|
1077 break; |
|
1078 case eNoUpdateAction: |
|
1079 // We don't need to do any special processing here, we'll just continue to |
|
1080 // startup the application. |
|
1081 break; |
|
1082 } |
|
1083 |
|
1084 return NS_OK; |
|
1085 } |
|
1086 |
|
1087 |
|
1088 |
|
1089 NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor) |
|
1090 |
|
1091 nsUpdateProcessor::nsUpdateProcessor() |
|
1092 : mUpdaterPID(0) |
|
1093 { |
|
1094 } |
|
1095 |
|
1096 NS_IMETHODIMP |
|
1097 nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate) |
|
1098 { |
|
1099 nsCOMPtr<nsIFile> greDir, appDir, updRoot; |
|
1100 nsAutoCString appVersion; |
|
1101 int argc; |
|
1102 char **argv; |
|
1103 |
|
1104 nsAutoCString binPath; |
|
1105 nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton(); |
|
1106 if (dirProvider) { // Normal code path |
|
1107 // Check for and process any available updates |
|
1108 bool persistent; |
|
1109 nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK |
|
1110 #ifdef MOZ_WIDGET_GONK |
|
1111 // Check in the sdcard for updates first, since that's our preferred |
|
1112 // download location. |
|
1113 rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent, |
|
1114 getter_AddRefs(updRoot)); |
|
1115 #endif |
|
1116 if (NS_FAILED(rv)) { |
|
1117 rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent, |
|
1118 getter_AddRefs(updRoot)); |
|
1119 } |
|
1120 // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed |
|
1121 if (NS_FAILED(rv)) |
|
1122 updRoot = dirProvider->GetAppDir(); |
|
1123 |
|
1124 greDir = dirProvider->GetGREDir(); |
|
1125 nsCOMPtr<nsIFile> exeFile; |
|
1126 rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent, |
|
1127 getter_AddRefs(exeFile)); |
|
1128 if (NS_SUCCEEDED(rv)) |
|
1129 rv = exeFile->GetParent(getter_AddRefs(appDir)); |
|
1130 |
|
1131 if (NS_FAILED(rv)) |
|
1132 appDir = dirProvider->GetAppDir(); |
|
1133 |
|
1134 #ifdef TOR_BROWSER_UPDATE |
|
1135 appVersion = TOR_BROWSER_VERSION; |
|
1136 #else |
|
1137 appVersion = gAppData->version; |
|
1138 #endif |
|
1139 argc = gRestartArgc; |
|
1140 argv = gRestartArgv; |
|
1141 } else { |
|
1142 // In the xpcshell environment, the usual XRE_main is not run, so things |
|
1143 // like dirProvider and gAppData do not exist. This code path accesses |
|
1144 // XPCOM (which is not available in the previous code path) in order to get |
|
1145 // the same information. |
|
1146 nsCOMPtr<nsIProperties> ds = |
|
1147 do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); |
|
1148 if (!ds) { |
|
1149 NS_ABORT(); // There's nothing which we can do if this fails! |
|
1150 } |
|
1151 |
|
1152 nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), |
|
1153 getter_AddRefs(greDir)); |
|
1154 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir"); |
|
1155 appDir = greDir; |
|
1156 |
|
1157 rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile), |
|
1158 getter_AddRefs(updRoot)); |
|
1159 if (NS_FAILED(rv)) |
|
1160 updRoot = appDir; |
|
1161 |
|
1162 // To support Tor Browser Bundle updates from xpcshell, modify the |
|
1163 // following code to use the TBB version fron the configure process. |
|
1164 nsCOMPtr<nsIXULAppInfo> appInfo = |
|
1165 do_GetService("@mozilla.org/xre/app-info;1"); |
|
1166 if (appInfo) { |
|
1167 rv = appInfo->GetVersion(appVersion); |
|
1168 NS_ENSURE_SUCCESS(rv, rv); |
|
1169 } else { |
|
1170 appVersion = MOZ_APP_VERSION; |
|
1171 } |
|
1172 |
|
1173 // We need argv[0] to point to the current executable's name. The rest of |
|
1174 // the entries in this array will be ignored if argc<2. Therefore, for |
|
1175 // xpcshell, we only fill out that item, and leave the rest empty. |
|
1176 argc = 1; |
|
1177 nsCOMPtr<nsIFile> binary; |
|
1178 rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile), |
|
1179 getter_AddRefs(binary)); |
|
1180 NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path"); |
|
1181 binary->GetNativePath(binPath); |
|
1182 } |
|
1183 |
|
1184 // Copy the parameters to the StagedUpdateInfo structure shared with the |
|
1185 // watcher thread. |
|
1186 mInfo.mGREDir = greDir; |
|
1187 mInfo.mAppDir = appDir; |
|
1188 mInfo.mUpdateRoot = updRoot; |
|
1189 mInfo.mArgc = argc; |
|
1190 mInfo.mArgv = new char*[argc]; |
|
1191 if (dirProvider) { |
|
1192 for (int i = 0; i < argc; ++i) { |
|
1193 const size_t length = strlen(argv[i]); |
|
1194 mInfo.mArgv[i] = new char[length + 1]; |
|
1195 strcpy(mInfo.mArgv[i], argv[i]); |
|
1196 } |
|
1197 } else { |
|
1198 MOZ_ASSERT(argc == 1); // see above |
|
1199 const size_t length = binPath.Length(); |
|
1200 mInfo.mArgv[0] = new char[length + 1]; |
|
1201 strcpy(mInfo.mArgv[0], binPath.get()); |
|
1202 } |
|
1203 mInfo.mAppVersion = appVersion; |
|
1204 |
|
1205 #if defined(MOZ_WIDGET_GONK) |
|
1206 NS_ENSURE_ARG_POINTER(aUpdate); |
|
1207 |
|
1208 bool isOSUpdate; |
|
1209 if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) && |
|
1210 isOSUpdate) { |
|
1211 nsAutoCString osApplyToDir; |
|
1212 |
|
1213 // This needs to be done on the main thread, so we pass it along in |
|
1214 // BackgroundThreadInfo |
|
1215 nsresult rv = GetOSApplyToDir(osApplyToDir); |
|
1216 if (NS_FAILED(rv)) { |
|
1217 LOG(("Can't get the OS apply to dir")); |
|
1218 return rv; |
|
1219 } |
|
1220 |
|
1221 SetOSApplyToDir(aUpdate, osApplyToDir); |
|
1222 |
|
1223 mInfo.mIsOSUpdate = true; |
|
1224 rv = NS_NewNativeLocalFile(osApplyToDir, false, |
|
1225 getter_AddRefs(mInfo.mOSApplyToDir)); |
|
1226 if (NS_FAILED(rv)) { |
|
1227 LOG(("Can't create nsIFile for OS apply to dir")); |
|
1228 return rv; |
|
1229 } |
|
1230 } |
|
1231 #endif |
|
1232 |
|
1233 mUpdate = aUpdate; |
|
1234 |
|
1235 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); |
|
1236 return NS_NewThread(getter_AddRefs(mProcessWatcher), |
|
1237 NS_NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate)); |
|
1238 } |
|
1239 |
|
1240 |
|
1241 |
|
1242 void |
|
1243 nsUpdateProcessor::StartStagedUpdate() |
|
1244 { |
|
1245 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread"); |
|
1246 |
|
1247 nsresult rv = ProcessUpdates(mInfo.mGREDir, |
|
1248 mInfo.mAppDir, |
|
1249 mInfo.mUpdateRoot, |
|
1250 mInfo.mArgc, |
|
1251 mInfo.mArgv, |
|
1252 mInfo.mAppVersion.get(), |
|
1253 false, |
|
1254 mInfo.mIsOSUpdate, |
|
1255 mInfo.mOSApplyToDir, |
|
1256 &mUpdaterPID); |
|
1257 NS_ENSURE_SUCCESS_VOID(rv); |
|
1258 |
|
1259 if (mUpdaterPID) { |
|
1260 // Track the state of the updater process while it is staging an update. |
|
1261 rv = NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess)); |
|
1262 NS_ENSURE_SUCCESS_VOID(rv); |
|
1263 } else { |
|
1264 // Failed to launch the updater process for some reason. |
|
1265 // We need to shutdown the current thread as there isn't anything more for |
|
1266 // us to do... |
|
1267 rv = NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread)); |
|
1268 NS_ENSURE_SUCCESS_VOID(rv); |
|
1269 } |
|
1270 } |
|
1271 |
|
1272 void |
|
1273 nsUpdateProcessor::ShutdownWatcherThread() |
|
1274 { |
|
1275 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); |
|
1276 mProcessWatcher->Shutdown(); |
|
1277 mProcessWatcher = nullptr; |
|
1278 mUpdate = nullptr; |
|
1279 } |
|
1280 |
|
1281 void |
|
1282 nsUpdateProcessor::WaitForProcess() |
|
1283 { |
|
1284 NS_ABORT_IF_FALSE(!NS_IsMainThread(), "main thread"); |
|
1285 ::WaitForProcess(mUpdaterPID); |
|
1286 NS_DispatchToMainThread(NS_NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone)); |
|
1287 } |
|
1288 |
|
1289 void |
|
1290 nsUpdateProcessor::UpdateDone() |
|
1291 { |
|
1292 NS_ABORT_IF_FALSE(NS_IsMainThread(), "not main thread"); |
|
1293 |
|
1294 nsCOMPtr<nsIUpdateManager> um = |
|
1295 do_GetService("@mozilla.org/updates/update-manager;1"); |
|
1296 if (um && mUpdate) { |
|
1297 um->RefreshUpdateStatus(mUpdate); |
|
1298 } |
|
1299 |
|
1300 ShutdownWatcherThread(); |
|
1301 } |
|
1302 |