diff --git a/src/mod/languages/mod_managed/managed/Loader.cs b/src/mod/languages/mod_managed/managed/Loader.cs index 19ab3a52db..cee7af86c2 100644 --- a/src/mod/languages/mod_managed/managed/Loader.cs +++ b/src/mod/languages/mod_managed/managed/Loader.cs @@ -26,6 +26,7 @@ * Michael Giagnocavo * David Brazier * Jeff Lenk + * Artur Kraev * * Loader.cs -- mod_managed loader * @@ -33,7 +34,6 @@ using System; using System.Collections.Generic; -using System.Text; using System.IO; using System.Linq; using System.Reflection; @@ -46,16 +46,15 @@ namespace FreeSWITCH { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool ExecuteDelegate(string cmd, IntPtr streamH, IntPtr eventH); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool ExecuteBackgroundDelegate(string cmd); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool RunDelegate(string cmd, IntPtr session); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool ReloadDelegate(string cmd); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool ListDelegate(string cmd); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] delegate bool ReloadDelegate(string cmd); static readonly ExecuteDelegate _execute = Execute; static readonly ExecuteBackgroundDelegate _executeBackground = ExecuteBackground; static readonly RunDelegate _run = Run; static readonly ReloadDelegate _reload = Reload; - static readonly ListDelegate _list = List; + static readonly ExecuteDelegate _list = List; [DllImport("mod_managed", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] - static extern void InitManagedDelegates(RunDelegate run, ExecuteDelegate execute, ExecuteBackgroundDelegate executeBackground, ReloadDelegate reload, ListDelegate list); + static extern void InitManagedDelegates(RunDelegate run, ExecuteDelegate execute, ExecuteBackgroundDelegate executeBackground, ReloadDelegate reload, ExecuteDelegate list); static readonly object loaderLock = new object(); @@ -65,13 +64,23 @@ namespace FreeSWITCH { public static bool Load() { managedDir = Path.Combine(Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir, "managed"); shadowDir = Path.Combine(managedDir, "shadow"); - if (Directory.Exists(shadowDir)) { - Directory.Delete(shadowDir, true); + if (Directory.Exists(shadowDir)) + { + try + { + Directory.Delete(shadowDir, true); + } + catch (Exception ex) + { + Log.WriteLine(LogLevel.Warning, "Cannot delete shadow directory: {0}", ex); + } + Directory.CreateDirectory(shadowDir); } Log.WriteLine(LogLevel.Debug, "FreeSWITCH.Managed loader is starting with directory '{0}'.", managedDir); - if (!Directory.Exists(managedDir)) { + if (!Directory.Exists(managedDir)) + { Log.WriteLine(LogLevel.Error, "Managed directory not found: {0}", managedDir); return false; } @@ -220,11 +229,38 @@ namespace FreeSWITCH { setup.ConfigurationFile = fileName + ".config"; } setup.ApplicationBase = Native.freeswitch.SWITCH_GLOBAL_dirs.mod_dir; - setup.ShadowCopyDirectories = managedDir + ";"; setup.LoaderOptimization = LoaderOptimization.MultiDomainHost; // TODO: would MultiDomain work better since FreeSWITCH.Managed isn't gac'd? setup.CachePath = shadowDir; setup.ShadowCopyFiles = "true"; - setup.PrivateBinPath = "managed"; + + // computing private bin path + var binPath = setup.PrivateBinPath ?? string.Empty; + + var binPaths = binPath.Split(new[] { ";" }, StringSplitOptions.RemoveEmptyEntries) + .Select(x => x.Trim()) + .ToList(); + + // adding "managed" (modules) directory + if (!binPaths.Contains("managed", StringComparer.OrdinalIgnoreCase)) + { + binPaths.Add("managed"); + } + + // adding "managed/" directory for per-module references support + var moduleRefsDir = Path.GetFileName(fileName); + moduleRefsDir = Path.GetFileNameWithoutExtension(moduleRefsDir); + + if (moduleRefsDir != null && moduleRefsDir.Trim() != "") + { + moduleRefsDir = Path.Combine("managed", moduleRefsDir); + if (!binPaths.Contains(moduleRefsDir, StringComparer.OrdinalIgnoreCase)) + { + binPaths.Add(moduleRefsDir); + } + } + + // bringing all together + setup.PrivateBinPath = string.Join(";", binPaths); // Create domain and load PM inside System.Threading.Interlocked.Increment(ref appDomainCount); @@ -408,18 +444,33 @@ namespace FreeSWITCH { } } - public static bool List(string command) { - try { - Log.WriteLine(LogLevel.Info, "Available APIs:"); - getApiExecs().Values.ForEach(x => { - Log.WriteLine(LogLevel.Info, "{0}: {1}", x.Name, String.Join(",", x.Aliases.ToArray())); - }); - Log.WriteLine(LogLevel.Info, "Available Apps:"); - getAppExecs().Values.ForEach(x => { - Log.WriteLine(LogLevel.Info, "{0}: {1}", x.Name, String.Join(",", x.Aliases.ToArray())); - }); + public static bool List(string command, IntPtr streamHandle, IntPtr eventHandle) + { + try + { + if (streamHandle != IntPtr.Zero) + { + using (var stream = new Native.Stream(new Native.switch_stream_handle(streamHandle, false))) + { + stream.Write("Available APIs:\n"); + + getApiExecs().Values.ForEach(x => stream.Write(string.Format("{0}: {1}\n", x.Name, String.Join(",", x.Aliases.ToArray())))); + + stream.Write("Available Apps:\n"); + getAppExecs().Values.ForEach(x => stream.Write(string.Format("{0}: {1}\n", x.Name, String.Join(",", x.Aliases.ToArray())))); + } + } + else + { + Log.WriteLine(LogLevel.Info, "Available APIs:"); + getApiExecs().Values.ForEach(x => Log.WriteLine(LogLevel.Info, "{0}: {1}", x.Name, String.Join(",", x.Aliases.ToArray()))); + Log.WriteLine(LogLevel.Info, "Available Apps:"); + getAppExecs().Values.ForEach(x => Log.WriteLine(LogLevel.Info, "{0}: {1}", x.Name, String.Join(",", x.Aliases.ToArray()))); + } return true; - } catch (Exception ex) { + } + catch (Exception ex) + { Log.WriteLine(LogLevel.Error, "Exception listing managed modules: {0}", ex.ToString()); return false; } diff --git a/src/mod/languages/mod_managed/managed/Log.cs b/src/mod/languages/mod_managed/managed/Log.cs index 1c58ff1458..aa2bacf57e 100644 --- a/src/mod/languages/mod_managed/managed/Log.cs +++ b/src/mod/languages/mod_managed/managed/Log.cs @@ -57,6 +57,7 @@ namespace FreeSWITCH static string ToLogString(this LogLevel level) { switch (level) { + case LogLevel.Console: return "CONSOLE"; case LogLevel.Alert: return "ALERT"; case LogLevel.Critical: return "CRIT"; case LogLevel.Debug: return "DEBUG"; @@ -85,6 +86,7 @@ namespace FreeSWITCH };*/ public enum LogLevel { + Console, Debug, Info, Error, diff --git a/src/mod/languages/mod_managed/managed/ManagedSession.cs b/src/mod/languages/mod_managed/managed/ManagedSession.cs index bed7a03878..84718df60f 100644 --- a/src/mod/languages/mod_managed/managed/ManagedSession.cs +++ b/src/mod/languages/mod_managed/managed/ManagedSession.cs @@ -149,6 +149,23 @@ namespace FreeSWITCH.Native }; return del; } + + /// Wraps a nice handler into a delegate suitable for reverse P/Invoke. For native api using + public static switch_state_handler_t_delegate CreateStateHandlerDelegate(Action handler) + { + // We create a ManagedSession on top of the session so callbacks can use it "nicely" + // Then we sort of dispose it. + switch_state_handler_t_delegate del = ptr => + { + using (var sess = new ManagedSession(new SWIGTYPE_p_switch_core_session(ptr, false))) + { + handler(sess); + return switch_status_t.SWITCH_STATUS_SUCCESS; + } + }; + return del; + } + public static SWIGTYPE_p_f_p_switch_core_session__switch_status_t WrapStateHandlerDelegate(switch_state_handler_t_delegate del) { return new SWIGTYPE_p_f_p_switch_core_session__switch_status_t(Marshal.GetFunctionPointerForDelegate(del), false); } diff --git a/src/mod/languages/mod_managed/managed/Util.cs b/src/mod/languages/mod_managed/managed/Util.cs index 18c2d89369..7fd5194770 100644 --- a/src/mod/languages/mod_managed/managed/Util.cs +++ b/src/mod/languages/mod_managed/managed/Util.cs @@ -46,5 +46,31 @@ namespace FreeSWITCH { if (cons == null) throw new ArgumentException(ty.Name + " constructor not found."); return (T)cons.Invoke(new object[] { cPtr, false }); } + + /// + /// Getting IntPtr from wrapper + /// + /// swig generated class + /// instance + /// Original pointer + public static IntPtr GetPtr(T obj) + { + // internal static HandleRef getCPtr(CoreSession obj) + var ty = typeof(T); + var bflags = BindingFlags.Static | BindingFlags.NonPublic; + var getCPtr = ty.GetMethod("getCPtr", bflags, null, new[] { typeof(T) }, null); + + if (getCPtr != null) + { + var handleRef = getCPtr.Invoke(null, new object[] { obj }); + + if (handleRef is HandleRef) + { + return ((HandleRef)handleRef).Handle; + } + } + + return IntPtr.Zero; + } } } diff --git a/src/mod/languages/mod_managed/mod_managed.cpp b/src/mod/languages/mod_managed/mod_managed.cpp index 215a98336b..6718265d53 100644 --- a/src/mod/languages/mod_managed/mod_managed.cpp +++ b/src/mod/languages/mod_managed/mod_managed.cpp @@ -26,6 +26,7 @@ * Michael Giagnocavo * David Brazier * Jeff Lenk + * Artur Kraev * * mod_mono.cpp -- FreeSWITCH mod_mono main class * @@ -73,14 +74,13 @@ 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 (*reloadFunction)(const char* cmd); -typedef int (*listFunction)(const char* cmd); static runFunction runDelegate; static executeFunction executeDelegate; static executeBackgroundFunction executeBackgroundDelegate; static reloadFunction reloadDelegate; -static listFunction listDelegate; +static executeFunction listDelegate; -SWITCH_MOD_DECLARE_NONSTD(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground, reloadFunction reload, listFunction list) +SWITCH_MOD_DECLARE_NONSTD(void) InitManagedDelegates(runFunction run, executeFunction execute, executeBackgroundFunction executeBackground, reloadFunction reload, executeFunction list) { runDelegate = run; executeDelegate = execute; @@ -451,7 +451,7 @@ SWITCH_STANDARD_API(managedlist_api_function) #ifndef _MANAGED mono_thread_attach(globals.domain); #endif - listDelegate(cmd); + listDelegate(cmd, stream, stream->param_event); #ifndef _MANAGED mono_thread_detach(mono_thread_current()); #endif