@@ -3,10 +3,12 @@ package controller
33import (
44 "encoding/json"
55 "errors"
6+ "fmt"
67 "net"
78 "os"
89 "os/exec"
910 "path/filepath"
11+ "runtime"
1012 "strings"
1113 "sync"
1214 "sync/atomic"
@@ -81,7 +83,9 @@ func New(fsm *fsm.StateMachine, c lib.Config, valKey crypto.PrivateKeyI, metrics
8183 controller .loadCheckpointsFile ()
8284 // setup plugin if enabled
8385 if c .Plugin != "" {
84- controller .PluginExecute (c .Plugin )
86+ if err = controller .PluginExecute (c .Plugin ); err != nil {
87+ return nil , err
88+ }
8589 controller .PluginConnectSync ()
8690 }
8791 // initialize the consensus in the controller, passing a reference to itself
@@ -252,24 +256,25 @@ const socketDir = "/tmp/plugin"
252256const socketFile = "plugin.sock"
253257
254258// PluginExecute() executes the plugin control script to start the plugin process
255- func (c * Controller ) PluginExecute (plugin string ) {
259+ func (c * Controller ) PluginExecute (plugin string ) lib. ErrorI {
256260 if plugin == "" || strings .Contains (plugin , ".." ) || strings .ContainsRune (plugin , os .PathSeparator ) {
257- c .log .Errorf ("Invalid plugin name %q" , plugin )
258- return
261+ return lib .NewError (lib .NoCode , lib .MainModule , fmt .Sprintf ("invalid plugin name %q" , plugin ))
262+ }
263+ cmdPath , err := resolvePluginCtlPath (plugin )
264+ if err != nil {
265+ return lib .NewError (lib .NoCode , lib .MainModule , err .Error ())
259266 }
260- // construct the shell command path: plugin/<plugin>/pluginctl.sh start
261- cmdPath := filepath .Join ("plugin" , plugin , "pluginctl.sh" )
262267 // create the command to execute the plugin control script with 'start' argument
263268 cmd := exec .Command (cmdPath , "start" )
264269 // execute the command and capture output
265270 output , err := cmd .CombinedOutput ()
266271 // if an error occurred during execution
267272 if err != nil {
268- // log the error and exit
269- c .log .Errorf ("Failed to execute plugin %s: %v, output: %s" , plugin , err , string (output ))
273+ return lib .NewError (lib .NoCode , lib .MainModule , fmt .Sprintf ("failed to execute plugin %s: %v, output: %s" , plugin , err , string (output )))
270274 }
271275 // log successful plugin execution
272276 c .log .Infof ("Plugin %s started: %s" , plugin , string (output ))
277+ return nil
273278}
274279
275280// PluginConnectSync() blocking: enables a unix socket file where plugins can interact with the Canopy FSM
@@ -302,6 +307,31 @@ func (c *Controller) PluginConnectSync() {
302307 c .FSM .Plugin , c .Mempool .FSM .Plugin = c .Plugin , c .Plugin
303308}
304309
310+ func resolvePluginCtlPath (plugin string ) (string , error ) {
311+ relPath := filepath .Join ("plugin" , plugin , "pluginctl.sh" )
312+ candidates := []string {
313+ relPath ,
314+ }
315+ if exePath , err := os .Executable (); err == nil {
316+ exeDir := filepath .Dir (exePath )
317+ candidates = append (candidates ,
318+ filepath .Join (exeDir , relPath ),
319+ filepath .Join (filepath .Dir (exeDir ), relPath ),
320+ )
321+ }
322+ if _ , sourceFile , _ , ok := runtime .Caller (0 ); ok {
323+ repoRoot := filepath .Dir (filepath .Dir (sourceFile ))
324+ candidates = append (candidates , filepath .Join (repoRoot , relPath ))
325+ }
326+ for _ , candidate := range candidates {
327+ info , err := os .Stat (candidate )
328+ if err == nil && ! info .IsDir () {
329+ return candidate , nil
330+ }
331+ }
332+ return "" , fmt .Errorf ("plugin launcher not found for %q; checked: %s" , plugin , strings .Join (candidates , ", " ))
333+ }
334+
305335// INTERNAL CALLS BELOW
306336
307337// LoadIsOwnRoot() returns if this chain is its own root (base)
0 commit comments