package step import "os" import "errors" import "plugin" import "syscall" import "path/filepath" // LoadAllProviderPlugins loads all plugins according to the given search path. // // If a non-nil error is returned, the error should be treated as a list of // warning messages and the return value should be used normally. func LoadAllProviderPlugins (pluginPath ...string) ([]Provider, error) { return loadAllProviderPlugins(pluginPath, true) } // LoadAllProviderPluginsUnsafe is like LoadAllProviderPlugins, but does not // check to see that the files are owned by root, thereby making it easier to // run a random plugin you just compiled. This should not be used otherwise. // // If a non-nil error is returned, the error should be treated as a list of // warning messages and the return value should be used normally. func LoadAllProviderPluginsUnsafe (pluginPath ...string) ([]Provider, error) { return loadAllProviderPlugins(pluginPath, false) } func loadAllProviderPlugins (pluginPath []string, checkRoot bool) ([]Provider, error) { providers := []Provider { } errs := []error { } for _, pat := range pluginPath { entries, err := os.ReadDir(pat) if err != nil { continue } for _, entry := range entries { pluginPath := filepath.Join(pat, entry.Name()) ext := filepath.Ext(pluginPath) if ext != ".so" { continue } provider, err := loadProviderPlugin(pluginPath, checkRoot) if err != nil { errs = append(errs, err) continue } providers = append(providers, provider) } } return providers, errors.Join(errs...) } // LoadProviderPlugin loads a plugin given its file path. The file must: // // - Be a shared library // - Be addressed with an absolute path // - Be built with go -buildmode=plugin // - Be built with the same version of Go // - Be built with the same version of STEP // - Be owned by root // // Plugins cannot be unloaded from the current program once they are loaded. // Sorgy :( its Go's fault. func LoadProviderPlugin (name string) (Provider, error) { return loadProviderPlugin(name, true) } // LoadProviderPluginUnsafe is like LoadProviderPlugin, but does not check to // see that the file is owned by root, thereby making it easier to run a random // plugin you just compiled. This should not be used otherwise. func LoadProviderPluginUnsafe (name string) (Provider, error) { return loadProviderPlugin(name, false) } func loadProviderPlugin (name string, checkRoot bool) (Provider, error) { plugin, err := loadPlugin(name, checkRoot) if err != nil { return nil, err } providerSymbol, err := plugin.Lookup("NewProvider") if err != nil { return nil, err } providerFactory, ok := providerSymbol.(func () Provider) if !ok { return nil, ErrPluginBadSymbol } provider := providerFactory() return provider, nil } func loadPlugin (name string, checkRoot bool) (*plugin.Plugin, error) { name = filepath.Clean(name) if !filepath.IsAbs(name) { return nil, ErrPathNotAbsolute } if checkRoot { info, err := os.Stat(name) if err != nil { return nil, err } if info, ok := info.Sys().(*syscall.Stat_t); ok { if info.Uid != 0 { return nil, ErrPluginNotOwnedByRoot } } else { return nil, ErrInsufficientSystem } } return plugin.Open(name) }