@@ -2,18 +2,22 @@ package main
22
33import (
44 "fmt"
5+ "net"
56 "os"
67 "os/exec"
8+ "os/signal"
79 "strings"
810 "syscall"
911
1012 "github.com/docker/cli/cli"
1113 pluginmanager "github.com/docker/cli/cli-plugins/manager"
14+ "github.com/docker/cli/cli-plugins/plugin"
1215 "github.com/docker/cli/cli/command"
1316 "github.com/docker/cli/cli/command/commands"
1417 cliflags "github.com/docker/cli/cli/flags"
1518 "github.com/docker/cli/cli/version"
16- "github.com/docker/cli/cmd/docker/internal/appcontext"
19+ platformsignals "github.com/docker/cli/cmd/docker/internal/signals"
20+ "github.com/docker/distribution/uuid"
1721 "github.com/docker/docker/api/types/versions"
1822 "github.com/pkg/errors"
1923 "github.com/sirupsen/logrus"
@@ -187,16 +191,59 @@ func setValidateArgs(dockerCli command.Cli, cmd *cobra.Command) {
187191 })
188192}
189193
194+ func setupPluginSocket () (* net.UnixListener , error ) {
195+ return net .ListenUnix ("unix" , & net.UnixAddr {
196+ Name : "@docker_cli_" + uuid .Generate ().String (),
197+ Net : "unix" ,
198+ })
199+ }
200+
190201func tryPluginRun (dockerCli command.Cli , cmd * cobra.Command , subcommand string , envs []string ) error {
191202 plugincmd , err := pluginmanager .PluginRunCommand (dockerCli , subcommand , cmd )
192203 if err != nil {
193204 return err
194205 }
195206 plugincmd .Env = append (envs , plugincmd .Env ... )
196207
208+ var conn * net.UnixConn
209+ listener , err := setupPluginSocket ()
210+ if err == nil {
211+ defer listener .Close ()
212+ plugincmd .Env = append (plugincmd .Env , plugin .CLIPluginSocketEnvKey + "=" + listener .Addr ().String ())
213+
214+ go func () {
215+ for {
216+ // ignore error here, if we failed to accept a connection,
217+ // conn is nil and we fallback to previous behavior
218+ conn , _ = listener .AcceptUnix ()
219+ }
220+ }()
221+ }
222+
223+ const exitLimit = 3
224+
225+ signals := make (chan os.Signal , exitLimit )
226+ signal .Notify (signals , platformsignals .TerminationSignals ... )
227+ // signal handling goroutine: listen on signals channel, and if conn is
228+ // non-nil, attempt to close it to let the plugin know to exit. Regardless
229+ // of whether we successfully signal the plugin or not, after 3 SIGINTs,
230+ // we send a SIGKILL to the plugin process and exit
197231 go func () {
198- // override SIGTERM handler so we let the plugin shut down first
199- <- appcontext .Context ().Done ()
232+ retries := 0
233+ for range signals {
234+ if conn != nil {
235+ if err := conn .Close (); err != nil {
236+ _ , _ = fmt .Fprintf (dockerCli .Err (), "failed to signal plugin to close: %v\n " , err )
237+ }
238+ conn = nil
239+ }
240+ retries ++
241+ if retries >= exitLimit {
242+ _ , _ = fmt .Fprintf (dockerCli .Err (), "got %d SIGTERM/SIGINTs, forcefully exiting\n " , retries )
243+ _ = plugincmd .Process .Kill ()
244+ os .Exit (1 )
245+ }
246+ }
200247 }()
201248
202249 if err := plugincmd .Run (); err != nil {
0 commit comments