step/plugin.go

99 lines
3.2 KiB
Go

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)
}