55// the plugin interface.
66//
77// Plugins are external binaries that serve gRPC requests over a UNIX domain socket on the local machine. Each plugin
8- // creates a socket using its name (which must be unique across all plugins used locally). Plugins make use of the
9- // protobuf "Any" type in order to allow user-defined inputs and outputs to keep strong typing across application and
10- // language boundaries.
8+ // creates a socket using a unique identifier passed as an argument from the host application to the plugin. Plugins
9+ // make use of the protobuf "Any" type in order to allow user-defined inputs and outputs to keep strong typing across
10+ // application and language boundaries.
1111package plugin
1212
1313import (
@@ -23,6 +23,7 @@ import (
2323 "syscall"
2424 "time"
2525
26+ "github.com/rs/xid"
2627 "golang.org/x/sync/errgroup"
2728 "google.golang.org/grpc"
2829 "google.golang.org/grpc/codes"
@@ -102,6 +103,10 @@ func Run(config Config) error {
102103 ctx , cancel := signal .NotifyContext (context .Background (), syscall .SIGINT , syscall .SIGTERM , syscall .SIGKILL )
103104 defer cancel ()
104105
106+ if len (os .Args ) == 0 {
107+ return errors .New ("plugin expects at least one argument" )
108+ }
109+
105110 server := grpc .NewServer ()
106111
107112 info := plugin.Info {
@@ -117,8 +122,8 @@ func Run(config Config) error {
117122
118123 plugin .NewAPI (info , handlers ).Register (server )
119124
120- socket := "/tmp/plugin_ " + config . Name + ".sock"
121- listener , err := createPluginListener ( socket )
125+ socket := "/tmp/" + os . Args [ 0 ] + ".sock"
126+ listener , err := net . Listen ( "unix" , socket )
122127 if err != nil {
123128 return err
124129 }
@@ -131,31 +136,12 @@ func Run(config Config) error {
131136 group .Go (func () error {
132137 <- ctx .Done ()
133138 server .GracefulStop ()
134- return os . RemoveAll ( socket )
139+ return listener . Close ( )
135140 })
136141
137142 return group .Wait ()
138143}
139144
140- func createPluginListener (socket string ) (net.Listener , error ) {
141- var errno syscall.Errno
142-
143- listener , err := net .Listen ("unix" , socket )
144- switch {
145- case errors .As (err , & errno ) && errors .Is (errno , syscall .EADDRINUSE ):
146- // This socket should only ever be in use by us, so if it's already in use, we'll recreate it.
147- if err = os .Remove (socket ); err != nil {
148- return nil , err
149- }
150-
151- return net .Listen ("unix" , socket )
152- case err != nil :
153- return nil , err
154- default :
155- return listener , nil
156- }
157- }
158-
159145func getPluginVersion () string {
160146 if info , ok := debug .ReadBuildInfo (); ok {
161147 return info .Main .Version
@@ -190,8 +176,13 @@ var (
190176//
191177// If successful, it is up to the caller to eventually call Plugin.Close when they no longer require use of the plugin.
192178func Use (ctx context.Context , path string ) (* Plugin , error ) {
179+ socket := xid .New ().String ()
180+
193181 cmd := & exec.Cmd {
194182 Path : path ,
183+ Args : []string {
184+ socket ,
185+ },
195186 }
196187
197188 err := cmd .Start ()
@@ -204,7 +195,7 @@ func Use(ctx context.Context, path string) (*Plugin, error) {
204195 }
205196
206197 name := filepath .Base (path )
207- p .client , err = plugin .NewClient (name )
198+ p .client , err = plugin .NewClient (socket )
208199 if err != nil {
209200 return nil , fmt .Errorf ("failed to dial plugin %q: %w" , name , err )
210201 }
0 commit comments