179 lines
4.0 KiB
Go
179 lines
4.0 KiB
Go
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: `<container-id> [signal]
|
|
|
|
Where "<container-id>" 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
|
|
}
|