private ProcessInfo StartProcess(string dirName, params string[] requestedExtensionIDs)
 {
     string[] foundExtensionIDs, activeExtensionIDs;
     try
     {
         using(var loader = new SafeExtensionLoader(_extensionsBasePath, dirName, "", null))
             foundExtensionIDs = loader.AvailableExtensions.Select(e => e.ExtensionID).ToArray();
     }
     catch(FileNotFoundException ex)
     {
         _logger.Debug("Directory not found: " + Path.Combine(_extensionsBasePath, dirName));
         _logger.Error("Unable to start extension process - the extension subdirectory \"" + dirName + "\" does not exist or does not contain a valid set of extension assemblies.");
         return null;
     }
     if(requestedExtensionIDs == null || requestedExtensionIDs.Length == 0)
         activeExtensionIDs = foundExtensionIDs;
     else
         activeExtensionIDs = requestedExtensionIDs.Where(e => foundExtensionIDs.Contains(e)).ToArray();
     var info = new ProcessInfo
     {
         ID = Guid.NewGuid(),
         DirectoryName = dirName,
         RequestedExtensionIDs = requestedExtensionIDs,
         ActiveExtensionIDs = activeExtensionIDs,
         Timeout = DateTime.UtcNow.Add(_monitorInterval).AddSeconds(ExtensionStartupSeconds) // we add 5 seconds to give the process time to start
     };
     _logger.Info("Starting new process for extensions in /" + dirName + " - " + info.ID + " (" + (activeExtensionIDs.Length == 0 ? "all" : activeExtensionIDs.Concat(", ")) + ")");
     var cmdargs = string.Format(
         "-subdir \"{0}\" -basedir \"{1}\" -pid={2} -guid \"{3}\"{4}",
         dirName, _extensionsBasePath, Process.GetCurrentProcess().Id, info.ID, activeExtensionIDs.Concat(s => " " + s)
     );
     var psi = new ProcessStartInfo(_launcherExePath, cmdargs)
     {
         ErrorDialog = false,
         CreateNoWindow = true,
         RedirectStandardError = true,
         RedirectStandardOutput = true,
         WorkingDirectory = Path.Combine(_extensionsBasePath, dirName),
         UseShellExecute = false
     };
     info.Process = Process.Start(psi);
     return info;
 }
Beispiel #2
0
        static void Main(string[] args)
        {
            ConfigurationItemFactory.Default.Targets.RegisterDefinition("ServiceManager", typeof(ServiceManagerTarget));

            string subdir = null, runDebugMethodOnExtension = null;
            var baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Extensions");
            Environment.CurrentDirectory = ConfigurationManager.AppSettings["DataDirectory"] ?? AppDomain.CurrentDomain.BaseDirectory;
            var extensionIDs = new HashSet<string>();
            Process process = null;
            Guid guid = Guid.Empty;
            Logger logger = null;

            var options = new OptionSet
            {
                { "guid=", "Specifies a GUID that the extension can use to identify itself to the parent process", v =>
                    {
                        Guid id;
                        if(!Guid.TryParse(v, out id))
                            throw new OptionException("The specified id was not a valid GUID", "guid");
                        guid = id;
                    }
                },
                { "basedir=", "Specifies the base plugins directory (can be relative or absolute)", v => baseDir = Path.IsPathRooted(v) ? v : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, v) },
                { "subdir=", "Specifies the extension subdirectory name", v => subdir = v },
                { "debug=", "Specifies an extension ID to run the debug method on", v => runDebugMethodOnExtension = v },
                { "pid=", "Parent process ID - if specified, this process will close when the parent process closes", v =>
                    {
                        int pid;
                        if(!int.TryParse(v, out pid))
                            throw new OptionException("The parent process ID must be a 32 bit integer", "pid");
                        try
                        {
                            process = Process.GetProcessById(pid);
                        }
                        catch(Exception ex)
                        {
                            throw new OptionException(ex.Message, "pid");
                        }
                        if(process == null)
                            throw new OptionException("There is no process with ID [" + pid + "]", "pid");
                    }
                },
                { "<>", v => extensionIDs.Add(v) }
            };

            CancellationTokenSource src = new CancellationTokenSource();
            try
            {
                options.Parse(args);
                if(subdir == null)
                {
                    Console.Write("Enter plugin directory name (not the full path): ");
                    subdir = Console.ReadLine();
                    if(string.IsNullOrWhiteSpace(subdir))
                    {
                        Console.WriteLine("No plugin directory specified.");
                        Exit(null, src, ExtensionRunnerExitCode.InvalidArguments);
                    }
                }

                GlobalDiagnosticsContext.Set("ExeBaseDir", new FileInfo(Assembly.GetExecutingAssembly().Location).Directory.FullName);
                GlobalDiagnosticsContext.Set("SubDirName", subdir);
                GlobalDiagnosticsContext.Set("ParentProcess", process == null ? "" : process.Id.ToString());
                logger = LogManager.GetCurrentClassLogger();
                logger.Info(new [] {
                    "ExtensionRunner Started:",
                    "	=> Command Line: " + Environment.CommandLine,
                    "	=> Subdirectory: " + subdir,
                    "	=> Base Directory: " + baseDir,
                    "	=> Specified Extensions: " + extensionIDs.Concat(", "),
                    "	=> GUID: " + guid,
                    "	=> Parent Process ID: " + (process == null ? "(none)" : process.Id.ToString())
                }.Concat(Environment.NewLine));

                AppDomain.CurrentDomain.UnhandledException += (s,e) => logger.FatalException("UNTRAPPED SERVICE EXCEPTION", (Exception)e.ExceptionObject);
                TaskScheduler.UnobservedTaskException += (s,e) => logger.FatalException("UNTRAPPED TASK EXCEPTION:", e.Exception);

                if(process != null)
                {
                    Task.Factory.StartNew(() =>
                    {
                        while(!src.IsCancellationRequested)
                        {
                            process.Refresh();
                            if(process.HasExited)
                            {
                                logger.Warn("Detected parent process shutdown.");
                                Exit(logger, src, ExtensionRunnerExitCode.ParentExited);
                                return;
                            }
                            Thread.Sleep(250);
                        }
                    });
                }

                // Read list of available extensions
                Dictionary<string, ExtensionInfo> extInfos;
                using(var loader = new SafeExtensionLoader(baseDir, subdir, process == null ? "" : process.Id.ToString(), src))
                    extInfos = loader.AvailableExtensions.ToDictionary(x => x.ExtensionID, x => x.Clone());

                if(extensionIDs.Count == 0)
                    extensionIDs = new HashSet<string>(extInfos.Select(x => x.Key)); // use all available extensions
                else
                    extensionIDs = new HashSet<string>(extensionIDs.Where(x => extInfos.ContainsKey(x))); // eliminate invalid any extension IDs
                logger.Info("Active extensions: " + (extensionIDs.Any() ? extensionIDs.Concat(", ") : "(none)"));
                logger.Info("Inactive extensions: " + (!extensionIDs.Any() ? extInfos.Where(x => !extensionIDs.Contains(x.Key)).Concat(", ") : "(none)"));

                var extLoaders = new List<SafeExtensionLoader>();
                var extTasks = new List<Task>();
                try
                {
                    foreach(var id in extensionIDs)
                    {
                        logger.Debug("Starting appdomain for extension: {0}", id);
                        var loader = new SafeExtensionLoader(baseDir, subdir, process == null ? "" : process.Id.ToString(), src);
                        var extID = id;
                        extTasks.Add(Task.Factory.StartNew(() => loader.RunExtension(guid, runDebugMethodOnExtension == extID, extID)));
                    }
                    Task.WaitAll(extTasks.ToArray(), src.Token);
                }
                finally
                {
                    foreach(var extLoader in extLoaders)
                        extLoader.Dispose();
                }
                //using(var loader = new SafeExtensionLoader(baseDir, subdir, process == null ? "" : process.Id.ToString(), src))
                //{
                //    var runExtsTask = Task.Factory.StartNew(() =>
                //    {
                //        // Verify that all extensions are available and if so, run them
                //        var sb = new StringBuilder();
                //        sb.AppendLine("[list of all plugins]");
                //        foreach(var extInfo in loader.AllExtensions)
                //            sb.AppendLine("\t" + extInfo.ExtensionID + ": " + extInfo.Name + " [" + (extensionIDs.Count == 0 || extensionIDs.Contains(extInfo.ExtensionID) ? "ACTIVE" : "INACTIVE") + "]");
                //        logger.Info(sb.ToString());
                //        loader.RunExtensions(guid, runDebugMethodOnExtension, extensionIDs.ToArray());
                //    }, src.Token);

                //    loader.RunMainAppThread();
                //    Task.WaitAll(new[] { runExtsTask }, src.Token);
                //}
            }
            catch(OptionException ex)
            {
                if(logger != null)
                    logger.Error("Invalid command options: " + ex.Message, options.WriteOptionDescriptions());
                Exit(logger, src, ExtensionRunnerExitCode.Exception);
            }
            catch(Exception ex)
            {
                if(logger != null)
                    logger.FatalException("An exception was thrown", ex);
                Exit(logger, src, ExtensionRunnerExitCode.Exception);
            }
            finally
            {
                Exit(logger, src, ExtensionRunnerExitCode.Success);
            }
        }
Beispiel #3
0
 //public string[] ListIncludedExtensionDirectories()
 //{
 //    throw new NotImplementedException();
 //}
 //public ExtensionInfo[] ListAvailableExtensions()
 //{
 //    throw new NotImplementedException();
 //}
 public ExtensionInfo[] ListExtensionsInDirectory(string name)
 {
     using(var pl = new SafeExtensionLoader(_extensionsBaseDir.FullName, name, "", null))
         return pl.AvailableExtensions;
 }