feat(updater): per #9, move over synthos updater code
Signed-off-by: zontreck <tarapiccari@gmail.com>
This commit is contained in:
parent
ec2f61125f
commit
d7bd3cf805
4 changed files with 479 additions and 0 deletions
141
src/raw/pkg_server/server.lsl
Normal file
141
src/raw/pkg_server/server.lsl
Normal file
|
@ -0,0 +1,141 @@
|
|||
#include "includes/common.lsl"
|
||||
|
||||
#define MSGS_0 "Server startup in progress... reading manifest"
|
||||
#define MSGS_1 "Server startup completed with "
|
||||
|
||||
default
|
||||
{
|
||||
state_entry()
|
||||
{
|
||||
// Startup
|
||||
llOwnerSay(MSGS_0);
|
||||
|
||||
UpdateDSRequest(NULL, llGetNotecardLine("server_manifest", 0), SetDSMeta(["read",0, ""]));
|
||||
}
|
||||
changed(integer iChange)
|
||||
{
|
||||
if(iChange && CHANGED_INVENTORY)
|
||||
{
|
||||
llOwnerSay("Reloading server...");
|
||||
llResetScript();
|
||||
}
|
||||
}
|
||||
listen(integer c,string n,key i,string m)
|
||||
{
|
||||
llWhisper(0, m);
|
||||
if(llJsonGetValue(m, ["operation"]) == "check_package_servers")
|
||||
{
|
||||
llRegionSayTo(i,c,Build("package_server_reply", ["server", g_sServerName]));
|
||||
}else if(llJsonGetValue(m,["operation"]) == "connect")
|
||||
{
|
||||
// This is always going to be successful in reality, so what we do is we send back all package names
|
||||
// Anything beyond that is package specific
|
||||
list lPackages = llJson2List(g_sPackages);
|
||||
integer count = llGetListLength(lPackages)/2;
|
||||
|
||||
list lNames= StrideOfList(lPackages, 2,0,-1);
|
||||
lPackages=[];// Mark for GC
|
||||
|
||||
llRegionSayTo(i,c,Build("package_list", ["packages", llList2Json(JSON_ARRAY, lNames)]));
|
||||
} else if(llJsonGetValue(m,["operation"]) == "check_package")
|
||||
{
|
||||
string sPackage = llJsonGetValue(m,["pkg"]);
|
||||
|
||||
list lPkgDef = llJson2List(llJsonGetValue(g_sPackages,[sPackage]));
|
||||
lPkgDef=StrideOfList(lPkgDef,2,0,-1);
|
||||
|
||||
llRegionSayTo(i,c,Build("package_versions", [sPackage, llList2Json(JSON_ARRAY, lPkgDef)])); // Send all versions
|
||||
|
||||
} else if(llJsonGetValue(m,["operation"]) == "check_version")
|
||||
{
|
||||
string sPackage = llJsonGetValue(m,["pkg"]);
|
||||
string sVer = llJsonGetValue(m,["ver"]);
|
||||
if(sVer == "latest") sVer = llJsonGetValue(g_sPackages, [sPackage, "latest"]);
|
||||
string sCurVer = llJsonGetValue(m,["cur"]);
|
||||
string sCompat = llJsonGetValue(g_sPackages, [sPackage, sVer, "compat"]);
|
||||
|
||||
integer iRes=VersionNumberCompare(sCurVer, sVer);
|
||||
integer iSame;
|
||||
integer iNewer;
|
||||
if(iRes == 0)
|
||||
{
|
||||
iSame=TRUE;
|
||||
}else {
|
||||
iSame=FALSE;
|
||||
if(iRes>0)iNewer=1;
|
||||
else iNewer=0;
|
||||
}
|
||||
|
||||
integer iCompat;
|
||||
|
||||
iRes = VersionNumberCompare(sCurVer, sCompat);
|
||||
if(iRes <= 0)iCompat=1;
|
||||
else iCompat=0;
|
||||
|
||||
llRegionSayTo(i,c,Build("version_back", ["same", iSame, "newer", iNewer, "compatible", iCompat]));
|
||||
} else if(llJsonGetValue(m,["operation"]) == "send")
|
||||
{
|
||||
string sPackage = llJsonGetValue(m,["pkg"]);
|
||||
string sRequest = llJsonGetValue(m,["ver"]);
|
||||
if(sRequest == "latest")sRequest = llJsonGetValue(g_sPackages, [sPackage, "latest"]);
|
||||
|
||||
string sItem = llJsonGetValue(g_sPackages, [sPackage, sRequest, "item"]);
|
||||
|
||||
llGiveInventory(llJsonGetValue(m,["dest"]), sItem);
|
||||
|
||||
llRegionSayTo(i,c,Build("send_back", ["item", sItem]));
|
||||
}
|
||||
}
|
||||
dataserver( key queryid, string data )
|
||||
{
|
||||
if(~HasDSRequest(queryid))
|
||||
{
|
||||
list lMeta = GetMetaList(queryid);
|
||||
string sOp = llList2String(lMeta,0);
|
||||
if(sOp == "read")
|
||||
{
|
||||
integer iLine = (integer)llList2String(lMeta,1);
|
||||
string sPkg = llList2String(lMeta,2);
|
||||
if(data==EOF)
|
||||
{
|
||||
DeleteDSReq(queryid);
|
||||
llOwnerSay(MSGS_1 + (string)llGetFreeMemory()+"b free");
|
||||
|
||||
g_iServerListener = llListen(PACKAGE_SERVER_CHANNEL, "", "", "");
|
||||
} else {
|
||||
iLine++;
|
||||
|
||||
list lOpts = llParseStringKeepNulls(data, [" ", "|"], []);
|
||||
|
||||
if(llList2String(lOpts,0) == "SERVER_NAME")
|
||||
{
|
||||
g_sServerName = llDumpList2String(llList2List(lOpts,1,-1), " ");
|
||||
}else if(llList2String(lOpts,0) == "BEGIN")
|
||||
{
|
||||
sPkg = llDumpList2String(llList2List(lOpts,1,-1), " ");
|
||||
|
||||
g_sPackages = llJsonSetValue(g_sPackages, [sPkg], "{}");
|
||||
} else if(llList2String(lOpts,0) == "LATEST")
|
||||
{
|
||||
g_sPackages = llJsonSetValue(g_sPackages, [sPkg,"latest"], llDumpList2String(llList2List(lOpts,1,-1), " "));
|
||||
}else if(llList2String(lOpts,0) == "END")
|
||||
{
|
||||
sPkg="";
|
||||
}else {
|
||||
string sVer = llList2String(lOpts,0);
|
||||
string sItem = llDumpList2String(llList2List(lOpts,1,-2)," ");
|
||||
// The version number for which this package is backward compatible.
|
||||
// Backwards compatibility means that at least in theory, someone could even downgrade without issues to at least this version number
|
||||
// It also means that no middle steps are required for upgrading. The user can upgrade from the version to this package version without issues.
|
||||
string sBackwardCompat = llList2String(lOpts,2);
|
||||
|
||||
g_sPackages = llJsonSetValue(g_sPackages, [sPkg, sVer], llList2Json(JSON_OBJECT, ["item", sItem, "compat", sBackwardCompat]));
|
||||
}
|
||||
|
||||
|
||||
UpdateDSRequest(queryid, llGetNotecardLine("server_manifest", iLine), SetDSMeta(["read", iLine, sPkg]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
src/raw/updater/force_update.lsl
Normal file
53
src/raw/updater/force_update.lsl
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
|
||||
This file is a temporary placeholder for development purposes to help bootstrap the update system. It is a drop in script to immediately start the update
|
||||
|
||||
*/
|
||||
|
||||
#include "includes/common.lsl"
|
||||
|
||||
default
|
||||
{
|
||||
state_entry()
|
||||
{
|
||||
// Send the update scan signal
|
||||
VERSION = "0.0.0.0";
|
||||
g_iUpdaterListener = llListen(UPDATER_CHANNEL, "", "", "");
|
||||
llWhisper(UPDATER_CHANNEL, Build("checkupdate", ["myversion", compileVersion()]));
|
||||
|
||||
llSetTimerEvent(1);
|
||||
}
|
||||
|
||||
timer(){
|
||||
if(llGetTime()>=10){
|
||||
llSay(0, "No updater responded in time, good bye!");
|
||||
llRemoveInventory(llGetScriptName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
listen(integer iChan, string sName, key kID, string sMsg)
|
||||
{
|
||||
//llSay(0, "DEBUG ["+llDumpList2String([iChan, sName, kID, sMsg], " ~ ")+"]");
|
||||
integer iStart=0;
|
||||
if(llJsonGetValue(sMsg,["operation"]) == "available")
|
||||
{
|
||||
// Begin stage 2
|
||||
iStart=1;
|
||||
}else if(llJsonGetValue(sMsg,["operation"]) == "same")
|
||||
{
|
||||
// Impossible as this is the force start. Tell it to start anyway!
|
||||
iStart=1;
|
||||
}
|
||||
|
||||
if(!iStart)return;
|
||||
|
||||
g_iClientPin = llAbs(llRound(llFrand(0xFFFF)));
|
||||
llSetRemoteScriptAccessPin(g_iClientPin);
|
||||
llSleep(1);
|
||||
|
||||
llRegionSayTo(kID, UPDATER_CHANNEL, Build("stage2", ["pin", g_iClientPin]));
|
||||
llSay(0, "Updater found, sent pairing code, good bye!");
|
||||
llRemoveInventory(llGetScriptName());
|
||||
}
|
||||
}
|
129
src/raw/updater/shim.lsl
Normal file
129
src/raw/updater/shim.lsl
Normal file
|
@ -0,0 +1,129 @@
|
|||
#include "includes/common.lsl"
|
||||
|
||||
default
|
||||
{
|
||||
state_entry()
|
||||
{
|
||||
// We are installed by the updater.
|
||||
// We can now perform update specific preparations
|
||||
// First off send the update started signal
|
||||
if(llGetStartParameter()==0)llSetScriptState(llGetScriptName(), FALSE);
|
||||
if(llGetInventoryType("SynthOS Installer") == INVENTORY_SCRIPT){
|
||||
llSetScriptState(llGetScriptName(), FALSE);
|
||||
return;
|
||||
}
|
||||
g_kSession = MakeSession(SESSION_UPDATE);
|
||||
signal(SIGNAL_UPDATE_START, g_kSession);
|
||||
g_iStartup = llGetStartParameter();
|
||||
|
||||
g_iUpdaterListener = llListen(UPDATER_CHANNEL, "", "", "");
|
||||
}
|
||||
|
||||
dataserver( key kID, string sData )
|
||||
{
|
||||
if(HasDSRequest(kID)!=-1)
|
||||
{
|
||||
list lMeta = GetMetaList(kID);
|
||||
if(llList2String(lMeta,0) == "read")
|
||||
{
|
||||
if(sData == EOF)
|
||||
{
|
||||
DeleteDSReq(kID);
|
||||
llRemoveInventory(llList2String(lMeta,1)); // Delete the BOM file
|
||||
llRegionSayTo(g_kUpdatePair, UPDATER_CHANNEL, Build("upgrade_security", ["pin", g_iClientPin, "secure_channel", g_iUpdaterSecureChannel]));
|
||||
}else {
|
||||
if(sData=="")jump next;
|
||||
list lTmp = llParseString2List(sData,["|"], []);
|
||||
string sItem = llList2String(lTmp,1);
|
||||
if(llGetInventoryType(sItem) == INVENTORY_NONE)jump next;
|
||||
llRemoveInventory(sItem);
|
||||
|
||||
@next;
|
||||
integer iLine = (integer)llList2String(lMeta,2);
|
||||
iLine++;
|
||||
lMeta[2]=iLine;
|
||||
UpdateDSRequest(kID, llGetNotecardLine(llList2String(lMeta,1), iLine), SetDSMeta(lMeta));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
listen(integer iChannel, string sName, key kID, string sMsg)
|
||||
{
|
||||
//llSay(0, "DEBUG ["+llDumpList2String([iChannel, sName, kID, sMsg], " ~ ")+"]");
|
||||
// Check the startup number against the pairing code
|
||||
if(iChannel == UPDATER_CHANNEL)
|
||||
{
|
||||
if(llJsonGetValue(sMsg,["operation"]) == "connect")
|
||||
{
|
||||
integer iPair = (integer)llJsonGetValue(sMsg,["pair"]);
|
||||
if(iPair == g_iStartup){
|
||||
// Upgrade to a secure channel
|
||||
llListenRemove(g_iUpdaterListener);
|
||||
|
||||
g_iUpdaterSecureChannel = llAbs(llRound(llFrand(0xFFFFFF)));
|
||||
g_iUpdaterSecureListener = llListen(g_iUpdaterSecureChannel, "", kID, "");
|
||||
g_kUpdatePair = kID;
|
||||
g_iClientPin = llAbs(llRound(llFrand(0xFFFFFF)));
|
||||
// Inform the updater that we are ready to upgrade to the secure channel
|
||||
llSetRemoteScriptAccessPin(g_iClientPin);
|
||||
|
||||
string sBOM = llJsonGetValue(sMsg,["bom"]);
|
||||
if(llGetInventoryType(sBOM)==INVENTORY_NOTECARD)
|
||||
UpdateDSRequest(NULL, llGetNotecardLine(sBOM,1), SetDSMeta(["read", sBOM, 1])); // Start at line 1 to skip the Version Number
|
||||
else
|
||||
llRegionSayTo(g_kUpdatePair, UPDATER_CHANNEL, Build("upgrade_security", ["pin", g_iClientPin, "secure_channel", g_iUpdaterSecureChannel]));
|
||||
}
|
||||
|
||||
}
|
||||
} else if (iChannel == g_iUpdaterSecureChannel)
|
||||
{
|
||||
// This is the secure channel, which means that the handshake was successful
|
||||
// Here we process update signals from the updater itself
|
||||
// The packets from the updater will tell us information about the item in question
|
||||
// Due to SL not telling us UUIDs of things that we do not have full permission to, we must rely on a force update all approach.
|
||||
// This is not ideal but it is how non-full perm stuff must be updated.
|
||||
|
||||
if (llJsonGetValue(sMsg, ["operation"]) == "began")
|
||||
{
|
||||
// The updater has begun to send items or install scripts. It'll be quiet for a little bit while the install happens.
|
||||
// Currently there is nothing here to process
|
||||
} else if(llJsonGetValue(sMsg, ["operation"]) == "ended")
|
||||
{
|
||||
// The updater has sent us everything and it has started to reset itself.
|
||||
// We can safely assume the update is done. Sleep for about 10 seconds and then dispatch the update done signal
|
||||
|
||||
// Sleep for 1 second to let everything settle down
|
||||
llSleep(1);
|
||||
|
||||
integer i=0;
|
||||
integer end = llGetInventoryNumber(INVENTORY_SCRIPT);
|
||||
for(i=0;i<end;i++)
|
||||
{
|
||||
string sName = llGetInventoryName(INVENTORY_SCRIPT, i);
|
||||
llSetScriptState(sName, TRUE); // We do not care about resetting them right here. Let them start up, then sleep and send the update done signal
|
||||
}
|
||||
llSleep(10);
|
||||
signal(SIGNAL_UPDATE_DONE, g_kSession);
|
||||
llSetRemoteScriptAccessPin(0);
|
||||
signal(SIGNAL_RESET, g_kSession);
|
||||
llSleep(1);
|
||||
llSay(0, "Update completed to "+llLinksetDataRead("os.softwareversion"));
|
||||
llRemoveInventory(llGetScriptName()); // Clean up ourself
|
||||
} else if(llJsonGetValue(sMsg, ["operation"]) == "prepare")
|
||||
{
|
||||
string sItem=llJsonGetValue(sMsg,["details", "name"]);
|
||||
// This signal is specific to items that need to be removed to make room for the newer item
|
||||
if(llGetInventoryType(sItem) != INVENTORY_NONE)
|
||||
{
|
||||
llRemoveInventory(sItem);
|
||||
}
|
||||
|
||||
// Send back the details packet which will include details like the item name, and type
|
||||
// We are sending it on the main json payload this time instead of inside a json object ["details"]
|
||||
llRegionSayTo(kID, iChannel, Build("continue", llJson2List(llJsonGetValue(sMsg,["details"]))));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
156
src/raw/updater/updater.lsl
Normal file
156
src/raw/updater/updater.lsl
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
|
||||
This file is the updater
|
||||
|
||||
*/
|
||||
#include "includes/common.lsl"
|
||||
integer g_iDelOnFinish=0;
|
||||
default
|
||||
{
|
||||
state_entry()
|
||||
{
|
||||
g_iUpdaterListener = llListen(UPDATER_CHANNEL, "", "", "");
|
||||
|
||||
// Scan for BOM file
|
||||
integer i=0;
|
||||
integer end = llGetInventoryNumber(INVENTORY_NOTECARD);
|
||||
for(i=0;i<end;i++)
|
||||
{
|
||||
string sName = llGetInventoryName(INVENTORY_NOTECARD, i);
|
||||
if(llSubStringIndex(sName, ".BOM")!=-1)
|
||||
{
|
||||
g_sBOM = sName;
|
||||
}
|
||||
}
|
||||
if(g_sBOM == "")
|
||||
llOwnerSay("/!\\ ALERT /!\\ NO BOM DETECTED. INSTALLER WILL NOT WORK");
|
||||
else llOwnerSay("Installer is now operational and set to : "+g_sBOM);
|
||||
|
||||
if(g_sBOM != "")UpdateDSRequest(NULL, llGetNotecardLine(g_sBOM,0), SetDSMeta(["read_version"]));
|
||||
}
|
||||
|
||||
on_rez(integer t){
|
||||
g_iDelOnFinish=t;
|
||||
}
|
||||
|
||||
changed(integer iChange)
|
||||
{
|
||||
if(iChange & CHANGED_INVENTORY)
|
||||
{
|
||||
// Reset ourselves if not update in progress
|
||||
if(g_iClientPin != 0){
|
||||
// We're in a update, scream at the object owner
|
||||
llOwnerSay("/!\\ FATAL /!\\\n \n[ You have changed my contents mid-update. There could be serious problems that arise now. You should repeat the update after this one ends if there are issues ]");
|
||||
}else
|
||||
llResetScript();
|
||||
}
|
||||
}
|
||||
listen( integer iChannel, string sName, key kID, string sMsg )
|
||||
{
|
||||
//llSay(0, "DEBUG ["+llDumpList2String([iChannel, sName, kID, sMsg], " ~ ")+"]");
|
||||
if(iChannel == UPDATER_CHANNEL)
|
||||
{
|
||||
if(llJsonGetValue(sMsg,["operation"]) == "checkupdate")
|
||||
{
|
||||
string sVer = llJsonGetValue(sMsg,["myversion"]);
|
||||
integer iCompare = VersionNumberCompare(compileVersion(), sVer);
|
||||
if(iCompare == 0)
|
||||
{
|
||||
llRegionSayTo(kID, UPDATER_CHANNEL, Build("same", []));
|
||||
}else if(iCompare == -1){
|
||||
// Bingo, we are good to begin
|
||||
llRegionSayTo(kID, UPDATER_CHANNEL, Build("available", []));
|
||||
} else if(iCompare == 1)
|
||||
{
|
||||
llRegionSayTo(kID, UPDATER_CHANNEL, Build("older", []));
|
||||
}
|
||||
} else if(llJsonGetValue(sMsg, ["operation"]) == "stage2")
|
||||
{
|
||||
// Send the shim
|
||||
g_iClientPin = (integer)llJsonGetValue(sMsg, ["pin"]);
|
||||
g_iStartup = llAbs(llRound(llFrand(0xFFFF))); // Pairing code
|
||||
llRemoteLoadScriptPin(kID, UPDATER_SHIM, g_iClientPin, TRUE, g_iStartup);
|
||||
llSleep(5); // Wait for it to start up...and any lag that might be happening
|
||||
llRegionSayTo(kID, UPDATER_CHANNEL, Build("connect", ["pair", g_iStartup, "bom", g_sBOM]));
|
||||
} else if(llJsonGetValue(sMsg,["operation"]) == "upgrade_security")
|
||||
{
|
||||
llListenControl(g_iUpdaterListener, FALSE);
|
||||
g_iUpdaterSecureChannel = (integer)llJsonGetValue(sMsg,["secure_channel"]);
|
||||
g_iClientPin = (integer)llJsonGetValue(sMsg,["pin"]);
|
||||
g_kUpdatePair = kID;
|
||||
g_iUpdaterSecureListener = llListen(g_iUpdaterSecureChannel, "", g_kUpdatePair, "");
|
||||
g_kSession = MakeSession(SESSION_UPDATE);
|
||||
|
||||
// Initial plans had there to be a bundle reader here but this can be done in a single script
|
||||
// Start reading BOM
|
||||
g_kBundleReader = MakeSession(SESSION_UPDATE);
|
||||
UpdateDSRequest(NULL, llGetNotecardLine(g_sBOM, 1), SetDSMeta(["read_bom", 1]));
|
||||
}
|
||||
} else if(iChannel == g_iUpdaterSecureChannel){
|
||||
if(llJsonGetValue(sMsg,["operation"]) == "continue")
|
||||
{
|
||||
integer iScript = (integer)llJsonGetValue(sMsg,["type"]);
|
||||
string sScript = llJsonGetValue(sMsg,["name"]);
|
||||
|
||||
if(iScript)
|
||||
{
|
||||
llOwnerSay("Install: "+sScript);
|
||||
llRemoteLoadScriptPin(g_kUpdatePair, sScript, g_iClientPin, FALSE, 0);
|
||||
}else {
|
||||
llOwnerSay("Give: "+sScript);
|
||||
llGiveInventory(g_kUpdatePair, sScript);
|
||||
}
|
||||
|
||||
list lMeta = GetMetaList(g_kBundleReader);
|
||||
UpdateDSRequest(g_kBundleReader, llGetNotecardLine(g_sBOM, (integer)llList2String(lMeta,1)), SetDSMeta(lMeta));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dataserver(key kID, string sData)
|
||||
{
|
||||
if(HasDSRequest(kID)!=-1)
|
||||
{
|
||||
list lMeta = GetMetaList(kID);
|
||||
if(llList2String(lMeta,0)=="read_bom")
|
||||
{
|
||||
if(sData == EOF)
|
||||
{
|
||||
llRegionSayTo(g_kUpdatePair, g_iUpdaterSecureChannel, Build("ended", []));
|
||||
llGiveInventory(g_kUpdatePair, g_sBOM); // Give the manifest. We're done now
|
||||
llSay(0, "Update completed!");
|
||||
llSleep(2);
|
||||
if(g_iDelOnFinish)llDie();
|
||||
llResetScript();
|
||||
}else {
|
||||
integer iLine = (integer)llList2String(lMeta,1);
|
||||
iLine++;
|
||||
lMeta[1]=iLine;
|
||||
|
||||
if(sData == "")jump next;
|
||||
|
||||
list lParams = llParseString2List(sData,["|"], []);
|
||||
|
||||
integer iType = 0;
|
||||
if(llList2String(lParams,0)=="SCRIPT")iType=1;
|
||||
|
||||
string sDetailsPacket = llList2Json(JSON_OBJECT,["name", llList2String(lParams,1), "type", iType]);
|
||||
|
||||
UpdateDSRequest(kID, g_kBundleReader, SetDSMeta(lMeta));
|
||||
llRegionSayTo(g_kUpdatePair, g_iUpdaterSecureChannel, Build("prepare", ["details", sDetailsPacket]));
|
||||
|
||||
jump done;
|
||||
@next;
|
||||
UpdateDSRequest(kID, llGetNotecardLine(g_sBOM, iLine), SetDSMeta(lMeta));
|
||||
|
||||
@done; // Called to await the continue signal
|
||||
}
|
||||
} else if(llList2String(lMeta,0) == "read_version")
|
||||
{
|
||||
VERSION = sData;
|
||||
DeleteDSReq(kID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue