github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/contrib/cmd/pidfd-kill/pidfd-kill.go (about) 1 package main 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "os" 8 "os/signal" 9 10 "github.com/urfave/cli" 11 "golang.org/x/sys/unix" 12 13 "github.com/opencontainers/runc/libcontainer/utils" 14 ) 15 16 const ( 17 usage = `Open Container Initiative contrib/cmd/pidfd-kill 18 19 pidfd-kill is an implementation of a consumer of runC's --pidfd-socket API. 20 After received SIGTERM, pidfd-kill sends the given signal to init process by 21 pidfd received from --pidfd-socket. 22 23 To use pidfd-kill, just specify a socket path at which you want to receive 24 pidfd: 25 26 $ pidfd-kill [--signal KILL] socket.sock 27 ` 28 ) 29 30 func main() { 31 app := cli.NewApp() 32 app.Name = "pidfd-kill" 33 app.Usage = usage 34 35 app.Flags = []cli.Flag{ 36 cli.StringFlag{ 37 Name: "signal", 38 Value: "SIGKILL", 39 Usage: "Signal to send to the init process", 40 }, 41 cli.StringFlag{ 42 Name: "pid-file", 43 Value: "", 44 Usage: "Path to write the pidfd-kill process ID to", 45 }, 46 } 47 48 app.Action = func(ctx *cli.Context) error { 49 args := ctx.Args() 50 if len(args) != 1 { 51 return errors.New("required a single socket path") 52 } 53 54 socketFile := ctx.Args()[0] 55 56 pidFile := ctx.String("pid-file") 57 if pidFile != "" { 58 pid := fmt.Sprintf("%d\n", os.Getpid()) 59 if err := os.WriteFile(pidFile, []byte(pid), 0o644); err != nil { 60 return err 61 } 62 defer os.Remove(pidFile) 63 } 64 65 sigStr := ctx.String("signal") 66 if sigStr == "" { 67 sigStr = "SIGKILL" 68 } 69 sig := unix.SignalNum(sigStr) 70 71 pidfdFile, err := recvPidfd(socketFile) 72 if err != nil { 73 return err 74 } 75 defer pidfdFile.Close() 76 77 signalCh := make(chan os.Signal, 16) 78 signal.Notify(signalCh, unix.SIGTERM) 79 <-signalCh 80 81 return unix.PidfdSendSignal(int(pidfdFile.Fd()), sig, nil, 0) 82 } 83 if err := app.Run(os.Args); err != nil { 84 fmt.Fprintln(os.Stderr, "fatal error:", err) 85 os.Exit(1) 86 } 87 } 88 89 func recvPidfd(socketFile string) (*os.File, error) { 90 ln, err := net.Listen("unix", socketFile) 91 if err != nil { 92 return nil, err 93 } 94 defer ln.Close() 95 96 conn, err := ln.Accept() 97 if err != nil { 98 return nil, err 99 } 100 defer conn.Close() 101 102 unixconn, ok := conn.(*net.UnixConn) 103 if !ok { 104 return nil, errors.New("failed to cast to unixconn") 105 } 106 107 socket, err := unixconn.File() 108 if err != nil { 109 return nil, err 110 } 111 defer socket.Close() 112 113 return utils.RecvFile(socket) 114 }