github.com/cs3org/reva/v2@v2.27.7/pkg/plugin/loader.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package plugin 20 21 import ( 22 "bytes" 23 "fmt" 24 "os" 25 "os/exec" 26 "path/filepath" 27 "regexp" 28 29 "github.com/cs3org/reva/v2/pkg/errtypes" 30 "github.com/hashicorp/go-hclog" 31 "github.com/hashicorp/go-plugin" 32 ) 33 34 // RevaPlugin represents the runtime plugin 35 type RevaPlugin struct { 36 Plugin interface{} 37 Client *plugin.Client 38 } 39 40 const dirname = "/var/tmp/reva" 41 42 var isAlphaNum = regexp.MustCompile(`^[A-Za-z0-9]+$`).MatchString 43 44 // Kill kills the plugin process 45 func (plug *RevaPlugin) Kill() { 46 plug.Client.Kill() 47 } 48 49 var handshake = plugin.HandshakeConfig{ 50 ProtocolVersion: 1, 51 MagicCookieKey: "BASIC_PLUGIN", 52 MagicCookieValue: "hello", 53 } 54 55 func compile(pluginType string, path string) (string, error) { 56 var errb bytes.Buffer 57 binaryPath := filepath.Join(dirname, "bin", pluginType, filepath.Base(path)) 58 command := fmt.Sprintf("go build -o %s %s", binaryPath, path) 59 cmd := exec.Command("bash", "-c", command) 60 cmd.Stderr = &errb 61 err := cmd.Run() 62 if err != nil { 63 return "", fmt.Errorf("%v: %w", errb.String(), err) 64 } 65 return binaryPath, nil 66 } 67 68 // checkDir checks and compiles plugin if the configuration points to a directory. 69 func checkDirAndCompile(pluginType, driver string) (string, error) { 70 bin := driver 71 file, err := os.Stat(driver) 72 if err != nil { 73 return "", err 74 } 75 // compile if we point to a package 76 if file.IsDir() { 77 bin, err = compile(pluginType, driver) 78 if err != nil { 79 return "", err 80 } 81 } 82 return bin, nil 83 } 84 85 // Load loads the plugin using the hashicorp go-plugin system 86 func Load(pluginType, driver string) (*RevaPlugin, error) { 87 if isAlphaNum(driver) { 88 return nil, errtypes.NotFound(driver) 89 } 90 bin, err := checkDirAndCompile(pluginType, driver) 91 if err != nil { 92 return nil, err 93 } 94 95 logger := hclog.New(&hclog.LoggerOptions{ 96 Name: "plugin", 97 Output: os.Stdout, 98 Level: hclog.Trace, 99 }) 100 101 client := plugin.NewClient(&plugin.ClientConfig{ 102 HandshakeConfig: handshake, 103 Plugins: PluginMap, 104 Cmd: exec.Command(bin), 105 AllowedProtocols: []plugin.Protocol{ 106 plugin.ProtocolNetRPC, 107 }, 108 Logger: logger, 109 }) 110 111 rpcClient, err := client.Client() 112 if err != nil { 113 return nil, err 114 } 115 116 raw, err := rpcClient.Dispense(pluginType) 117 if err != nil { 118 return nil, err 119 } 120 121 revaPlugin := &RevaPlugin{ 122 Plugin: raw, 123 Client: client, 124 } 125 126 return revaPlugin, nil 127 }