Landing patch for bug 292408 "Software update driver for nsAppRunner.cpp" r=bsmedberg a=chofmann

This commit is contained in:
darin%meer.net 2005-06-08 17:40:38 +00:00
Родитель 8daab17916
Коммит 1c9ba2a090
4 изменённых файлов: 430 добавлений и 0 удалений

Просмотреть файл

@ -106,6 +106,11 @@ CPPSRCS = \
nsNativeAppSupportBase.cpp \
$(NULL)
ifdef MOZ_UPDATER
CPPSRCS += nsUpdateDriver.cpp
DEFINES += -DMOZ_UPDATER
endif
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
CPPSRCS += nsNativeAppSupportWin.cpp
DEFINES += -DWIN32_LEAN_AND_MEAN

Просмотреть файл

@ -41,6 +41,7 @@
#include "nsAppRunner.h"
#include "nsUpdateDriver.h"
#include "nsBuildID.h"
#ifdef XP_MACOSX
@ -1752,6 +1753,11 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
return 0;
}
#if defined(MOZ_UPDATER)
// Check for and process any available updates
ProcessUpdates(dirProvider.GetAppDir(), gRestartArgc, gRestartArgv);
#endif
#if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
// setup for private colormap. Ideally we'd like to do this
// in nsAppShell::Create, but we need to get in before gtk

Просмотреть файл

@ -0,0 +1,362 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* 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>
*
* 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
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <stdio.h>
#include "nsUpdateDriver.h"
#include "nsILocalFile.h"
#include "nsISimpleEnumerator.h"
#include "nsIDirectoryEnumerator.h"
#include "nsCOMPtr.h"
#include "nsCOMArray.h"
#include "nsString.h"
#include "nsPrintfCString.h"
#include "prproces.h"
#include "prlog.h"
#if defined(XP_WIN)
# include <direct.h>
# include <process.h>
# include <windows.h>
#elif defined(XP_UNIX)
# include <unistd.h>
#endif
#ifdef PR_LOGGING
static PRLogModuleInfo *sUpdateLog = PR_NewLogModule("updatedriver");
#endif
#define LOG(args) PR_LOG(sUpdateLog, PR_LOG_DEBUG, args)
#ifdef XP_WIN
static const char kUpdaterBin[] = "updater.exe";
#else
static const char kUpdaterBin[] = "updater";
#endif
static const char kUpdaterINI[] = "updater.ini";
#ifdef XP_WIN
// On windows, we need to double-quote parameters before passing them to execv.
// Otherwise, they will get parsed as separate parameters by the new process.
static void
DoubleQuoteIfNeeded(nsCString &str)
{
if (str.FindChar(' ') != kNotFound) {
str.Insert('\"', 0);
str.Append('\"');
}
}
#endif
static void
MakeCommandLine(char **argv, nsCString &result)
{
result.Truncate();
while (*argv) {
result.Append(*argv);
++argv;
if (*argv)
result.Append(' ');
}
}
PR_STATIC_CALLBACK(int)
ScanDirComparator(nsIFile *a, nsIFile *b, void *unused)
{
// lexically compare the leaf names of these two files
nsCAutoString a_name, b_name;
a->GetNativeLeafName(a_name);
b->GetNativeLeafName(b_name);
return Compare(a_name, b_name);
}
static nsresult
ScanDir(nsIFile *dir, nsCOMArray<nsIFile> *result)
{
nsresult rv;
nsCOMPtr<nsISimpleEnumerator> simpEnum;
rv = dir->GetDirectoryEntries(getter_AddRefs(simpEnum));
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIDirectoryEnumerator> dirEnum = do_QueryInterface(simpEnum, &rv);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsIFile> file;
for (;;) {
rv = dirEnum->GetNextFile(getter_AddRefs(file));
if (NS_FAILED(rv))
return rv;
// enumeration complete when null file is returned
if (!file)
break;
if (!result->AppendObject(file))
return NS_ERROR_OUT_OF_MEMORY;
}
result->Sort(ScanDirComparator, nsnull);
return NS_OK;
}
static PRBool
GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsILocalFile> &result)
{
nsresult rv;
nsCOMPtr<nsIFile> statusFile;
rv = dir->Clone(getter_AddRefs(statusFile));
if (NS_FAILED(rv))
return PR_FALSE;
rv = statusFile->AppendNative(name);
if (NS_FAILED(rv))
return PR_FALSE;
result = do_QueryInterface(statusFile, &rv);
return NS_SUCCEEDED(rv);
}
static PRBool
GetStatusFile(nsIFile *dir, nsCOMPtr<nsILocalFile> &result)
{
return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
}
static PRBool
IsPending(nsILocalFile *statusFile)
{
nsresult rv;
FILE *fp;
rv = statusFile->OpenANSIFileDesc("r", &fp);
if (NS_FAILED(rv))
return PR_FALSE;
char buf[32];
char *result = fgets(buf, sizeof(buf), fp);
fclose(fp);
if (!result)
return PR_FALSE;
const char kPending[] = "pending";
return (strncmp(buf, kPending, sizeof(kPending) - 1) == 0);
}
static PRBool
SetStatus(nsILocalFile *statusFile, const char *status)
{
FILE *fp;
nsresult rv = statusFile->OpenANSIFileDesc("w", &fp);
if (NS_FAILED(rv))
return PR_FALSE;
fprintf(fp, "%s\n", status);
fclose(fp);
return PR_TRUE;
}
static PRBool
CopyUpdaterIntoUpdateDir(nsIFile *appDir, nsIFile *updateDir,
nsCOMPtr<nsIFile> &updater)
{
// We have to move the updater binary and its resource file.
const char *filesToMove[] = {
kUpdaterINI,
kUpdaterBin,
nsnull
};
nsresult rv;
for (const char **leafName = filesToMove; *leafName; ++leafName) {
nsDependentCString leaf(*leafName);
nsCOMPtr<nsIFile> file;
// Make sure there is not an existing file in the target location.
rv = updateDir->Clone(getter_AddRefs(file));
if (NS_FAILED(rv))
return PR_FALSE;
rv = file->AppendNative(leaf);
if (NS_FAILED(rv))
return PR_FALSE;
file->Remove(PR_FALSE);
// Now, copy into the target location.
rv = appDir->Clone(getter_AddRefs(file));
if (NS_FAILED(rv))
return PR_FALSE;
rv = file->AppendNative(leaf);
if (NS_FAILED(rv))
return PR_FALSE;
rv = file->CopyToNative(updateDir, EmptyCString());
if (NS_FAILED(rv))
return PR_FALSE;
}
// Finally, return the location of the updater binary.
rv = updateDir->Clone(getter_AddRefs(updater));
if (NS_FAILED(rv))
return PR_FALSE;
rv = updater->AppendNative(NS_LITERAL_CSTRING(kUpdaterBin));
return NS_SUCCEEDED(rv);
}
static void
ApplyUpdate(nsIFile *appDir, nsIFile *updateDir, nsILocalFile *statusFile,
int appArgc, char **appArgv)
{
// Steps:
// - mark update as 'applying'
// - copy updater into update dir
// - run updater w/ app dir as the current working dir
nsCOMPtr<nsIFile> updater;
if (!CopyUpdaterIntoUpdateDir(appDir, updateDir, updater)) {
LOG(("failed copying updater\n"));
return;
}
nsCAutoString updaterPath;
nsresult rv = updater->GetNativePath(updaterPath);
if (NS_FAILED(rv))
return;
nsCAutoString appDirPath;
rv = appDir->GetNativePath(appDirPath);
if (NS_FAILED(rv))
return;
nsCAutoString updateDirPath;
rv = updateDir->GetNativePath(updateDirPath);
if (NS_FAILED(rv))
return;
// Form the command line that will be used to call us back.
nsCAutoString commandLine;
MakeCommandLine(appArgv, commandLine);
if (!SetStatus(statusFile, "applying")) {
LOG(("failed setting status to 'applying'\n"));
return;
}
#if defined(XP_WIN)
// On windows, we may need to quote these paths.
DoubleQuoteIfNeeded(updaterPath);
DoubleQuoteIfNeeded(updateDirPath);
DoubleQuoteIfNeeded(commandLine);
#endif
char **argv = new char*[5];
if (!argv)
return;
argv[0] = (char*) updaterPath.get();
argv[1] = (char*) updateDirPath.get();
argv[2] = (char*) commandLine.get();
#if defined(XP_WIN)
nsPrintfCString pid("%u", GetCurrentProcessId());
argv[3] = (char*) pid.get();
argv[4] = nsnull;
#else
argv[3] = nsnull;
#endif
LOG(("spawning updater process...\n"));
#if defined(XP_WIN) || defined(XP_UNIX)
chdir(appDirPath.get());
execv(updaterPath.get(), argv);
#else
PRStatus status;
PRProcessAttr *attr;
attr = PR_NewProcessAttr();
if (!attr)
goto end;
status = PR_ProcessAttrSetCurrentDirectory(attr, appDirPath.get());
if (status != PR_SUCCESS)
goto end;
PR_CreateProcessDetached(updaterPath.get(), argv, nsnull, attr);
exit(1);
end:
PR_DestroyProcessAttr(attr);
delete[] argv;
#endif
}
nsresult
ProcessUpdates(nsIFile *appDir, int argc, char **argv)
{
nsresult rv;
nsCOMPtr<nsIFile> updatesDir;
rv = appDir->Clone(getter_AddRefs(updatesDir));
if (NS_FAILED(rv))
return rv;
rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
if (NS_FAILED(rv))
return rv;
PRBool exists;
rv = updatesDir->Exists(&exists);
if (NS_FAILED(rv) || !exists)
return rv;
nsCOMArray<nsIFile> dirEntries;
rv = ScanDir(updatesDir, &dirEntries);
if (NS_FAILED(rv))
return rv;
if (dirEntries.Count() == 0)
return NS_OK;
// look for the first update subdirectory with a status of pending
for (int i = 0; i < dirEntries.Count(); ++i) {
nsCOMPtr<nsILocalFile> statusFile;
if (GetStatusFile(dirEntries[i], statusFile) && IsPending(statusFile)) {
ApplyUpdate(appDir, dirEntries[i], statusFile, argc, argv);
break;
}
}
return NS_OK;
}

Просмотреть файл

@ -0,0 +1,57 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* 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>
*
* 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
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsUpdateDriver_h__
#define nsUpdateDriver_h__
#include "nscore.h"
class nsIFile;
/**
* This function processes any available updates. As part of that process, it
* may exit the current process and relaunch it at a later time.
*
* The argc and argv passed to this function should be what is needed to
* relaunch the current process.
*
* This function does not modify appDir.
*/
NS_HIDDEN_(nsresult) ProcessUpdates(nsIFile *appDir, int argc, char **argv);
#endif // nsUpdateDriver_h__