1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
// Copyright (c) 2018-2020, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.
package plugin
import (
"errors"
"fmt"
"plugin"
"strings"
"sync"
callback "github.com/sylabs/singularity/v4/internal/pkg/plugin/callback"
pluginapi "github.com/sylabs/singularity/v4/pkg/plugin"
)
type loadedPlugins struct {
metas []*Meta
plugins map[string]struct{}
sync.Mutex
}
var lp loadedPlugins
// LoadCallbacks loads plugins registered for the hook instance passed in parameter.
func LoadCallbacks(cb pluginapi.Callback) ([]pluginapi.Callback, error) {
callbackName := callback.Name(cb)
if err := initMetaPlugin(); err != nil {
return nil, err
}
var errs []error
for _, meta := range lp.metas {
if !meta.Enabled {
continue
}
for _, name := range meta.Callbacks {
if name == callbackName {
if err := loadCallbacks(meta.binaryName()); err != nil {
// This might be destroying information by
// grabbing only the textual description of the
// error
wrappedErr := fmt.Errorf("while initializing plugin %q: %s", meta.Name, err)
errs = append(errs, wrappedErr)
}
}
}
}
if len(errs) > 0 {
// Collect all the errors into a single one that can be
// returned.
//
// Beware that we are destroying information that might
// be part of the type underlying the error interface we
// are getting here. UI-wise this might not be ideal,
// because the user might end up seeing a bunch of
// errors "slightly" separated by "; ".
//
// The alternative is to implement a type that collects
// the individual errors and implements the error
// interface by doing something similar to this. If
// there's some code that needs to handle errors in a
// more discrete way, it could type-assert an interface
// to check if it's possible to obtain the individual
// errors.
var b strings.Builder
for i, err := range errs {
if i > 0 {
b.WriteString("; ")
}
b.WriteString(err.Error())
}
return nil, errors.New(b.String())
}
return callback.Loaded(cb)
}
// initMetaPlugin reads plugin metadata files and stores data
// in the loaded plugin instance.
func initMetaPlugin() error {
var err error
lp.Lock()
defer lp.Unlock()
if lp.metas != nil {
return nil
}
if lp.plugins == nil {
lp.plugins = make(map[string]struct{})
}
lp.metas, err = List()
if err != nil {
return fmt.Errorf("while getting plugin's metadata: %s", err)
}
return nil
}
// loadCallbacks loads the plugin and the plugin callbacks.
func loadCallbacks(path string) error {
lp.Lock()
defer lp.Unlock()
if _, ok := lp.plugins[path]; ok {
return nil
}
pl, err := LoadObject(path)
if err != nil {
return err
}
lp.plugins[path] = struct{}{}
for _, c := range pl.Callbacks {
callback.Load(c)
}
return nil
}
// LoadObject loads a plugin object in memory and returns
// the Plugin object set within the plugin.
func LoadObject(path string) (*pluginapi.Plugin, error) {
pluginPointer, err := plugin.Open(path)
if err != nil {
return nil, err
}
pluginObject, err := getPluginObject(pluginPointer)
if err != nil {
return nil, err
}
return pluginObject, nil
}
func getPluginObject(pl *plugin.Plugin) (*pluginapi.Plugin, error) {
sym, err := pl.Lookup(pluginapi.PluginSymbol)
if err != nil {
return nil, err
}
p, ok := sym.(*pluginapi.Plugin)
if !ok {
return nil, fmt.Errorf("symbol \"Plugin\" not of type Plugin")
}
return p, nil
}
|