diff --git a/grpc.go b/grpc.go index 71581bec09..b460f19fae 100644 --- a/grpc.go +++ b/grpc.go @@ -7,6 +7,7 @@ package main import ( + "bufio" "bytes" "encoding/json" "fmt" @@ -833,6 +834,16 @@ func (a *agentGRPC) SignalProcess(ctx context.Context, req *pb.SignalProcessRequ if req.ExecId == "" || status == libcontainer.Paused { return emptyResp, ctr.container.Signal(signal, true) } else if ctr.initProcess.id == req.ExecId { + pid, err := ctr.initProcess.process.Pid() + if err != nil { + return emptyResp, err + } + // For container initProcess, if it hasn't installed handler for "SIGTERM" signal, + // it will ignore the "SIGTERM" signal sent to it, thus send it "SIGKILL" signal + // instead of "SIGTERM" to terminate it. + if signal == syscall.SIGTERM && !isSignalHandled(pid, syscall.SIGTERM) { + signal = syscall.SIGKILL + } return emptyResp, ctr.container.Signal(signal, false) } @@ -848,6 +859,39 @@ func (a *agentGRPC) SignalProcess(ctx context.Context, req *pb.SignalProcessRequ return emptyResp, nil } +// Check is the container process installed the +// handler for specific signal. +func isSignalHandled(pid int, signum syscall.Signal) bool { + var sigMask uint64 = 1 << (uint(signum) - 1) + procFile := fmt.Sprintf("/proc/%d/status", pid) + file, err := os.Open(procFile) + if err != nil { + agentLog.WithField("procFile", procFile).Warn("Open proc file failed") + return false + } + defer file.Close() + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "SigCgt:") { + maskSlice := strings.Split(line, ":") + if len(maskSlice) != 2 { + agentLog.WithField("procFile", procFile).Warn("Parse the SigCgt field failed") + return false + } + sigCgtStr := strings.TrimSpace(maskSlice[1]) + sigCgtMask, err := strconv.ParseUint(sigCgtStr, 16, 64) + if err != nil { + agentLog.WithField("sigCgt", sigCgtStr).Warn("parse the SigCgt to hex failed") + return false + } + return (sigCgtMask & sigMask) == sigMask + } + } + return false +} + func (a *agentGRPC) WaitProcess(ctx context.Context, req *pb.WaitProcessRequest) (*pb.WaitProcessResponse, error) { proc, ctr, err := a.sandbox.getProcess(req.ContainerId, req.ExecId) if err != nil { diff --git a/grpc_test.go b/grpc_test.go index b82d02dd07..6ec0fc5aa6 100644 --- a/grpc_test.go +++ b/grpc_test.go @@ -15,6 +15,7 @@ import ( "reflect" "sort" "strconv" + "syscall" "testing" "time" @@ -836,3 +837,23 @@ func TestCopyFile(t *testing.T) { // check file's content assert.Equal(content, append(part1, part2...)) } + +func TestIsSignalHandled(t *testing.T) { + assert := assert.New(t) + pid := 1 + + // process will not handle SIGKILL signal + signum := syscall.SIGKILL + handled := isSignalHandled(pid, signum) + assert.False(handled) + + // init process will not handle SIGTERM signal + signum = syscall.SIGTERM + handled = isSignalHandled(pid, signum) + assert.False(handled) + + // init process will handle the SIGQUIT signal + signum = syscall.SIGQUIT + handled = isSignalHandled(pid, signum) + assert.True(handled) +}