10666: Added version number to go sdk and go tools & services
[arvados.git] / services / crunchstat / crunchstat.go
index 8c05069dbf91acc92f6d0fccb1827203a61d14b1..454a5fc3a4363d24b6f858183da6d404750ea6a1 100644 (file)
@@ -1,8 +1,13 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
 package main
 
 import (
        "bufio"
        "flag"
+       "fmt"
        "io"
        "log"
        "os"
@@ -12,10 +17,16 @@ import (
        "time"
 
        "git.curoverse.com/arvados.git/lib/crunchstat"
+       arvadosVersion "git.curoverse.com/arvados.git/sdk/go/version"
 )
 
 const MaxLogLine = 1 << 14 // Child stderr lines >16KiB will be split
 
+var (
+       signalOnDeadPPID  int = 15
+       ppidCheckInterval     = time.Second
+)
+
 func main() {
        reporter := crunchstat.Reporter{
                Logger: log.New(os.Stderr, "crunchstat: ", 0),
@@ -24,14 +35,27 @@ func main() {
        flag.StringVar(&reporter.CgroupRoot, "cgroup-root", "", "Root of cgroup tree")
        flag.StringVar(&reporter.CgroupParent, "cgroup-parent", "", "Name of container parent under cgroup")
        flag.StringVar(&reporter.CIDFile, "cgroup-cid", "", "Path to container id file")
+       flag.IntVar(&signalOnDeadPPID, "signal-on-dead-ppid", signalOnDeadPPID, "Signal to send child if crunchstat's parent process disappears (0 to disable)")
+       flag.DurationVar(&ppidCheckInterval, "ppid-check-interval", ppidCheckInterval, "Time between checks for parent process disappearance")
        pollMsec := flag.Int64("poll", 1000, "Reporting interval, in milliseconds")
+       getVersion := flag.Bool("version", false, "Print version information and exit.")
 
        flag.Parse()
 
+       // Print version information if requested
+       if *getVersion {
+               fmt.Printf("Version: %s\n", arvadosVersion.GetVersion())
+               os.Exit(0)
+       }
+
+       reporter.Logger.Printf("crunchstat %q started", arvadosVersion.GetVersion())
+
        if reporter.CgroupRoot == "" {
                reporter.Logger.Fatal("error: must provide -cgroup-root")
+       } else if signalOnDeadPPID < 0 {
+               reporter.Logger.Fatalf("-signal-on-dead-ppid=%d is invalid (use a positive signal number, or 0 to disable)", signalOnDeadPPID)
        }
-       reporter.Poll = time.Duration(*pollMsec) * time.Millisecond
+       reporter.PollPeriod = time.Duration(*pollMsec) * time.Millisecond
 
        reporter.Start()
        err := runCommand(flag.Args(), reporter.Logger)
@@ -77,6 +101,11 @@ func runCommand(argv []string, logger *log.Logger) error {
        signal.Notify(sigChan, syscall.SIGTERM)
        signal.Notify(sigChan, syscall.SIGINT)
 
+       // Kill our child proc if our parent process disappears
+       if signalOnDeadPPID != 0 {
+               go sendSignalOnDeadPPID(ppidCheckInterval, signalOnDeadPPID, os.Getppid(), cmd, logger)
+       }
+
        // Funnel stderr through our channel
        stderr_pipe, err := cmd.StderrPipe()
        if err != nil {
@@ -97,6 +126,28 @@ func runCommand(argv []string, logger *log.Logger) error {
        return cmd.Wait()
 }
 
+func sendSignalOnDeadPPID(intvl time.Duration, signum, ppidOrig int, cmd *exec.Cmd, logger *log.Logger) {
+       ticker := time.NewTicker(intvl)
+       for range ticker.C {
+               ppid := os.Getppid()
+               if ppid == ppidOrig {
+                       continue
+               }
+               if cmd.Process == nil {
+                       // Child process isn't running yet
+                       continue
+               }
+               logger.Printf("notice: crunchstat ppid changed from %d to %d -- killing child pid %d with signal %d", ppidOrig, ppid, cmd.Process.Pid, signum)
+               err := cmd.Process.Signal(syscall.Signal(signum))
+               if err != nil {
+                       logger.Printf("error: sending signal: %s", err)
+                       continue
+               }
+               ticker.Stop()
+               break
+       }
+}
+
 func copyPipeToChildLog(in io.ReadCloser, logger *log.Logger) {
        reader := bufio.NewReaderSize(in, MaxLogLine)
        var prefix string