DRTVWR-474: Do NOT autokill updater process on viewer termination.

The updater is required to survive beyond termination of the viewer that
launched it so it can launch the next installer, or a replacement viewer.
Having the old viewer forcibly terminate it on shutdown would be counter-
productive.

Introduce a third LLLeap::create() overload taking LLProcess::Params, which
gives access to autokill, cwd and other options previously unsupported by
LLLeap. Reimplement the existing create() overloads in terms of this new one,
since LLLeapImpl::LLLeapImpl() is already based on LLProcess::Params anyway.

Use LLProcess::Params in LLAppViewer::init() to specify the updater process,
setting autokill=false.

Refactoring LLLeapImpl() apparently involved engaging an LLInitParam::Block
feature never before used: had to drag operator() into Multiple from its base
class TypedParam (as has been done in other TypedParam subclasses).
master
Nat Goodspeed 2018-10-03 14:00:05 -04:00
parent 6e1b2e137e
commit b1955d4247
4 changed files with 65 additions and 36 deletions

View File

@ -2115,6 +2115,9 @@ namespace LLInitParam
typedef typename super_t::iterator iterator;
typedef typename super_t::const_iterator const_iterator;
using super_t::operator();
using super_t::operator const container_t&;
explicit Multiple(const char* name = "")
: super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount)
{}

View File

@ -47,9 +47,9 @@ class LLLeapImpl: public LLLeap
LOG_CLASS(LLLeap);
public:
// Called only by LLLeap::create()
LLLeapImpl(const std::string& desc, const std::vector<std::string>& plugin):
LLLeapImpl(const LLProcess::Params& cparams):
// We might reassign mDesc in the constructor body if it's empty here.
mDesc(desc),
mDesc(cparams.desc),
// We expect multiple LLLeapImpl instances. Definitely tweak
// mDonePump's name for uniqueness.
mDonePump("LLLeap", true),
@ -67,17 +67,17 @@ public:
// this class or method name.
mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))
{
// Rule out empty vector
if (plugin.empty())
// Rule out unpopulated Params block
if (! cparams.executable.isProvided())
{
LLTHROW(Error("no plugin command"));
}
// Don't leave desc empty either, but in this case, if we weren't
// given one, we'll fake one.
if (desc.empty())
if (mDesc.empty())
{
mDesc = LLProcess::basename(plugin[0]);
mDesc = LLProcess::basename(cparams.executable);
// how about a toLower() variant that returns the transformed string?!
std::string desclower(mDesc);
LLStringUtil::toLower(desclower);
@ -87,9 +87,9 @@ public:
// notice Python specially: we provide Python LLSD serialization
// support, so there's a pretty good reason to implement plugins
// in that language.
if (plugin.size() >= 2 && (desclower == "python" || desclower == "python.exe"))
if (cparams.args.size() && (desclower == "python" || desclower == "python.exe"))
{
mDesc = LLProcess::basename(plugin[1]);
mDesc = LLProcess::basename(cparams.args()[0]);
}
}
@ -97,14 +97,10 @@ public:
mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));
// Okay, launch child.
LLProcess::Params params;
// Get a modifiable copy of params block to set files and postend.
LLProcess::Params params(cparams);
// copy our deduced mDesc back into the params block
params.desc = mDesc;
std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end());
params.executable = *pi++;
for ( ; pi != pend; ++pi)
{
params.args.add(*pi);
}
params.files.add(LLProcess::FileParam("pipe")); // stdin
params.files.add(LLProcess::FileParam("pipe")); // stdout
params.files.add(LLProcess::FileParam("pipe")); // stderr
@ -429,17 +425,17 @@ private:
boost::scoped_ptr<LLLeapListener> mListener;
};
// This must follow the declaration of LLLeapImpl, so it may as well be last.
LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc)
// These must follow the declaration of LLLeapImpl, so they may as well be last.
LLLeap* LLLeap::create(const LLProcess::Params& params, bool exc)
{
// If caller is willing to permit exceptions, just instantiate.
if (exc)
return new LLLeapImpl(desc, plugin);
return new LLLeapImpl(params);
// Caller insists on suppressing LLLeap::Error. Very well, catch it.
try
{
return new LLLeapImpl(desc, plugin);
return new LLLeapImpl(params);
}
catch (const LLLeap::Error&)
{
@ -447,6 +443,23 @@ LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>&
}
}
LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc)
{
LLProcess::Params params;
params.desc = desc;
std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end());
// could validate here, but let's rely on LLLeapImpl's constructor
if (pi != pend)
{
params.executable = *pi++;
}
for ( ; pi != pend; ++pi)
{
params.args.add(*pi);
}
return create(params, exc);
}
LLLeap* LLLeap::create(const std::string& desc, const std::string& plugin, bool exc)
{
// Use LLStringUtil::getTokens() to parse the command line

View File

@ -14,6 +14,7 @@
#include "llinstancetracker.h"
#include "llexception.h"
#include "llprocess.h"
#include <string>
#include <vector>
@ -61,6 +62,19 @@ public:
static LLLeap* create(const std::string& desc, const std::string& plugin,
bool exc=true);
/**
* Pass an LLProcess::Params instance to specify desc, executable, args et al.
*
* Note that files and postend are set implicitly; any values you set in
* those fields will be disregarded.
*
* Pass exc=false to suppress LLLeap::Error exception. Obviously in that
* case the caller cannot discover the nature of the error, merely that an
* error of some kind occurred (because create() returned NULL). Either
* way, the error is logged.
*/
static LLLeap* create(const LLProcess::Params& params, bool exc=true);
/**
* Exception thrown for invalid create() arguments, e.g. no plugin
* program. This is more resiliant than an LL_ERRS failure, because the

View File

@ -1130,30 +1130,35 @@ bool LLAppViewer::init()
gGLActive = FALSE;
std::vector<std::string> updater
LLProcess::Params updater;
updater.desc = "updater process";
// Because it's the updater, it MUST persist beyond the lifespan of the
// viewer itself.
updater.autokill = false;
#if LL_WINDOWS
{ gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "updater.exe") };
updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "updater.exe");
#elif LL_DARWIN
// explicitly run the system Python interpreter on updater.py
{ "python", gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", "updater.py") };
updater.executable = "python";
updater.args.add(gDirUtilp->add(gDirUtilp->getAppRODataDir(), "updater", "updater.py"));
#else
{ gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "updater") };
updater.executable = gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, "updater");
#endif
// add LEAP mode command-line argument to whichever of these we selected
updater.push_back("leap");
updater.args.add("leap");
// UpdaterServiceSettings
updater.push_back(stringize(gSavedSettings.getU32("UpdaterServiceSetting")));
updater.args.add(stringize(gSavedSettings.getU32("UpdaterServiceSetting")));
// channel
updater.push_back(LLVersionInfo::getChannel());
updater.args.add(LLVersionInfo::getChannel());
// testok
updater.push_back(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest")));
updater.args.add(stringize(gSavedSettings.getBOOL("UpdaterWillingToTest")));
// UpdaterServiceURL
updater.push_back(gSavedSettings.getString("UpdaterServiceURL"));
updater.args.add(gSavedSettings.getString("UpdaterServiceURL"));
// ForceAddressSize
updater.push_back(stringize(gSavedSettings.getU32("ForceAddressSize")));
updater.args.add(stringize(gSavedSettings.getU32("ForceAddressSize")));
// Run the updater. An exception from launching the updater should bother us.
LLLeap::create("updater process", updater, true);
LLLeap::create(updater, true);
// Iterate over --leap command-line options. But this is a bit tricky: if
// there's only one, it won't be an array at all.
@ -3923,12 +3928,6 @@ void LLAppViewer::requestQuit()
gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
}
// Try to send last batch of avatar rez metrics.
if (!gDisconnected && isAgentAvatarValid())
{
gAgentAvatarp->updateAvatarRezMetrics(true); // force a last packet to be sent.
}
LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral*)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
effectp->setPositionGlobal(gAgent.getPositionGlobal());
effectp->setColor(LLColor4U(gAgent.getEffectColor()));