2018-10-19 23:34:09 +00:00
|
|
|
// Copyright 2018 Google LLC
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package cmd
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
|
|
|
|
"context"
|
|
|
|
"flag"
|
|
|
|
"github.com/google/subcommands"
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
"gvisor.googlesource.com/gvisor/runsc/boot"
|
2018-05-15 17:17:19 +00:00
|
|
|
"gvisor.googlesource.com/gvisor/runsc/container"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Kill implements subcommands.Command for the "kill" command.
|
2018-09-27 22:00:03 +00:00
|
|
|
type Kill struct {
|
|
|
|
all bool
|
2018-10-17 17:50:24 +00:00
|
|
|
pid int
|
2018-09-27 22:00:03 +00:00
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
|
|
|
|
// Name implements subcommands.Command.Name.
|
|
|
|
func (*Kill) Name() string {
|
|
|
|
return "kill"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Synopsis implements subcommands.Command.Synopsis.
|
|
|
|
func (*Kill) Synopsis() string {
|
2018-05-15 17:17:19 +00:00
|
|
|
return "sends a signal to the container"
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Usage implements subcommands.Command.Usage.
|
|
|
|
func (*Kill) Usage() string {
|
|
|
|
return `kill <container id> [signal]`
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetFlags implements subcommands.Command.SetFlags.
|
2018-09-27 22:00:03 +00:00
|
|
|
func (k *Kill) SetFlags(f *flag.FlagSet) {
|
|
|
|
f.BoolVar(&k.all, "all", false, "send the specified signal to all processes inside the container")
|
2018-10-17 17:50:24 +00:00
|
|
|
f.IntVar(&k.pid, "pid", 0, "send the specified signal to a specific process")
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute implements subcommands.Command.Execute.
|
2018-09-27 22:00:03 +00:00
|
|
|
func (k *Kill) Execute(_ context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
|
2018-04-27 17:37:02 +00:00
|
|
|
if f.NArg() == 0 || f.NArg() > 2 {
|
|
|
|
f.Usage()
|
|
|
|
return subcommands.ExitUsageError
|
|
|
|
}
|
|
|
|
|
|
|
|
id := f.Arg(0)
|
|
|
|
conf := args[0].(*boot.Config)
|
|
|
|
|
2018-10-17 17:50:24 +00:00
|
|
|
if k.pid != 0 && k.all {
|
|
|
|
Fatalf("it is invalid to specify both --all and --pid")
|
|
|
|
}
|
|
|
|
|
2018-05-15 17:17:19 +00:00
|
|
|
c, err := container.Load(conf.RootDir, id)
|
2018-04-27 17:37:02 +00:00
|
|
|
if err != nil {
|
2018-05-15 17:17:19 +00:00
|
|
|
Fatalf("error loading container: %v", err)
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The OCI command-line spec says that the signal should be specified
|
|
|
|
// via a flag, but runc (and things that call runc) pass it as an
|
|
|
|
// argument.
|
2018-06-15 18:05:10 +00:00
|
|
|
signal := f.Arg(1)
|
2018-04-27 17:37:02 +00:00
|
|
|
if signal == "" {
|
|
|
|
signal = "TERM"
|
|
|
|
}
|
|
|
|
|
|
|
|
sig, err := parseSignal(signal)
|
|
|
|
if err != nil {
|
|
|
|
Fatalf("%v", err)
|
|
|
|
}
|
2018-10-17 17:50:24 +00:00
|
|
|
|
|
|
|
if k.pid != 0 {
|
|
|
|
if err := c.SignalProcess(sig, int32(k.pid)); err != nil {
|
|
|
|
Fatalf("failed to signal pid %d: %v", k.pid, err)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := c.SignalContainer(sig, k.all); err != nil {
|
|
|
|
Fatalf("%v", err)
|
|
|
|
}
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
return subcommands.ExitSuccess
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseSignal(s string) (syscall.Signal, error) {
|
|
|
|
n, err := strconv.Atoi(s)
|
|
|
|
if err == nil {
|
|
|
|
sig := syscall.Signal(n)
|
|
|
|
for _, msig := range signalMap {
|
|
|
|
if sig == msig {
|
|
|
|
return sig, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1, fmt.Errorf("unknown signal %q", s)
|
|
|
|
}
|
|
|
|
if sig, ok := signalMap[strings.TrimPrefix(strings.ToUpper(s), "SIG")]; ok {
|
|
|
|
return sig, nil
|
|
|
|
}
|
|
|
|
return -1, fmt.Errorf("unknown signal %q", s)
|
|
|
|
}
|
|
|
|
|
|
|
|
var signalMap = map[string]syscall.Signal{
|
|
|
|
"ABRT": unix.SIGABRT,
|
|
|
|
"ALRM": unix.SIGALRM,
|
|
|
|
"BUS": unix.SIGBUS,
|
|
|
|
"CHLD": unix.SIGCHLD,
|
|
|
|
"CLD": unix.SIGCLD,
|
|
|
|
"CONT": unix.SIGCONT,
|
|
|
|
"FPE": unix.SIGFPE,
|
|
|
|
"HUP": unix.SIGHUP,
|
|
|
|
"ILL": unix.SIGILL,
|
|
|
|
"INT": unix.SIGINT,
|
|
|
|
"IO": unix.SIGIO,
|
|
|
|
"IOT": unix.SIGIOT,
|
|
|
|
"KILL": unix.SIGKILL,
|
|
|
|
"PIPE": unix.SIGPIPE,
|
|
|
|
"POLL": unix.SIGPOLL,
|
|
|
|
"PROF": unix.SIGPROF,
|
|
|
|
"PWR": unix.SIGPWR,
|
|
|
|
"QUIT": unix.SIGQUIT,
|
|
|
|
"SEGV": unix.SIGSEGV,
|
|
|
|
"STKFLT": unix.SIGSTKFLT,
|
|
|
|
"STOP": unix.SIGSTOP,
|
|
|
|
"SYS": unix.SIGSYS,
|
|
|
|
"TERM": unix.SIGTERM,
|
|
|
|
"TRAP": unix.SIGTRAP,
|
|
|
|
"TSTP": unix.SIGTSTP,
|
|
|
|
"TTIN": unix.SIGTTIN,
|
|
|
|
"TTOU": unix.SIGTTOU,
|
|
|
|
"URG": unix.SIGURG,
|
|
|
|
"USR1": unix.SIGUSR1,
|
|
|
|
"USR2": unix.SIGUSR2,
|
|
|
|
"VTALRM": unix.SIGVTALRM,
|
|
|
|
"WINCH": unix.SIGWINCH,
|
|
|
|
"XCPU": unix.SIGXCPU,
|
|
|
|
"XFSZ": unix.SIGXFSZ,
|
|
|
|
}
|