plugins.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package plugins
  2. import (
  3. "errors"
  4. "os"
  5. "os/exec"
  6. "path/filepath"
  7. "sync"
  8. "time"
  9. "imuslab.com/zoraxy/mod/database"
  10. "imuslab.com/zoraxy/mod/info/logger"
  11. )
  12. type Plugin struct {
  13. Spec *IntroSpect //The plugin specification
  14. Process *exec.Cmd //The process of the plugin
  15. Enabled bool //Whether the plugin is enabled
  16. }
  17. type ManagerOptions struct {
  18. ZoraxyVersion string
  19. PluginDir string
  20. SystemConst *RuntimeConstantValue
  21. Database database.Database
  22. Logger *logger.Logger
  23. }
  24. type Manager struct {
  25. LoadedPlugins sync.Map //Storing *Plugin
  26. Options *ManagerOptions
  27. }
  28. func NewPluginManager(options *ManagerOptions) *Manager {
  29. return &Manager{
  30. LoadedPlugins: sync.Map{},
  31. Options: options,
  32. }
  33. }
  34. // LoadPlugins loads all plugins from the plugin directory
  35. func (m *Manager) LoadPlugins() error {
  36. // Load all plugins from the plugin directory
  37. foldersInPluginDir, err := os.ReadDir(m.Options.PluginDir)
  38. if err != nil {
  39. return err
  40. }
  41. for _, folder := range foldersInPluginDir {
  42. if folder.IsDir() {
  43. pluginPath := filepath.Join(m.Options.PluginDir, folder.Name())
  44. thisPlugin, err := m.LoadPluginSpec(pluginPath)
  45. if err != nil {
  46. m.Log("Failed to load plugin: "+filepath.Base(pluginPath), err)
  47. continue
  48. }
  49. m.LoadedPlugins.Store(thisPlugin.Spec.ID, thisPlugin)
  50. }
  51. }
  52. return nil
  53. }
  54. // GetPluginByID returns a plugin by its ID
  55. func (m *Manager) GetPluginByID(pluginID string) (*Plugin, error) {
  56. plugin, ok := m.LoadedPlugins.Load(pluginID)
  57. if !ok {
  58. return nil, errors.New("plugin not found")
  59. }
  60. return plugin.(*Plugin), nil
  61. }
  62. // EnablePlugin enables a plugin
  63. func (m *Manager) EnablePlugin(pluginID string) error {
  64. plugin, ok := m.LoadedPlugins.Load(pluginID)
  65. if !ok {
  66. return errors.New("plugin not found")
  67. }
  68. plugin.(*Plugin).Enabled = true
  69. return nil
  70. }
  71. // DisablePlugin disables a plugin
  72. func (m *Manager) DisablePlugin(pluginID string) error {
  73. plugin, ok := m.LoadedPlugins.Load(pluginID)
  74. if !ok {
  75. return errors.New("plugin not found")
  76. }
  77. thisPlugin := plugin.(*Plugin)
  78. thisPlugin.Process.Process.Signal(os.Interrupt)
  79. go func() {
  80. //Wait for 10 seconds for the plugin to stop gracefully
  81. time.Sleep(10 * time.Second)
  82. if thisPlugin.Process.ProcessState == nil || !thisPlugin.Process.ProcessState.Exited() {
  83. m.Log("Plugin "+thisPlugin.Spec.Name+" failed to stop gracefully, killing it", nil)
  84. thisPlugin.Process.Process.Kill()
  85. } else {
  86. m.Log("Plugin "+thisPlugin.Spec.Name+" background process stopped", nil)
  87. }
  88. }()
  89. thisPlugin.Enabled = false
  90. return nil
  91. }
  92. // Terminate all plugins and exit
  93. func (m *Manager) Close() {
  94. m.LoadedPlugins.Range(func(key, value interface{}) bool {
  95. plugin := value.(*Plugin)
  96. if plugin.Enabled {
  97. m.DisablePlugin(plugin.Spec.ID)
  98. }
  99. return true
  100. })
  101. //Wait until all loaded plugin process are terminated
  102. m.BlockUntilAllProcessExited()
  103. }