From 7fbd860aca8c5d6234f3efbf69da88602c99b6ce Mon Sep 17 00:00:00 2001 From: lifupan Date: Mon, 8 Apr 2019 01:50:11 -0400 Subject: [PATCH] agent: send SIGKILL instead of SIGTERM to container init process If container initProcess doesn't install any handler for SIGTERM, it will ignore this signal, thus send it SIGKILL instead of SIGTERM to terminate it. Fixes:#525 Signed-off-by: lifupan --- grpc.go | 44 ++++++++++++++++++++++++++++++++++++++++++++ grpc_test.go | 21 +++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/grpc.go b/grpc.go index eae600d15e..f6af4a64e9 100644 --- a/grpc.go +++ b/grpc.go @@ -7,6 +7,7 @@ package main import ( + "bufio" "bytes" "encoding/json" "fmt" @@ -919,6 +920,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) } @@ -934,6 +945,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 b660d85eec..fbefd9f0fb 100644 --- a/grpc_test.go +++ b/grpc_test.go @@ -15,6 +15,7 @@ import ( "reflect" "sort" "strconv" + "syscall" "testing" "time" @@ -961,3 +962,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) +}