From 705376272b99f5c86e06d8799d293efaef645fde 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 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) +}