Bug 600777: Update driver code should launch updater application with a preference for the parent architecture. r=rstrong a=blocking-beta7
authorJosh Aas <joshmoz@gmail.com>
Sat, 02 Oct 2010 00:11:24 -0400
changeset 54891 861afa477ababd8b80b398e9eb3bd1ba30d1e199
parent 54890 ec3316fdc36044df94d9baee2717cf16bf955850
child 54892 7a1028b9d42ffd7ec79595dfdcbf9e399dceeaf6
push id16061
push userjosh@mozilla.com
push dateSat, 02 Oct 2010 04:12:12 +0000
treeherdermozilla-central@861afa477aba [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong, blocking-beta7
bugs600777
milestone2.0b7pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 600777: Update driver code should launch updater application with a preference for the parent architecture. r=rstrong a=blocking-beta7
toolkit/mozapps/update/test/unit/head_update.js.in
toolkit/mozapps/update/test/unit/test_0110_general.js
toolkit/mozapps/update/test/unit/test_0111_general.js
toolkit/mozapps/update/test/unit/test_0112_general.js
toolkit/mozapps/update/updater/updater.cpp
toolkit/xre/nsUpdateDriver.cpp
--- a/toolkit/mozapps/update/test/unit/head_update.js.in
+++ b/toolkit/mozapps/update/test/unit/head_update.js.in
@@ -149,17 +149,17 @@ function runUpdate(aUpdatesDir, aUpdater
 
   var cwdPath = do_get_file("/", true).path;
   if (/ /.test(cwdPath))
     cwdPath = '"' + cwdPath + '"';
 
   var process = AUS_Cc["@mozilla.org/process/util;1"].
                 createInstance(AUS_Ci.nsIProcess);
   process.init(updateBin);
-  var args = [updatesDirPath, 0, cwdPath];
+  var args = [updatesDirPath, cwdPath];
   process.run(true, args, args.length);
   return process.exitValue;
 }
 
 /**
  * Sets up the bare bones XMLHttpRequest implementation below. 
  *
  * @param   callback
--- a/toolkit/mozapps/update/test/unit/test_0110_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0110_general.js
@@ -82,22 +82,16 @@ var gTestFiles = [
   compareContents  : "ToBeDeleted\n",
   originalFile     : null,
   compareFile      : null,
   originalPerms    : 0767,
   comparePerms     : 0644
 }];
 
 function run_test() {
-  var isOSX = ("nsILocalFileMac" in Components.interfaces);
-  if (isOSX) {
-    dump("INFO | test_0110_general.js | Skipping test on mac, bug 599477")
-    return;
-  }
-
   var testFile;
   // The directory the updates will be applied to is the current working
   // directory and not dist/bin.
   var testDir = do_get_file("mar_test", true);
   // The mar files were created with all files in a subdirectory named
   // mar_test... clear it out of the way if it exists and then create it.
   try {
     removeDirRecursive(testDir);
--- a/toolkit/mozapps/update/test/unit/test_0111_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0111_general.js
@@ -100,22 +100,16 @@ var gTestFiles = [
   compareContents  : "Added\n",
   originalFile     : null,
   compareFile      : null,
   originalPerms    : null,
   comparePerms     : 0644
 }];
 
 function run_test() {
-  var isOSX = ("nsILocalFileMac" in Components.interfaces);
-  if (isOSX) {
-    dump("INFO | test_0111_general.js | Skipping test on mac, bug 599477")
-    return;
-  }
-
   var testFile;
   // The directory the updates will be applied to is the current working
   // directory and not dist/bin.
   var testDir = do_get_file("mar_test", true);
   // The mar files were created with all files in a subdirectory named
   // mar_test... clear it out of the way if it exists and then create it.
   try {
     removeDirRecursive(testDir);
--- a/toolkit/mozapps/update/test/unit/test_0112_general.js
+++ b/toolkit/mozapps/update/test/unit/test_0112_general.js
@@ -82,22 +82,16 @@ var gTestFiles = [
   compareContents  : "ShouldNotBeDeleted\n",
   originalFile     : null,
   compareFile      : null,
   originalPerms    : 0644,
   comparePerms     : null
 }];
 
 function run_test() {
-  var isOSX = ("nsILocalFileMac" in Components.interfaces);
-  if (isOSX) {
-    dump("INFO | test_0112_general.js | Skipping test on mac, bug 599477")
-    return;
-  }
-
   var testFile;
   // The directory the updates will be applied to is the current working
   // directory and not dist/bin.
   var testDir = do_get_file("mar_test", true);
   // The mar files were created with all files in a subdirectory named
   // mar_test... clear it out of the way if it exists and then create it.
   try {
     removeDirRecursive(testDir);
--- a/toolkit/mozapps/update/updater/updater.cpp
+++ b/toolkit/mozapps/update/updater/updater.cpp
@@ -1461,81 +1461,88 @@ UpdateThreadFunc(void *param)
 
   LOG(("calling QuitProgressUI\n"));
   QuitProgressUI();
 }
 
 int NS_main(int argc, NS_tchar **argv)
 {
   InitProgressUI(&argc, &argv);
-  // The updater command line consists of the directory path containing the
-  // updater.mar file to process followed by the PID of the calling process.
-  // The updater will wait on the parent process to exit if the PID is non-
-  // zero.  This is leveraged on platforms such as Windows where it is
-  // necessary for the parent process to exit before its executable image may
-  // be altered.
 
-#ifndef WINCE
-  if (argc < 2) {
-    fprintf(stderr, "Usage: updater <dir-path> [parent-pid [working-dir callback args...]]\n");
+  // To process an update the updater command line must at a minimum have the
+  // directory path containing the updater.mar file to process as the first argument
+  // and the directory to apply the update to as the second argument. When the
+  // updater is launched by another process the PID of the parent process should be
+  // provided in the optional third argument and the updater will wait on the parent
+  // process to exit if the value is non-zero and the process is present. This is
+  // necessary due to not being able to update files that are in use on Windows. The
+  // optional fourth argument is the callback's working directory and the optional
+  // fifth argument is the callback path. The callback is the application to launch
+  // after  updating and it will be launched when these arguments are provided
+  // whether the update was successful or not. All remaining arguments are optional
+  // and are passed to the callback when it is launched.
+  if (argc < 3) {
+    fprintf(stderr, "Usage: updater update-dir apply-to-dir [wait-pid [callback-working-dir callback-path args...]]\n");
     return 1;
   }
-#else
-  if (argc < 4) {
-    fprintf(stderr, "Usage: updater <dir-path> parent-pid <working-dir> [callback args...]]\n");
+
+  // Change current directory to the directory where we need to apply the update.
+#ifndef WINCE
+  if (NS_tchdir(argv[2]) != 0) {
     return 1;
   }
 #endif
 
-  if (argc > 2 ) {
+  // If there is a PID specified and it is not '0' then wait for the process to exit.
+  if (argc > 3) {
 #ifdef XP_WIN
-    __int64 pid = _wtoi64(argv[2]);
+    __int64 pid = _wtoi64(argv[3]);
 #else
-    int pid = atoi(argv[2]);
+    int pid = atoi(argv[3]);
 #endif
-    if (pid) {
+    if (pid != 0) {
 #ifdef XP_WIN
       HANDLE parent = OpenProcess(SYNCHRONIZE, FALSE, (DWORD) pid);
       // May return NULL if the parent process has already gone away.
       // Otherwise, wait for the parent process to exit before starting the
       // update.
       if (parent) {
         DWORD result = WaitForSingleObject(parent, 5000);
         CloseHandle(parent);
         if (result != WAIT_OBJECT_0)
           return 1;
       }
 
       // The process may be signaled before it releases the executable image.
       // This is a terrible hack, but it'll have to do for now :-(
       Sleep(50);
 #else
-      int status;
-      waitpid(pid, &status, 0);
+      waitpid(pid, NULL, 0);
 #endif
     }
   }
 
-  // The callback is the last N command line arguments starting from argOffset.
-  // The argument specified by argOffset is the callback executable and the
-  // argument prior to argOffset is the working directory.
-  const int argOffset = 4;
+  // The directory containing the update information.
+  gSourcePath = argv[1];
 
-  gSourcePath = argv[1];
+  // The callback is the remaining arguments starting at callbackIndex.
+  // The argument specified by callbackIndex is the callback executable and the
+  // argument prior to callbackIndex is the working directory.
+  const int callbackIndex = 5;
 
 #if defined(XP_WIN) && !defined(WINCE)
   // Launch a second instance of the updater with the runas verb on Windows
   // when write access is denied to the installation directory.
   HANDLE updateLockFileHandle;
   NS_tchar elevatedLockFilePath[MAXPATHLEN];
-  if (argc > argOffset) {
+  if (argc > callbackIndex) {
     NS_tchar updateLockFilePath[MAXPATHLEN];
     NS_tsnprintf(updateLockFilePath,
                  sizeof(updateLockFilePath)/sizeof(updateLockFilePath[0]),
-                 NS_T("%s.update_in_progress.lock"), argv[argOffset]);
+                 NS_T("%s.update_in_progress.lock"), argv[callbackIndex]);
 
     // The update_in_progress.lock file should only exist during an update. In
     // case it exists attempt to remove it and exit if that fails to prevent
     // simultaneous updates occurring.
     if (!_waccess(updateLockFilePath, F_OK) &&
         NS_tremove(updateLockFilePath) != 0) {
       fprintf(stderr, "Update already in progress! Exiting\n");
       return 1;
@@ -1597,88 +1604,83 @@ int NS_main(int argc, NS_tchar **argv)
 
       if (result) {
         WaitForSingleObject(sinfo.hProcess, INFINITE);
         CloseHandle(sinfo.hProcess);
       } else {
         WriteStatusFile(ELEVATION_CANCELED);
       }
 
-      if (argc > argOffset) {
-        LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
+      if (argc > callbackIndex) {
+        LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
       }
 
       CloseHandle(elevatedFileHandle);
       return 0;
     }
   }
 #endif
 
   LogInit();
-  LOG(("SOURCE DIRECTORY " LOG_S "\n", gSourcePath));
-
-  // The destination directory (the same as the working-dir argument) does not
-  // have to be specified when updating manually.
-  if (argc > argOffset - 1) {
-    LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[3]));
-  }
+  LOG(("SOURCE DIRECTORY " LOG_S "\n", argv[1]));
+  LOG(("DESTINATION DIRECTORY " LOG_S "\n", argv[2]));
 
 #ifdef WINCE
   // This is the working directory to apply the update and is required on WinCE
   // since it doesn't have the concept of a working directory.
-  gDestPath = argv[3];
+  gDestPath = argv[2];
 #endif
 
 #ifdef XP_WIN
   HANDLE callbackFile = INVALID_HANDLE_VALUE;
   NS_tchar callbackBackupPath[MAXPATHLEN];
-  if (argc > argOffset) {
+  if (argc > callbackIndex) {
     // FindFirstFileW is used to get the callback's filename for comparison
     // with the callback's patch since it will return the correct case and the
     // long name instead of the 8.3 format name.
     HANDLE hFindFile;
-    hFindFile = FindFirstFileW(argv[argOffset], &gFFData);
+    hFindFile = FindFirstFileW(argv[callbackIndex], &gFFData);
     if (hFindFile == INVALID_HANDLE_VALUE) {
-      LOG(("NS_main: unable to find callback file: " LOG_S "\n", argv[argOffset]));
+      LOG(("NS_main: unable to find callback file: " LOG_S "\n", argv[callbackIndex]));
       LogFinish();
       WriteStatusFile(WRITE_ERROR);
       EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
-      LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
+      LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
       return 1;
     }
     FindClose(hFindFile);
 
     // Make a copy of the callback executable.
     NS_tsnprintf(callbackBackupPath,
                  sizeof(callbackBackupPath)/sizeof(callbackBackupPath[0]),
-                 NS_T("%s" CALLBACK_BACKUP_EXT), argv[argOffset]);
+                 NS_T("%s" CALLBACK_BACKUP_EXT), argv[callbackIndex]);
     NS_tremove(callbackBackupPath);
-    CopyFileW(argv[argOffset], callbackBackupPath, FALSE);
+    CopyFileW(argv[callbackIndex], callbackBackupPath, FALSE);
 
     // By opening a file handle to the callback executable, the OS will prevent
     // launching the process while it is being updated. 
-    callbackFile = CreateFileW(argv[argOffset],
+    callbackFile = CreateFileW(argv[callbackIndex],
 #ifdef WINCE
                                GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
 #else
                                DELETE | GENERIC_WRITE,
                                0, // no sharing!
 #endif
                                NULL, OPEN_EXISTING, 0, NULL);
     // CreateFileW will fail if the callback executable is already in use. Since
     // it isn't possible to update write the status file and return.
     if (callbackFile == INVALID_HANDLE_VALUE) {
       LOG(("NS_main: file in use - failed to exclusively open executable " \
-           "file: " LOG_S "\n", argv[argOffset]));
+           "file: " LOG_S "\n", argv[callbackIndex]));
       LogFinish();
       WriteStatusFile(WRITE_ERROR);
       NS_tremove(callbackBackupPath);
       EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
-      LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
+      LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
       return 1;
     }
   }
 #endif
 
   BigBuffer = (char *)malloc(BigBufferSize);
   if (!BigBuffer) {
     LOG(("NS_main: failed to allocate default buffer of %i. Trying 1K " \
@@ -1691,58 +1693,59 @@ int NS_main(int argc, NS_tchar **argv)
       LOG(("NS_main: failed to allocate 1K buffer - exiting\n"));
       LogFinish();
       WriteStatusFile(MEM_ERROR);
 #ifdef XP_WIN
       CloseHandle(callbackFile);
       NS_tremove(callbackBackupPath);
       EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 1);
 #endif
-      LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
+      if (argc > callbackIndex)
+        LaunchCallbackApp(argv[4], argc - callbackIndex, argv + callbackIndex);
       return 1;
     }
   }
 
   // Run update process on a background thread.  ShowProgressUI may return
   // before QuitProgressUI has been called, so wait for UpdateThreadFunc to
   // terminate.
   Thread t;
   if (t.Run(UpdateThreadFunc, NULL) == 0) {
     ShowProgressUI();
   }
   t.Join();
 
 #ifdef XP_WIN
-  if (argc > argOffset) {
+  if (argc > callbackIndex) {
     CloseHandle(callbackFile);
     // CopyFile will preserve the case of the destination file if it already
     // exists.
-    if (CopyFileW(callbackBackupPath, argv[argOffset], FALSE) != 0) {
+    if (CopyFileW(callbackBackupPath, argv[callbackIndex], FALSE) != 0) {
       NS_tremove(callbackBackupPath);
     }
   }
 #endif
 
   LogFinish();
   free(BigBuffer);
   BigBuffer = NULL;
 
-  if (argc > argOffset) {
+  if (argc > callbackIndex) {
 #if defined(XP_WIN) && !defined(WINCE)
     if (gSucceeded) {
-      LaunchWinPostProcess(argv[argOffset]);
+      LaunchWinPostProcess(argv[callbackIndex]);
     }
     EXIT_WHEN_ELEVATED(elevatedLockFilePath, updateLockFileHandle, 0);
 #endif
 #ifdef XP_MACOSX
     if (gSucceeded) {
-      LaunchMacPostProcess(argv[argOffset]);
+      LaunchMacPostProcess(argv[callbackIndex]);
     }
 #endif /* XP_MACOSX */
-    LaunchCallbackApp(argv[3], argc - argOffset, argv + argOffset);
+    LaunchCallbackApp(argv[3], argc - callbackIndex, argv + callbackIndex);
   }
 
   return 0;
 }
 
 class ActionList
 {
 public:
--- a/toolkit/xre/nsUpdateDriver.cpp
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -18,16 +18,17 @@
  * The Initial Developer of the Original Code is Google Inc.
  * Portions created by the Initial Developer are Copyright (C) 2005
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *  Darin Fisher <darin@meer.net>
  *  Ben Turner <mozilla@songbirdnest.com>
  *  Robert Strong <robert.bugzilla@gmail.com>
+ *  Josh Aas <josh@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -50,16 +51,17 @@
 #include "prproces.h"
 #include "prlog.h"
 #include "prenv.h"
 #include "nsVersionComparator.h"
 
 #ifdef XP_MACOSX
 #include "nsILocalFileMac.h"
 #include "nsCommandLineServiceMac.h"
+#include "MacLaunchHelper.h"
 #endif
 
 #if defined(XP_WIN)
 # include <direct.h>
 # include <process.h>
 # include <windows.h>
 # define getcwd(path, size) _getcwd(path, size)
 # define getpid() GetCurrentProcessId()
@@ -74,17 +76,17 @@
 //
 // We use execv to spawn the updater process on all UNIX systems except Mac OSX
 // since it is known to cause problems on the Mac.  Windows has execv, but it
 // is a faked implementation that doesn't really replace the current process.
 // Instead it spawns a new process, so we gain nothing from using execv on
 // Windows.
 //
 // On platforms where we are not calling execv, we may need to make the
-// udpaterfail executable wait for the calling process to exit.  Otherwise, the
+// updater executable wait for the calling process to exit.  Otherwise, the
 // updater may have trouble modifying our executable image (because it might
 // still be in use).  This is accomplished by passing our PID to the updater so
 // that it can wait for us to exit.  This is not perfect as there is a race
 // condition that could bite us.  It's possible that the calling process could
 // exit before the updater waits on the specified PID, and in the meantime a
 // new process with the same PID could be created.  This situation is unlikely,
 // however, given the way most operating systems recycle PIDs.  We'll take our
 // chances ;-)
@@ -131,53 +133,40 @@ GetCurrentWorkingDir(char *buf, size_t s
 #else
   if(!getcwd(buf, size))
     return NS_ERROR_FAILURE;
 #endif
   return NS_OK;
 }
 
 #if defined(XP_MACOSX)
-
 // This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
 // gBinaryPath check removed so that the updater can reload the stub executable
 // instead of xulrunner-bin. See bug 349737.
 static nsresult
 GetXULRunnerStubPath(const char* argv0, nsILocalFile* *aResult)
 {
-  nsresult rv;
-  nsCOMPtr<nsILocalFile> lf;
-
-  NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(lf));
-  nsCOMPtr<nsILocalFileMac> lfm (do_QueryInterface(lf));
-  if (!lfm)
-    return NS_ERROR_FAILURE;
-
   // Works even if we're not bundled.
-  CFBundleRef appBundle = CFBundleGetMainBundle();
+  CFBundleRef appBundle = ::CFBundleGetMainBundle();
   if (!appBundle)
     return NS_ERROR_FAILURE;
 
-  CFURLRef bundleURL = CFBundleCopyExecutableURL(appBundle);
+  CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
   if (!bundleURL)
     return NS_ERROR_FAILURE;
 
-  FSRef fileRef;
-  if (!CFURLGetFSRef(bundleURL, &fileRef)) {
-    CFRelease(bundleURL);
-    return NS_ERROR_FAILURE;
-  }
+  nsCOMPtr<nsILocalFileMac> lfm;
+  nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, PR_TRUE, getter_AddRefs(lfm));
 
-  rv = lfm->InitWithFSRef(&fileRef);
-  CFRelease(bundleURL);
+  ::CFRelease(bundleURL);
 
   if (NS_FAILED(rv))
     return rv;
 
-  NS_ADDREF(*aResult = lf);
+  NS_ADDREF(*aResult = static_cast<nsILocalFile*>(lfm.get()));
   return NS_OK;
 }
 #endif /* XP_MACOSX */
 
 static PRBool
 GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsILocalFile> &result)
 {
   nsresult rv;
@@ -451,71 +440,58 @@ ApplyUpdate(nsIFile *greDir, nsIFile *up
   // we pass "0" which is then ignored by the updater.
 #if defined(USE_EXECV)
   NS_NAMED_LITERAL_CSTRING(pid, "0");
 #else
   nsCAutoString pid;
   pid.AppendInt((PRInt32) getpid());
 #endif
 
-  int argc = appArgc + 4;
+  int argc = appArgc + 5;
   char **argv = new char*[argc + 1];
   if (!argv)
     return;
   argv[0] = (char*) updaterPath.get();
   argv[1] = (char*) updateDirPath.get();
-  argv[2] = (char*) pid.get();
+  argv[2] = (char*) applyToDir.get();
+  argv[3] = (char*) pid.get();
   if (appArgc) {
-    argv[3] = workingDirPath;
-    argv[4] = (char*) appFilePath.get();
+    argv[4] = workingDirPath;
+    argv[5] = (char*) appFilePath.get();
     for (int i = 1; i < appArgc; ++i)
-      argv[4 + i] = appArgv[i];
-    argv[4 + appArgc] = nsnull;
+      argv[5 + i] = appArgv[i];
+    argc = 5 + appArgc;
+    argv[argc] = NULL;
   } else {
-    argv[3] = nsnull;
-    argc = 3;
+    argc = 4;
+    argv[4] = NULL;
   }
 
   if (gSafeMode) {
     PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
   }
 
   LOG(("spawning updater process [%s]\n", updaterPath.get()));
 
 #if defined(USE_EXECV)
-  chdir(applyToDir.get());
   execv(updaterPath.get(), argv);
 #elif defined(XP_WIN)
-  _wchdir(applyToDir.get());
-
-  if (!WinLaunchChild(updaterPathW.get(), appArgc + 4, argv))
+  if (!WinLaunchChild(updaterPathW.get(), argc, argv))
     return;
   _exit(0);
+#elif defined(XP_MACOSX)
+  CommandLineServiceMac::SetupMacCommandLine(argc, argv, PR_TRUE);
+  // LaunchChildMac uses posix_spawnp and prefers the current
+  // architecture when launching. It doesn't require a
+  // null-terminated string but it doesn't matter if we pass one.
+  LaunchChildMac(argc, argv);
+  exit(0);
 #else
-  PRStatus status;
-  PRProcessAttr *attr;
-  
-  attr = PR_NewProcessAttr();
-  if (!attr)
-    goto end;
-
-  status = PR_ProcessAttrSetCurrentDirectory(attr, applyToDir.get());
-  if (status != PR_SUCCESS)
-    goto end;
-
-#ifdef XP_MACOSX
-  CommandLineServiceMac::SetupMacCommandLine(argc, argv, PR_TRUE);
-#endif
-
-  PR_CreateProcessDetached(updaterPath.get(), argv, nsnull, attr);
+  PR_CreateProcessDetached(updaterPath.get(), argv, NULL, NULL);
   exit(0);
-
-end:
-  PR_DestroyProcessAttr(attr); 
-  delete[] argv;
 #endif
 }
 
 nsresult
 ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
                int argc, char **argv, const char *&appVersion)
 {
   nsresult rv;