package main import ( "strconv" "strings" "github.com/Microsoft/hcsshim/internal/appargs" "github.com/Microsoft/hcsshim/internal/guestrequest" "github.com/Microsoft/hcsshim/internal/hcs" "github.com/Microsoft/hcsshim/internal/schema1" "github.com/Microsoft/hcsshim/osversion" "github.com/pkg/errors" "github.com/urfave/cli" ) var killCommand = cli.Command{ Name: "kill", Usage: "kill sends the specified signal (default: SIGTERM) to the container's init process", ArgsUsage: ` [signal] Where "" is the name for the instance of the container and "[signal]" is the signal to be sent to the init process. EXAMPLE: For example, if the container id is "ubuntu01" the following will send a "KILL" signal to the init process of the "ubuntu01" container: # runhcs kill ubuntu01 KILL`, Flags: []cli.Flag{}, Before: appargs.Validate(argID, appargs.Optional(appargs.String)), Action: func(context *cli.Context) error { id := context.Args().First() c, err := getContainer(id, true) if err != nil { return err } defer c.Close() status, err := c.Status() if err != nil { return err } if status != containerRunning { return errContainerStopped } signalsSupported := false // The Signal feature was added in RS5 if osversion.Get().Build >= osversion.RS5 { if c.IsHost || c.HostID != "" { var hostID string if c.IsHost { // This is the LCOW, Pod Sandbox, or Windows Xenon V2 for RS5+ hostID = vmID(c.ID) } else { // This is the Nth container in a Pod hostID = c.HostID } uvm, err := hcs.OpenComputeSystem(hostID) if err != nil { return err } defer uvm.Close() if props, err := uvm.Properties(schema1.PropertyTypeGuestConnection); err == nil && props.GuestConnectionInfo.GuestDefinedCapabilities.SignalProcessSupported { signalsSupported = true } } else if c.Spec.Linux == nil && c.Spec.Windows.HyperV == nil { // RS5+ Windows Argon signalsSupported = true } } signal := 0 if signalsSupported { signal, err = validateSigstr(context.Args().Get(1), signalsSupported, c.Spec.Linux != nil) if err != nil { return err } } var pid int if err := stateKey.Get(id, keyInitPid, &pid); err != nil { return err } p, err := c.hc.OpenProcess(pid) if err != nil { return err } defer p.Close() if signalsSupported && (c.Spec.Linux != nil || !c.Spec.Process.Terminal) { opts := guestrequest.SignalProcessOptions{ Signal: signal, } return p.Signal(opts) } // Legacy signal issue a kill return p.Kill() }, } func validateSigstr(sigstr string, signalsSupported bool, isLcow bool) (int, error) { errInvalidSignal := errors.Errorf("invalid signal '%s'", sigstr) // All flavors including legacy default to SIGTERM on LCOW CtrlC on Windows if sigstr == "" { if isLcow { return 0xf, nil } return 0, nil } sigstr = strings.ToUpper(sigstr) if !signalsSupported { // If signals arent supported we just validate that its a known signal. // We already return 0 since we only supported a platform Kill() at that // time. if isLcow { switch sigstr { case "15": fallthrough case "TERM": fallthrough case "SIGTERM": return 0, nil default: return 0, errInvalidSignal } } switch sigstr { // Docker sends a UNIX term in the supported Windows Signal map. case "15": fallthrough case "TERM": fallthrough case "0": fallthrough case "CTRLC": return 0, nil case "9": fallthrough case "KILL": return 0, nil default: return 0, errInvalidSignal } } var sigmap map[string]int if isLcow { sigmap = signalMapLcow } else { sigmap = signalMapWindows } signal, err := strconv.Atoi(sigstr) if err != nil { // Signal might still match the string value for k, v := range sigmap { if k == sigstr { return v, nil } } return 0, errInvalidSignal } // Match signal by value for _, v := range sigmap { if signal == v { return signal, nil } } return 0, errInvalidSignal }