diff --git a/src/mod/languages/mod_managed/freeswitch_managed.h b/src/mod/languages/mod_managed/freeswitch_managed.h index 2bbd0b80b6..477ca82b0b 100644 --- a/src/mod/languages/mod_managed/freeswitch_managed.h +++ b/src/mod/languages/mod_managed/freeswitch_managed.h @@ -60,7 +60,6 @@ struct mod_managed_globals { switch_bool_t embedded; MonoMethod *loadMethod; - MonoMethod *unloadMethod; #endif }; typedef struct mod_managed_globals mod_managed_globals; @@ -121,7 +120,6 @@ public ref class FreeSwitchManaged public: static Assembly^ mod_dotnet_managed; static MethodInfo^ loadMethod; - static MethodInfo^ unloadMethod; }; #endif diff --git a/src/mod/languages/mod_managed/managed/ApiFunction.cs b/src/mod/languages/mod_managed/managed/ApiFunction.cs index b3e92ce6dd..f751184b15 100644 --- a/src/mod/languages/mod_managed/managed/ApiFunction.cs +++ b/src/mod/languages/mod_managed/managed/ApiFunction.cs @@ -40,8 +40,6 @@ namespace FreeSWITCH { protected static bool Load() { return true; } - protected static void Unload() { } - public abstract void ExecuteBackground(string args); public abstract void Execute(FreeSWITCH.Native.Stream stream, FreeSWITCH.Native.Event evt, string args); diff --git a/src/mod/languages/mod_managed/managed/AppFunction.cs b/src/mod/languages/mod_managed/managed/AppFunction.cs index d64101aa13..636c8e9a7e 100644 --- a/src/mod/languages/mod_managed/managed/AppFunction.cs +++ b/src/mod/languages/mod_managed/managed/AppFunction.cs @@ -41,8 +41,6 @@ namespace FreeSWITCH { protected static bool Load() { return true; } - protected static void Unload() { } - protected Native.ManagedSession Session { get; private set; } protected string Arguments { get; private set; } diff --git a/src/mod/languages/mod_managed/managed/AssemblyInfo.cs b/src/mod/languages/mod_managed/managed/AssemblyInfo.cs index 592a791901..afb64faa41 100644 --- a/src/mod/languages/mod_managed/managed/AssemblyInfo.cs +++ b/src/mod/languages/mod_managed/managed/AssemblyInfo.cs @@ -5,11 +5,11 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("mod_mono_managed")] +[assembly: AssemblyTitle("mod_managed_lib")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("mod_mono_managed")] +[assembly: AssemblyProduct("mod_managed_lib")] [assembly: AssemblyCopyright("Copyright © 2008")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.0.2.0")] +[assembly: AssemblyFileVersion("1.0.2.0")] diff --git a/src/mod/languages/mod_managed/managed/Demo.cs b/src/mod/languages/mod_managed/managed/Demo.cs index 997ae52c21..22d151476e 100644 --- a/src/mod/languages/mod_managed/managed/Demo.cs +++ b/src/mod/languages/mod_managed/managed/Demo.cs @@ -46,11 +46,6 @@ namespace FreeSWITCH.Demo return true; } - new protected static void Unload() - { - Log.WriteLine(LogLevel.Info, "Inside AppDemo::Unload."); - } - protected override void Run() { Session.Answer(); @@ -80,11 +75,6 @@ namespace FreeSWITCH.Demo return true; } - new protected static void Unload() - { - Log.WriteLine(LogLevel.Debug, "Inside ApiDemo::Unload."); - } - public override void ExecuteBackground(string args) { Log.WriteLine(LogLevel.Debug, "ApiDemo on a background thread #({0}), with args '{1}'.", diff --git a/src/mod/languages/mod_managed/managed/Loader.cs b/src/mod/languages/mod_managed/managed/Loader.cs index 00b5e54f97..6c64d57e5e 100644 --- a/src/mod/languages/mod_managed/managed/Loader.cs +++ b/src/mod/languages/mod_managed/managed/Loader.cs @@ -43,15 +43,17 @@ namespace FreeSWITCH internal static class Loader { // Stores a list of the loaded function types so we can instantiate them as needed - static readonly Dictionary functions = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + static Dictionary functions = new Dictionary(StringComparer.OrdinalIgnoreCase); // Only class name. Last in wins. - static readonly Dictionary shortFunctions = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + static Dictionary shortFunctions = new Dictionary(StringComparer.OrdinalIgnoreCase); #region Load/Unload + static string managedDir; + public static bool Load() { - string managedDir = Path.Combine(Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir, "managed"); + managedDir = Path.Combine(Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir, "managed"); Log.WriteLine(LogLevel.Debug, "mod_managed_lib loader is starting with directory '{0}'.", managedDir); if (!Directory.Exists(managedDir)) { Log.WriteLine(LogLevel.Error, "Managed directory not found: {0}", managedDir); @@ -66,29 +68,43 @@ namespace FreeSWITCH return File.Exists(path) ? Assembly.LoadFile(path) : null; }; - InitManagedDelegates(_run, _execute, _executeBackground); + InitManagedDelegates(_run, _execute, _executeBackground, _loadAssembly); // This is a simple one-time loader to get things in memory // Some day we should allow reloading of modules or something - loadAssemblies(managedDir) - .SelectMany(a => a.GetExportedTypes()) - .Where(t => !t.IsAbstract) - .Where(t => t.IsSubclassOf(typeof(AppFunction)) || t.IsSubclassOf(typeof(ApiFunction))) - .ToList() - .loadFunctions(); + var allTypes = loadAssemblies(managedDir).SelectMany(a => a.GetExportedTypes()); + loadFunctions(allTypes); return true; } + public static bool LoadAssembly(string filename) { + try { + string path = Path.Combine(managedDir, filename); + if (!File.Exists(path)) { + Log.WriteLine(LogLevel.Error, "File not found: '{0}'.", path); + return false; + } + var asm = Assembly.LoadFile(path); + loadFunctions(asm.GetExportedTypes()); + return true; + } catch (Exception ex) { + Log.WriteLine(LogLevel.Error, "Exception in LoadAssembly('{0}'): {1}", filename, ex.ToString()); + return false; + } + } + delegate bool ExecuteDelegate(string cmd, IntPtr streamH, IntPtr eventH); delegate bool ExecuteBackgroundDelegate(string cmd); delegate bool RunDelegate(string cmd, IntPtr session); + delegate bool LoadAssemblyDelegate(string filename); static readonly ExecuteDelegate _execute = Execute; static readonly ExecuteBackgroundDelegate _executeBackground = ExecuteBackground; static readonly RunDelegate _run = Run; - //SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground) - [DllImport("mod_managed")] - static extern void InitManagedDelegates(RunDelegate run, ExecuteDelegate execute, ExecuteBackgroundDelegate executeBackground); + static readonly LoadAssemblyDelegate _loadAssembly = LoadAssembly; + //SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground, loadAssemblyFunction loadAssembly) + [DllImport("mod_managed", CharSet = CharSet.Ansi)] + static extern void InitManagedDelegates(RunDelegate run, ExecuteDelegate execute, ExecuteBackgroundDelegate executeBackground, LoadAssemblyDelegate loadAssembly); // Be rather lenient in finding the Load and Unload methods static readonly BindingFlags methodBindingFlags = @@ -97,13 +113,18 @@ namespace FreeSWITCH BindingFlags.IgnoreCase | // Some case insensitive languages? BindingFlags.FlattenHierarchy; // Allow inherited methods for hierarchies - static void loadFunctions(this List allTypes) + static void loadFunctions(IEnumerable allTypes) { - foreach (var t in allTypes) { + var functions = new Dictionary(Loader.functions, StringComparer.OrdinalIgnoreCase); + var shortFunctions = new Dictionary(Loader.shortFunctions, StringComparer.OrdinalIgnoreCase); + var filteredTypes = allTypes + .Where(t => !t.IsAbstract) + .Where(t => t.IsSubclassOf(typeof(AppFunction)) || t.IsSubclassOf(typeof(ApiFunction))); + foreach (var t in filteredTypes) { try { if (functions.ContainsKey(t.FullName)) { - Log.WriteLine(LogLevel.Error, "Duplicate function {0}. Skipping.", t.FullName); - continue; + functions.Remove(t.FullName); + Log.WriteLine(LogLevel.Warning, "Replacing function {0}.", t.FullName); } var loadMethod = t.GetMethod("Load", methodBindingFlags, null, Type.EmptyTypes, null); var shouldLoad = Convert.ToBoolean(loadMethod.Invoke(null, null)); // We don't require the Load method to return a bool exactly @@ -120,6 +141,8 @@ namespace FreeSWITCH logException("Load", t.FullName, ex); } } + Loader.functions = functions; + Loader.shortFunctions = shortFunctions; } static Assembly[] loadAssemblies(string managedDir) @@ -248,7 +271,7 @@ namespace FreeSWITCH /// Runs an application function. public static bool Run(string command, IntPtr sessionHandle) { - Log.WriteLine(LogLevel.Debug, "mod_mono attempting to run application '{0}'.", command); + Log.WriteLine(LogLevel.Debug, "mod_managed_lib: attempting to run application '{0}'.", command); System.Diagnostics.Debug.Assert(sessionHandle != IntPtr.Zero, "sessionHandle is null."); var parsed = parseCommand(command); if (parsed == null) return false; diff --git a/src/mod/languages/mod_managed/mod_managed.cpp b/src/mod/languages/mod_managed/mod_managed.cpp index 7233db8c2d..2a941c8cc7 100644 --- a/src/mod/languages/mod_managed/mod_managed.cpp +++ b/src/mod/languages/mod_managed/mod_managed.cpp @@ -47,17 +47,17 @@ using namespace System::Runtime::InteropServices; SWITCH_BEGIN_EXTERN_C SWITCH_MODULE_LOAD_FUNCTION(mod_managed_load); -SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_managed_shutdown); -SWITCH_MODULE_DEFINITION(mod_managed, mod_managed_load, mod_managed_shutdown, NULL); +SWITCH_MODULE_DEFINITION(mod_managed, mod_managed_load, NULL, NULL); SWITCH_STANDARD_API(managedrun_api_function); /* ExecuteBackground */ SWITCH_STANDARD_API(managed_api_function); /* Execute */ SWITCH_STANDARD_APP(managed_app_function); /* Run */ +SWITCH_STANDARD_API(managed_loadassembly); /* Load assembly */ #define MOD_MANAGED_ASM_NAME "mod_managed_lib" #define MOD_MANAGED_ASM_V1 1 #define MOD_MANAGED_ASM_V2 0 -#define MOD_MANAGED_ASM_V3 0 +#define MOD_MANAGED_ASM_V3 2 #define MOD_MANAGED_ASM_V4 0 #define MOD_MANAGED_DLL MOD_MANAGED_ASM_NAME ".dll" @@ -67,15 +67,18 @@ mod_managed_globals globals = { 0 }; typedef int (*runFunction)(const char *data, void *sessionPtr); typedef int (*executeFunction)(const char *cmd, void *stream, void *Event); typedef int (*executeBackgroundFunction)(const char* cmd); +typedef int (*loadAssemblyFunction)(const char* filename); static runFunction runDelegate; static executeFunction executeDelegate; static executeBackgroundFunction executeBackgroundDelegate; +static loadAssemblyFunction loadAssemblyDelegate; -SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground) +SWITCH_MOD_DECLARE(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground, loadAssemblyFunction loadAssembly) { runDelegate = run; executeDelegate = execute; executeBackgroundDelegate = executeBackground; + loadAssemblyDelegate = loadAssembly; } // Sets up delegates (and anything else needed) on the ManagedSession object @@ -253,10 +256,6 @@ switch_status_t findLoader() return SWITCH_STATUS_FALSE; } - if (!(globals.unloadMethod = getMethod("FreeSWITCH.Loader:Unload()", loaderClass))) { - return SWITCH_STATUS_FALSE; - } - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Found all loader functions.\n"); return SWITCH_STATUS_SUCCESS; } @@ -291,7 +290,6 @@ switch_status_t findLoader() { try { FreeSwitchManaged::loadMethod = FreeSwitchManaged::mod_dotnet_managed->GetType("FreeSWITCH.Loader")->GetMethod("Load"); - FreeSwitchManaged::unloadMethod = FreeSwitchManaged::mod_dotnet_managed->GetType("FreeSWITCH.Loader")->GetMethod("Unload"); } catch(Exception^ ex) { IntPtr msg = Marshal::StringToHGlobalAnsi(ex->ToString()); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Could not load FreeSWITCH.Loader class: %s\n", static_cast(msg.ToPointer())); @@ -363,6 +361,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_managed_load) SWITCH_ADD_API(api_interface, "managedrun", "Run a module (ExecuteBackground)", managedrun_api_function, " []"); SWITCH_ADD_API(api_interface, "managed", "Run a module as an API function (Execute)", managed_api_function, " []"); SWITCH_ADD_APP(app_interface, "managed", "Run CLI App", "Run an App on a channel", managed_app_function, " []", SAF_NONE); + SWITCH_ADD_API(api_interface, "managedload", "Load assembly", managed_loadassembly, ""); return SWITCH_STATUS_SUCCESS; } @@ -407,41 +406,23 @@ SWITCH_STANDARD_APP(managed_app_function) } success = runDelegate(data, session); if (!success) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Application run failed for %s (unknown module?).\n", data); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Application run failed for %s (unknown module or exception).\n", data); } } - - -SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_managed_shutdown) +SWITCH_STANDARD_API(managed_loadassembly) { -#ifdef _MANAGED - Object ^objResult; - try { - objResult = FreeSwitchManaged::unloadMethod->Invoke(nullptr, nullptr); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "UnloadMethod completed successfully.\n"); + int success; + if (switch_strlen_zero(cmd)) { + stream->write_function(stream, "-ERR no args specified!\n"); + return SWITCH_STATUS_SUCCESS; } - catch(Exception^ ex) { - IntPtr msg = Marshal::StringToHGlobalAnsi(ex->ToString()); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Exception occurred in Loader::Unload: %s\n", static_cast(msg.ToPointer())); - Marshal::FreeHGlobal(msg); - return SWITCH_STATUS_FALSE;; + success = loadAssemblyDelegate(cmd); + if (success) { + stream->write_function(stream, "+OK\n"); + } else { + stream->write_function(stream, "-ERR LoadAssembly returned false (invalid file or exception).\n"); } -#else - MonoObject * ex; - - mono_thread_attach(globals.domain); - mono_runtime_invoke(globals.unloadMethod, NULL, NULL, &ex); - - if (ex) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Exception occurred in Loader::Unload.\n"); - mono_print_unhandled_exception(ex); - } - - if (!globals.embedded) { - mono_jit_cleanup(globals.domain); - } -#endif return SWITCH_STATUS_SUCCESS; }