2008-09-25 05:16:16 +00:00
|
|
|
|
/*
|
|
|
|
|
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_cli
|
|
|
|
|
* Copyright (C) 2008, Michael Giagnocavo <mgg@packetrino.com>
|
|
|
|
|
*
|
|
|
|
|
* Version: MPL 1.1
|
|
|
|
|
*
|
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
|
*
|
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
|
* License.
|
|
|
|
|
*
|
|
|
|
|
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application - mod_cli
|
|
|
|
|
*
|
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
|
* Michael Giagnocavo <mgg@packetrino.com>
|
|
|
|
|
* Portions created by the Initial Developer are Copyright (C)
|
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* Contributor(s):
|
|
|
|
|
*
|
|
|
|
|
* Michael Giagnocavo <mgg@packetrino.com>
|
|
|
|
|
* David Brazier <David.Brazier@360crm.co.uk>
|
|
|
|
|
*
|
|
|
|
|
* Loader.cs -- mod_mono managed loader
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection;
|
2008-10-04 21:29:32 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
2008-09-25 05:16:16 +00:00
|
|
|
|
|
|
|
|
|
namespace FreeSWITCH
|
|
|
|
|
{
|
|
|
|
|
internal static class Loader
|
|
|
|
|
{
|
|
|
|
|
// Stores a list of the loaded function types so we can instantiate them as needed
|
|
|
|
|
static readonly Dictionary<string, Type> functions = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
|
|
|
|
|
// Only class name. Last in wins.
|
|
|
|
|
static readonly Dictionary<string, Type> shortFunctions = new Dictionary<string, Type>(StringComparer.InvariantCultureIgnoreCase);
|
|
|
|
|
|
|
|
|
|
#region Load/Unload
|
|
|
|
|
|
|
|
|
|
public static bool Load()
|
|
|
|
|
{
|
|
|
|
|
string 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);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-04 09:36:52 +00:00
|
|
|
|
AppDomain.CurrentDomain.AssemblyResolve += (_, rargs) => {
|
|
|
|
|
Log.WriteLine(LogLevel.Debug, "Trying to resolve assembly '{0}'.", rargs.Name);
|
|
|
|
|
if (rargs.Name == Assembly.GetExecutingAssembly().FullName) return Assembly.GetExecutingAssembly();
|
|
|
|
|
var path = Path.Combine(managedDir, rargs.Name + ".dll");
|
|
|
|
|
Log.WriteLine(LogLevel.Debug, "Resolving to: '" + path + "'.");
|
|
|
|
|
return File.Exists(path) ? Assembly.LoadFile(path) : null;
|
|
|
|
|
};
|
|
|
|
|
|
2008-10-04 21:29:32 +00:00
|
|
|
|
InitManagedDelegates(_run, _execute, _executeBackground);
|
|
|
|
|
|
2008-09-25 05:16:16 +00:00
|
|
|
|
// 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();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2008-10-04 21:29:32 +00:00
|
|
|
|
delegate bool ExecuteDelegate(string cmd, IntPtr streamH, IntPtr eventH);
|
|
|
|
|
delegate bool ExecuteBackgroundDelegate(string cmd);
|
|
|
|
|
delegate bool RunDelegate(string cmd, IntPtr session);
|
|
|
|
|
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);
|
|
|
|
|
|
2008-09-25 05:16:16 +00:00
|
|
|
|
// Be rather lenient in finding the Load and Unload methods
|
|
|
|
|
static readonly BindingFlags methodBindingFlags =
|
|
|
|
|
BindingFlags.Static | // Required
|
|
|
|
|
BindingFlags.Public | BindingFlags.NonPublic | // Implementors might decide to make the load method private
|
|
|
|
|
BindingFlags.IgnoreCase | // Some case insensitive languages?
|
|
|
|
|
BindingFlags.FlattenHierarchy; // Allow inherited methods for hierarchies
|
|
|
|
|
|
|
|
|
|
static void loadFunctions(this List<Type> allTypes)
|
|
|
|
|
{
|
|
|
|
|
foreach (var t in allTypes) {
|
|
|
|
|
try {
|
|
|
|
|
if (functions.ContainsKey(t.FullName)) {
|
|
|
|
|
Log.WriteLine(LogLevel.Error, "Duplicate function {0}. Skipping.", t.FullName);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
if (shouldLoad) {
|
|
|
|
|
Log.WriteLine(LogLevel.Notice, "Function {0} loaded.", t.FullName);
|
|
|
|
|
functions.Add(t.FullName, t);
|
|
|
|
|
shortFunctions[t.Name] = t;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
Log.WriteLine(LogLevel.Notice, "Function {0} requested not to be loaded.", t.FullName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
logException("Load", t.FullName, ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Assembly[] loadAssemblies(string managedDir)
|
|
|
|
|
{
|
|
|
|
|
// load the modules in the mod/managed directory
|
|
|
|
|
Log.WriteLine(LogLevel.Notice, "loadAssemblies: {0}", managedDir);
|
|
|
|
|
foreach (string s in Directory.GetFiles(managedDir, "*.dll", SearchOption.AllDirectories))
|
|
|
|
|
{
|
|
|
|
|
string f = Path.Combine(managedDir, s);
|
|
|
|
|
try {
|
2008-10-04 09:36:52 +00:00
|
|
|
|
Log.WriteLine(LogLevel.Debug, "Loading '{0}'.", f);
|
2008-09-25 05:16:16 +00:00
|
|
|
|
System.Reflection.Assembly.LoadFile(f);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
Log.WriteLine(LogLevel.Notice, "Assembly.LoadFile failed; skipping {0} ({1})", f, ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return AppDomain.CurrentDomain.GetAssemblies(); // Includes anything else already loaded
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Unload()
|
|
|
|
|
{
|
|
|
|
|
Log.WriteLine(LogLevel.Debug, "mod_managed_lib Loader is unloading.");
|
|
|
|
|
foreach (var t in functions.Values) {
|
|
|
|
|
try {
|
|
|
|
|
var unloadMethod = t.GetMethod("Unload", methodBindingFlags, null, Type.EmptyTypes, null);
|
|
|
|
|
unloadMethod.Invoke(null, null);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
logException("Unload", t.FullName, ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Execution
|
|
|
|
|
|
|
|
|
|
static Type getFunctionType<TFunction>(string fullName)
|
|
|
|
|
{
|
|
|
|
|
Type t;
|
|
|
|
|
if (!functions.TryGetValue(fullName, out t) || !t.IsSubclassOf(typeof(TFunction))) {
|
|
|
|
|
if (!shortFunctions.TryGetValue(fullName, out t) || !t.IsSubclassOf(typeof(TFunction))) {
|
|
|
|
|
Log.WriteLine(LogLevel.Error, "Could not find function {0}.", fullName);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static readonly char[] spaceArray = new[] { ' ' };
|
|
|
|
|
/// <summary>Returns a string couple containing the module name and arguments</summary>
|
|
|
|
|
static string[] parseCommand(string command)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(command)) {
|
|
|
|
|
Log.WriteLine(LogLevel.Error, "No arguments supplied.");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
var args = command.Split(spaceArray, 2, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
|
if (args.Length == 0 || string.IsNullOrEmpty(args[0]) || string.IsNullOrEmpty(args[0].Trim())) {
|
|
|
|
|
Log.WriteLine(LogLevel.Error, "Module name not supplied.");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
if (args.Length == 1) {
|
|
|
|
|
return new[] { args[0], "" };
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool ExecuteBackground(string command)
|
|
|
|
|
{
|
2008-10-04 21:29:32 +00:00
|
|
|
|
try {
|
|
|
|
|
var parsed = parseCommand(command);
|
|
|
|
|
if (parsed == null) return false;
|
|
|
|
|
var fullName = parsed[0];
|
|
|
|
|
var args = parsed[1];
|
|
|
|
|
|
|
|
|
|
var fType = getFunctionType<ApiFunction>(fullName);
|
|
|
|
|
if (fType == null) return false;
|
|
|
|
|
|
|
|
|
|
new System.Threading.Thread(() => {
|
|
|
|
|
try {
|
|
|
|
|
var f = (ApiFunction)Activator.CreateInstance(fType);
|
|
|
|
|
f.ExecuteBackground(args);
|
|
|
|
|
Log.WriteLine(LogLevel.Debug, "ExecuteBackground in {0} completed.", fullName);
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
logException("ExecuteBackground", fullName, ex);
|
|
|
|
|
}
|
|
|
|
|
}).Start();
|
|
|
|
|
return true;
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
Log.WriteLine(LogLevel.Error, "Exception in ExecuteBackground({0}): {1}", command, ex.ToString());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2008-09-25 05:16:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool Execute(string command, IntPtr streamHandle, IntPtr eventHandle)
|
|
|
|
|
{
|
|
|
|
|
System.Diagnostics.Debug.Assert(streamHandle != IntPtr.Zero, "streamHandle is null.");
|
|
|
|
|
var parsed = parseCommand(command);
|
|
|
|
|
if (parsed == null) return false;
|
|
|
|
|
var fullName = parsed[0];
|
|
|
|
|
var args = parsed[1];
|
|
|
|
|
|
|
|
|
|
var fType = getFunctionType<ApiFunction>(fullName);
|
|
|
|
|
if (fType == null) return false;
|
|
|
|
|
|
|
|
|
|
using (var stream = new Native.Stream(new Native.switch_stream_handle(streamHandle, false)))
|
|
|
|
|
using (var evt = eventHandle == IntPtr.Zero ? null : new Native.Event(new Native.switch_event(eventHandle, false), 0)) {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
var f = (ApiFunction)Activator.CreateInstance(fType);
|
|
|
|
|
f.Execute(stream, evt, args);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
logException("Execute", fullName, ex);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>Runs an application function.</summary>
|
|
|
|
|
public static bool Run(string command, IntPtr sessionHandle)
|
|
|
|
|
{
|
|
|
|
|
Log.WriteLine(LogLevel.Debug, "mod_mono 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;
|
|
|
|
|
var fullName = parsed[0];
|
|
|
|
|
var args = parsed[1];
|
|
|
|
|
|
|
|
|
|
var fType = getFunctionType<AppFunction>(fullName);
|
|
|
|
|
if (fType == null) return false;
|
|
|
|
|
|
|
|
|
|
using (var session = new Native.ManagedSession(new Native.SWIGTYPE_p_switch_core_session(sessionHandle, false))) {
|
|
|
|
|
session.Initialize();
|
|
|
|
|
session.SetAutoHangup(false);
|
|
|
|
|
try {
|
|
|
|
|
var f = (AppFunction)Activator.CreateInstance(fType);
|
|
|
|
|
f.RunInternal(session, args);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex) {
|
|
|
|
|
logException("Run", fullName, ex);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void logException(string action, string moduleName, Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Log.WriteLine(LogLevel.Error, "{0} exception in {1}: {2}", action, moduleName, ex.Message);
|
|
|
|
|
Log.WriteLine(LogLevel.Debug, "{0} exception: {1}", moduleName, ex.ToString());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|