29 using System.CodeDom.Compiler;
30 using System.Collections;
31 using System.Collections.Generic;
32 using System.Diagnostics;
34 using System.Reflection;
35 using System.Security;
36 using System.Security.Permissions;
37 using System.Security.Policy;
40 using Microsoft.CSharp;
43 using OpenSim.Framework;
44 using OpenSim.Region.Framework.Interfaces;
45 using OpenSim.Region.Framework.Scenes;
48 namespace OpenSim.
Region.OptionalModules.Scripting.Minimodule
50 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"MRMModule")]
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 private Scene m_scene;
55 private bool m_Enabled;
56 private bool m_Hidden;
58 private readonly Dictionary<UUID,MRMBase> m_scripts =
new Dictionary<UUID, MRMBase>();
60 private readonly Dictionary<Type,object> m_extensions =
new Dictionary<Type, object>();
62 private static readonly CSharpCodeProvider CScodeProvider =
new CSharpCodeProvider();
67 private IConfig m_config;
69 public void RegisterExtension<T>(T instance)
71 m_extensions[typeof (T)] = instance;
74 #region INonSharedRegionModule
78 if (source.Configs[
"MRM"] != null)
80 m_config = source.Configs[
"MRM"];
82 if (source.Configs[
"MRM"].GetBoolean(
"Enabled",
false))
84 m_log.Info(
"[MRM]: Enabling MRM Module");
86 m_Hidden = source.Configs[
"MRM"].GetBoolean(
"Hidden",
false);
102 scene.EventManager.OnRezScript += EventManager_OnRezScript;
103 scene.EventManager.OnStopScript += EventManager_OnStopScript;
106 scene.EventManager.OnFrame += EventManager_OnFrame;
108 scene.RegisterModuleInterface<
IMRMModule>(
this);
121 foreach (KeyValuePair<UUID, MRMBase> pair
in m_scripts)
129 get {
return "MiniRegionModule"; }
132 public Type ReplaceableInterface
139 void EventManager_OnStopScript(uint localID, UUID itemID)
141 if (m_scripts.ContainsKey(itemID))
143 m_scripts[itemID].Stop();
147 void EventManager_OnFrame()
149 m_microthreads.Tick(1000);
152 static string ConvertMRMKeywords(
string script)
154 script = script.Replace(
"microthreaded void",
"IEnumerable");
155 script = script.Replace(
"relax;",
"yield return null;");
183 #pragma warning disable 0618
186 if (permissionSetName == null)
187 throw new ArgumentNullException(
"permissionSetName");
188 if (permissionSetName.Length == 0)
189 throw new ArgumentOutOfRangeException(
"permissionSetName", permissionSetName,
190 "Cannot have an empty permission set name");
193 PolicyStatement emptyPolicy =
new PolicyStatement(
new PermissionSet(PermissionState.None));
194 UnionCodeGroup policyRoot =
new UnionCodeGroup(
new AllMembershipCondition(), emptyPolicy);
196 bool foundName =
false;
197 PermissionSet setIntersection =
new PermissionSet(PermissionState.Unrestricted);
200 IEnumerator levelEnumerator = SecurityManager.PolicyHierarchy();
201 while (levelEnumerator.MoveNext())
203 PolicyLevel level = levelEnumerator.Current as PolicyLevel;
210 PermissionSet levelSet = level.GetNamedPermissionSet(permissionSetName);
211 if (levelSet != null)
214 if (setIntersection != null)
215 setIntersection = setIntersection.Intersect(levelSet);
223 if (setIntersection == null || !foundName)
224 setIntersection =
new PermissionSet(PermissionState.None);
226 setIntersection =
new NamedPermissionSet(permissionSetName, setIntersection);
230 PolicyStatement permissions =
new PolicyStatement(setIntersection);
231 policyRoot.AddChild(
new UnionCodeGroup(
new AllMembershipCondition(), permissions));
234 PolicyLevel appDomainLevel = PolicyLevel.CreateAppDomainLevel();
235 appDomainLevel.RootCodeGroup = policyRoot;
238 string domainName = appDomainName;
239 AppDomain restrictedDomain = AppDomain.CreateDomain(domainName);
240 restrictedDomain.SetAppDomainPolicy(appDomainLevel);
242 return restrictedDomain;
244 #pragma warning restore 0618
247 void EventManager_OnRezScript(uint localID, UUID itemID,
string script,
int startParam,
bool postOnRez,
string engine,
int stateSource)
249 if (script.StartsWith(
"//MRM:C#"))
251 if (m_config.GetBoolean(
"OwnerOnly",
true))
252 if (m_scene.GetSceneObjectPart(localID).OwnerID != m_scene.RegionInfo.EstateSettings.EstateOwner
253 || m_scene.GetSceneObjectPart(localID).CreatorID != m_scene.RegionInfo.EstateSettings.EstateOwner)
256 script = ConvertMRMKeywords(script);
261 if (m_config.GetBoolean(
"Sandboxed",
true))
263 m_log.Info(
"[MRM] Found C# MRM - Starting in AppDomain with " +
264 m_config.GetString(
"SandboxLevel",
"Internet") +
"-level security.");
266 string domainName = UUID.Random().ToString();
267 target = CreateRestrictedDomain(m_config.GetString(
"SandboxLevel",
"Internet"),
272 m_log.Info(
"[MRM] Found C# MRM - Starting in current AppDomain");
274 "[MRM] Security Risk: AppDomain is run in current context. Use only in trusted environments.");
275 target = AppDomain.CurrentDomain;
278 m_log.Info(
"[MRM] Unwrapping into target AppDomain");
279 MRMBase mmb = (MRMBase) target.CreateInstanceFromAndUnwrap(
280 CompileFromDotNetText(script, itemID.ToString()),
281 "OpenSim.MiniModule");
283 m_log.Info(
"[MRM] Initialising MRM Globals");
284 InitializeMRM(mmb, localID, itemID);
286 m_scripts[itemID] = mmb;
288 m_log.Info(
"[MRM] Starting MRM");
291 catch (UnauthorizedAccessException e)
293 m_log.Error(
"[MRM] UAE " + e.Message);
294 m_log.Error(
"[MRM] " + e.StackTrace);
296 if (e.InnerException != null)
297 m_log.Error(
"[MRM] " + e.InnerException);
299 m_scene.ForEachClient(delegate(
IClientAPI user)
301 user.SendAlertMessage(
302 "MRM UnAuthorizedAccess: " + e);
307 m_log.Info(
"[MRM] Error: " + e);
308 m_scene.ForEachClient(delegate(
IClientAPI user)
310 user.SendAlertMessage(
311 "Compile error while building MRM script, check OpenSim console for more information.");
320 UUID owner = m_scene.RegionInfo.EstateSettings.EstateOwner;
321 SEUser securityUser =
new SEUser(owner,
"Name Unassigned");
324 world =
new World(m_scene, creds);
331 m_log.Info(
"[MRM] Created MRM Instance");
336 GetGlobalEnvironment(localID, out world, out host);
338 mmb.InitMiniModule(world, host, itemID);
347 internal string CompileFromDotNetText(
string Script,
string uuid)
350 const string ext =
".cs";
351 const string FilePrefix =
"MiniModule";
354 string OutFile = Path.Combine(
"MiniModules", Path.Combine(
355 m_scene.RegionInfo.RegionID.ToString(),
356 FilePrefix +
"_compiled_" + uuid +
"_" +
357 Util.RandomClass.Next(9000) +
".dll"));
360 if (!Directory.Exists(
"MiniModules"))
361 Directory.CreateDirectory(
"MiniModules");
362 string tmp = Path.Combine(
"MiniModules", m_scene.RegionInfo.RegionID.ToString());
363 if (!Directory.Exists(tmp))
364 Directory.CreateDirectory(tmp);
370 File.Delete(OutFile);
372 catch (UnauthorizedAccessException e)
374 throw new Exception(
"Unable to delete old existing " +
375 "script-file before writing new. Compile aborted: " +
378 catch (IOException e)
380 throw new Exception(
"Unable to delete old existing " +
381 "script-file before writing new. Compile aborted: " +
388 string srcFileName = FilePrefix +
"_source_" +
389 Path.GetFileNameWithoutExtension(OutFile) + ext;
392 File.WriteAllText(Path.Combine(Path.Combine(
394 m_scene.RegionInfo.RegionID.ToString()),
395 srcFileName), Script);
399 m_log.Error(
"[Compiler]: Exception while " +
400 "trying to write script source to file \"" +
401 srcFileName +
"\": " + ex);
407 CompilerParameters parameters =
new CompilerParameters();
409 parameters.IncludeDebugInformation =
true;
411 string rootPath = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
413 List<string> libraries =
new List<string>();
414 string[] lines = Script.Split(
new string[] {
"\n"}, StringSplitOptions.RemoveEmptyEntries);
415 foreach (
string s
in lines)
417 if (s.StartsWith(
"//@DEPENDS:"))
419 libraries.Add(s.Replace(
"//@DEPENDS:",
""));
423 libraries.Add(
"OpenSim.Region.OptionalModules.dll");
424 libraries.Add(
"OpenMetaverseTypes.dll");
425 libraries.Add(
"log4net.dll");
427 foreach (
string library
in libraries)
429 parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, library));
432 parameters.GenerateExecutable =
false;
433 parameters.OutputAssembly = OutFile;
434 parameters.IncludeDebugInformation =
true;
435 parameters.TreatWarningsAsErrors =
false;
439 CompilerResults results = CScodeProvider.CompileAssemblyFromSource(
445 if (results.Errors.Count > 0)
447 string errtext = String.Empty;
448 foreach (CompilerError CompErr
in results.Errors)
456 string severity =
"Error";
457 if (CompErr.IsWarning)
459 severity =
"Warning";
462 string text = CompErr.ErrorText;
466 errtext += String.Format(
"Line ({0},{1}): {4} {2}: {3}\n",
467 CompErr.Line - 1, CompErr.Column - 1,
468 CompErr.ErrorNumber, text, severity);
471 if (!
File.Exists(OutFile))
473 throw new Exception(errtext);
479 if (!
File.Exists(OutFile))
481 string errtext = String.Empty;
482 errtext +=
"No compile error. But not able to locate compiled file.";
483 throw new Exception(errtext);
486 FileInfo fi =
new FileInfo(OutFile);
488 Byte[] data =
new Byte[fi.Length];
492 FileStream fs = File.Open(OutFile, FileMode.Open, FileAccess.Read);
493 fs.Read(data, 0, data.Length);
498 string errtext = String.Empty;
499 errtext +=
"No compile error. But not able to open file.";
500 throw new Exception(errtext);
507 string filetext = Convert.ToBase64String(data);
508 Byte[] buf = Encoding.ASCII.GetBytes(filetext);
512 FileStream sfs = File.Create(OutFile +
".cil.b64");
513 sfs.Write(buf, 0, buf.Length);
516 m_log.Info(
"MRM 10");
void GetGlobalEnvironment(uint localID, out IWorld world, out IHost host)
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
void InitializeMRM(MRMBase mmb, uint localID, UUID itemID)
static AppDomain CreateRestrictedDomain(string permissionSetName, string appDomainName)
Create an AppDomain that contains policy restricting code to execute with only the permissions grante...