From 89b4f6e9f6e604b3b17a2eb398271ad56a23fc86 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Thu, 15 Apr 2021 19:16:43 +0800 Subject: [PATCH] ansibleserver: wait zombie subprocesses --- pkg/ansibleserver/service/service.go | 3 + pkg/ansibleserver/service/zombie.go | 118 +++++++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 pkg/ansibleserver/service/zombie.go diff --git a/pkg/ansibleserver/service/service.go b/pkg/ansibleserver/service/service.go index 4c891dcafe..67c89c615f 100644 --- a/pkg/ansibleserver/service/service.go +++ b/pkg/ansibleserver/service/service.go @@ -15,6 +15,7 @@ package service import ( + "context" "os" _ "github.com/go-sql-driver/mysql" @@ -48,5 +49,7 @@ func StartService() { db.EnsureAppInitSyncDB(app, dbOpts, models.InitDB) defer cloudcommon.CloseDB() + go ReapZomebieLoop(context.TODO()) + common_app.ServeForever(app, baseOpts) } diff --git a/pkg/ansibleserver/service/zombie.go b/pkg/ansibleserver/service/zombie.go new file mode 100644 index 0000000000..224e054df6 --- /dev/null +++ b/pkg/ansibleserver/service/zombie.go @@ -0,0 +1,118 @@ +// Copyright 2019 Yunion +// +// 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 service + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strconv" + "strings" + "syscall" + "time" + + "yunion.io/x/log" +) + +func ReapZomebieLoop(ctx context.Context) { + myPid := os.Getpid() + if myPid != 1 { + log.Infof("My pid is not 1 and no need to wait zombies") + return + } + myPidStr := fmt.Sprintf("%d", myPid) + + tick := time.NewTicker(31 * time.Second) + for { + dirs, err := ioutil.ReadDir("/proc") + if err != nil { + log.Errorf("read /proc dir: %v", err) + } + for _, dir := range dirs { + // /proc// + if !dir.IsDir() { + continue + } + name := dir.Name() + allDigits := true + for _, c := range name { + if c < '0' || c > '9' { + allDigits = false + break + } + } + if !allDigits { + continue + } + + // read /proc//stat + statPath := filepath.Join("/proc", name, "stat") + data, err := ioutil.ReadFile(statPath) + if err != nil { + log.Errorf("read %s: %v", statPath, err) + continue + } + dataStr := string(data) + items := strings.Split(dataStr, " ") + const ( + idxPid = iota + idxName = iota + idxState = iota + idxPpid = iota + idxMinLen = iota + ) + if len(items) < idxMinLen { + log.Errorf("%s contains less than %d items: %s", statPath, idxMinLen, dataStr) + } + + // my zombie subprocesses + state := items[idxState] + if state != "Z" { + continue + } + ppidStr := items[idxPpid] + if ppidStr != myPidStr { + continue + } + + // wait it + pname := items[idxName] + pidStr := items[idxPid] + pid, err := strconv.Atoi(pidStr) + if err != nil { + log.Errorf("%s: %s has invalid pid number %q: %v", pname, statPath, pidStr, err) + continue + } + var ( + status syscall.WaitStatus + rusage syscall.Rusage + ) + pid1, err := syscall.Wait4(pid, &status, 0, &rusage) + if err != nil { + log.Errorf("%s: %s: wait error: %v", pname, statPath, err) + continue + } + if pid1 == pid { + log.Infof("%s: pid %d: wait done", pname, pid) + } + } + select { + case <-ctx.Done(): + case <-tick.C: + } + } +}