/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Threading; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Runtime.Remoting.Lifetime; using System.Security.Policy; using System.IO; using System.Xml; using System.Text; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.ScriptEngine.Interfaces; using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared.Api; using OpenSim.Region.ScriptEngine.Shared.ScriptBase; using OpenSim.Region.ScriptEngine.XMREngine; using OpenSim.Region.Framework.Scenes; using log4net; using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; namespace OpenSim.Region.ScriptEngine.XMREngine { public partial class XMRInstance { /********************************************************************************\ * The only method of interest to outside this module is GetExecutionState() * * which captures the current state of the script into an XML document. * * * * The rest of this module contains support routines for GetExecutionState(). * \********************************************************************************/ /** * @brief Create an XML element that gives the current state of the script. * * globalsandstackdump * m_Running * * * Updates the .state file while we're at it. */ public XmlElement GetExecutionState(XmlDocument doc) { /* * When we're detaching an attachment, we need to wait here. */ // Change this to a 5 second timeout. If things do mess up, // we don't want to be stuck forever. // m_DetachReady.WaitOne (5000, false); XmlElement scriptStateN = doc.CreateElement("", "ScriptState", ""); scriptStateN.SetAttribute("Engine", m_Engine.ScriptEngineName); scriptStateN.SetAttribute("Asset", m_Item.AssetID.ToString()); scriptStateN.SetAttribute ("SourceHash", m_ObjCode.sourceHash); /* * Make sure we aren't executing part of the script so it stays * stable. Setting suspendOnCheckRun tells CheckRun() to suspend * and return out so RunOne() will release the lock asap. */ suspendOnCheckRunHold = true; lock (m_RunLock) { m_RunOnePhase = "GetExecutionState enter"; CheckRunLockInvariants(true); /* * Get copy of script globals and stack in relocateable form. */ MemoryStream snapshotStream = new MemoryStream(); MigrateOutEventHandler(snapshotStream); Byte[] snapshotBytes = snapshotStream.ToArray(); snapshotStream.Close(); string snapshotString = Convert.ToBase64String(snapshotBytes); XmlElement snapshotN = doc.CreateElement("", "Snapshot", ""); snapshotN.AppendChild(doc.CreateTextNode(snapshotString)); scriptStateN.AppendChild(snapshotN); m_RunOnePhase = "GetExecutionState B"; CheckRunLockInvariants(true); /* * "Running" says whether or not we are accepting new events. */ XmlElement runningN = doc.CreateElement("", "Running", ""); runningN.AppendChild(doc.CreateTextNode(m_Running.ToString())); scriptStateN.AppendChild(runningN); m_RunOnePhase = "GetExecutionState C"; CheckRunLockInvariants(true); /* * "DoGblInit" says whether or not default:state_entry() will init global vars. */ XmlElement doGblInitN = doc.CreateElement("", "DoGblInit", ""); doGblInitN.AppendChild(doc.CreateTextNode(doGblInit.ToString())); scriptStateN.AppendChild(doGblInitN); m_RunOnePhase = "GetExecutionState D"; CheckRunLockInvariants(true); /* * More misc data. */ XmlNode permissionsN = doc.CreateElement("", "Permissions", ""); scriptStateN.AppendChild(permissionsN); XmlAttribute granterA = doc.CreateAttribute("", "granter", ""); granterA.Value = m_Item.PermsGranter.ToString(); permissionsN.Attributes.Append(granterA); XmlAttribute maskA = doc.CreateAttribute("", "mask", ""); maskA.Value = m_Item.PermsMask.ToString(); permissionsN.Attributes.Append(maskA); m_RunOnePhase = "GetExecutionState E"; CheckRunLockInvariants(true); /* * "DetectParams" are returned by llDetected...() script functions * for the currently active event, if any. */ if (m_DetectParams != null) { XmlElement detParArrayN = doc.CreateElement("", "DetectArray", ""); AppendXMLDetectArray(doc, detParArrayN, m_DetectParams); scriptStateN.AppendChild(detParArrayN); } m_RunOnePhase = "GetExecutionState F"; CheckRunLockInvariants(true); /* * Save any events we have in the queue. * * * ... ... * ... ... * * ... * */ XmlElement queuedEventsN = doc.CreateElement("", "EventQueue", ""); lock (m_QueueLock) { foreach (EventParams evt in m_EventQueue) { XmlElement singleEventN = doc.CreateElement("", "Event", ""); singleEventN.SetAttribute("Name", evt.EventName); AppendXMLObjectArray(doc, singleEventN, evt.Params, "param"); AppendXMLDetectArray(doc, singleEventN, evt.DetectParams); queuedEventsN.AppendChild(singleEventN); } } scriptStateN.AppendChild(queuedEventsN); m_RunOnePhase = "GetExecutionState G"; CheckRunLockInvariants(true); /* * "Plugins" indicate enabled timers and listens, etc. */ Object[] pluginData = AsyncCommandManager.GetSerializationData(m_Engine, m_ItemID); XmlNode plugins = doc.CreateElement("", "Plugins", ""); AppendXMLObjectArray(doc, plugins, pluginData, "plugin"); scriptStateN.AppendChild(plugins); m_RunOnePhase = "GetExecutionState H"; CheckRunLockInvariants(true); /* * Let script run again. */ suspendOnCheckRunHold = false; m_RunOnePhase = "GetExecutionState leave"; CheckRunLockInvariants(true); } /* * scriptStateN represents the contents of the .state file so * write the .state file while we are here. */ FileStream fs = File.Create(m_StateFileName); StreamWriter sw = new StreamWriter(fs); sw.Write(scriptStateN.OuterXml); sw.Close(); fs.Close(); return scriptStateN; } /** * @brief Write script state to output stream. * The script microthread is at same state on return, * ie, either inactive or suspended inside CheckRun(). * * Input: * stream = stream to write event handler state information to */ private void MigrateOutEventHandler (Stream stream) { moehexcep = null; // do all the work in the MigrateOutEventHandlerThread() method below moehstream = stream; XMRScriptThread cst = XMRScriptThread.CurrentScriptThread (); if (cst != null) { // we might be getting called inside some LSL Api function // so we are already in script thread and thus must do // migration directly MigrateOutEventHandlerThread (); } else { // some other thread, do migration via a script thread lock (XMRScriptThread.m_WakeUpLock) { m_Engine.m_ThunkQueue.Enqueue (this.MigrateOutEventHandlerThread); } XMRScriptThread.WakeUpOne (); // wait for it to complete lock (moehdone) { while (moehstream != null) { Monitor.Wait (moehdone); } } } // maybe it threw up if (moehexcep != null) throw moehexcep; } private Exception moehexcep; private object moehdone = new object (); private Stream moehstream; private void MigrateOutEventHandlerThread () { Exception except; try { /* * Resume the microthread and it will throw a StackCaptureException() * with the stack frames saved to this.stackFrames. * Then write the saved stack frames to the output stream. * * There is a stack only if the event code is not None. */ if (this.eventCode != ScriptEventCode.None) { // tell microthread to continue // it should see captureStackFrames and throw StackCaptureException() // ...generating XMRStackFrames as it unwinds this.captureStackFrames = true; except = this.microthread.ResumeEx (); this.captureStackFrames = false; if (except == null) { throw new Exception ("stack save did not complete"); } if (!(except is StackCaptureException)) { throw except; } } /* * Write script state out, frames and all, to the stream. * Does not change script state. */ moehstream.WriteByte (migrationVersion); moehstream.WriteByte ((byte)16); this.MigrateOut (new BinaryWriter (moehstream)); /* * Now restore script stack. * Microthread will suspend inside CheckRun() when restore is complete. */ if (this.eventCode != ScriptEventCode.None) { this.stackFramesRestored = false; except = this.microthread.StartEx (); if (except != null) { throw except; } if (!this.stackFramesRestored) { throw new Exception ("restore after save did not complete"); } } } catch (Exception e) { moehexcep = e; } finally { // make sure CheckRunLockInvariants() won't puque if (this.microthread.Active () == 0) { this.eventCode = ScriptEventCode.None; } // wake the MigrateOutEventHandler() method above lock (moehdone) { moehstream = null; Monitor.Pulse (moehdone); } } } /** * @brief Convert an DetectParams[] to corresponding XML. * DetectParams[] holds the values retrievable by llDetected...() for * a given event. */ private static void AppendXMLDetectArray(XmlDocument doc, XmlElement parent, DetectParams[] detect) { foreach (DetectParams d in detect) { XmlElement detectParamsN = GetXMLDetect(doc, d); parent.AppendChild(detectParamsN); } } private static XmlElement GetXMLDetect(XmlDocument doc, DetectParams d) { XmlElement detectParamsN = doc.CreateElement("", "DetectParams", ""); XmlAttribute d_key = doc.CreateAttribute("", "key", ""); d_key.Value = d.Key.ToString(); detectParamsN.Attributes.Append(d_key); XmlAttribute pos = doc.CreateAttribute("", "pos", ""); pos.Value = d.OffsetPos.ToString(); detectParamsN.Attributes.Append(pos); XmlAttribute d_linkNum = doc.CreateAttribute("", "linkNum", ""); d_linkNum.Value = d.LinkNum.ToString(); detectParamsN.Attributes.Append(d_linkNum); XmlAttribute d_group = doc.CreateAttribute("", "group", ""); d_group.Value = d.Group.ToString(); detectParamsN.Attributes.Append(d_group); XmlAttribute d_name = doc.CreateAttribute("", "name", ""); d_name.Value = d.Name.ToString(); detectParamsN.Attributes.Append(d_name); XmlAttribute d_owner = doc.CreateAttribute("", "owner", ""); d_owner.Value = d.Owner.ToString(); detectParamsN.Attributes.Append(d_owner); XmlAttribute d_position = doc.CreateAttribute("", "position", ""); d_position.Value = d.Position.ToString(); detectParamsN.Attributes.Append(d_position); XmlAttribute d_rotation = doc.CreateAttribute("", "rotation", ""); d_rotation.Value = d.Rotation.ToString(); detectParamsN.Attributes.Append(d_rotation); XmlAttribute d_type = doc.CreateAttribute("", "type", ""); d_type.Value = d.Type.ToString(); detectParamsN.Attributes.Append(d_type); XmlAttribute d_velocity = doc.CreateAttribute("", "velocity", ""); d_velocity.Value = d.Velocity.ToString(); detectParamsN.Attributes.Append(d_velocity); return detectParamsN; } /** * @brief Append elements of an array of objects to an XML parent. * @param doc = document the parent is part of * @param parent = parent to append the items to * @param array = array of objects * @param tag = ... for each element */ private static void AppendXMLObjectArray(XmlDocument doc, XmlNode parent, object[] array, string tag) { foreach (object o in array) { XmlElement element = GetXMLObject(doc, o, tag); parent.AppendChild(element); } } /** * @brief Get and XML representation of an object. * @param doc = document the tag will be put in * @param o = object to be represented * @param tag = ... */ private static XmlElement GetXMLObject(XmlDocument doc, object o, string tag) { XmlAttribute typ = doc.CreateAttribute("", "type", ""); XmlElement n = doc.CreateElement("", tag, ""); if (o is LSL_List) { typ.Value = "list"; n.Attributes.Append(typ); AppendXMLObjectArray(doc, n, ((LSL_List)o).Data, "item"); } else { typ.Value = o.GetType().ToString(); n.Attributes.Append(typ); n.AppendChild(doc.CreateTextNode(o.ToString())); } return n; } } }