X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/bfdecdcaf7dbeabfacc0efefb864e0024dbef9ab..c3d26486e5d4e4bd11964285a6fece4897120cf8:/services/crunchstat/crunchstat.go diff --git a/services/crunchstat/crunchstat.go b/services/crunchstat/crunchstat.go deleted file mode 100644 index 6383eae545..0000000000 --- a/services/crunchstat/crunchstat.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (C) The Arvados Authors. All rights reserved. -// -// SPDX-License-Identifier: AGPL-3.0 - -package main - -import ( - "bufio" - "flag" - "fmt" - "io" - "log" - "os" - "os/exec" - "os/signal" - "syscall" - "time" - - "git.arvados.org/arvados.git/lib/cmd" - "git.arvados.org/arvados.git/lib/crunchstat" -) - -const MaxLogLine = 1 << 14 // Child stderr lines >16KiB will be split - -var ( - signalOnDeadPPID int = 15 - ppidCheckInterval = time.Second - version = "dev" -) - -func main() { - reporter := crunchstat.Reporter{ - Logger: log.New(os.Stderr, "crunchstat: ", 0), - } - - flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError) - flags.StringVar(&reporter.CgroupRoot, "cgroup-root", "", "Root of cgroup tree") - flags.StringVar(&reporter.CgroupParent, "cgroup-parent", "", "Name of container parent under cgroup") - flags.StringVar(&reporter.CIDFile, "cgroup-cid", "", "Path to container id file") - flags.IntVar(&signalOnDeadPPID, "signal-on-dead-ppid", signalOnDeadPPID, "Signal to send child if crunchstat's parent process disappears (0 to disable)") - flags.DurationVar(&ppidCheckInterval, "ppid-check-interval", ppidCheckInterval, "Time between checks for parent process disappearance") - pollMsec := flags.Int64("poll", 1000, "Reporting interval, in milliseconds") - getVersion := flags.Bool("version", false, "Print version information and exit.") - - if ok, code := cmd.ParseFlags(flags, os.Args[0], os.Args[1:], "program [args ...]", os.Stderr); !ok { - os.Exit(code) - } else if *getVersion { - fmt.Printf("crunchstat %s\n", version) - return - } else if flags.NArg() == 0 { - fmt.Fprintf(os.Stderr, "missing required argument: program (try -help)\n") - os.Exit(2) - } - - reporter.Logger.Printf("crunchstat %s started", version) - - 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.PollPeriod = time.Duration(*pollMsec) * time.Millisecond - - reporter.Start() - err := runCommand(flags.Args(), reporter.Logger) - reporter.Stop() - - if err, ok := err.(*exec.ExitError); ok { - // The program has exited with an exit code != 0 - - // This works on both Unix and Windows. Although - // package syscall is generally platform dependent, - // WaitStatus is defined for both Unix and Windows and - // in both cases has an ExitStatus() method with the - // same signature. - if status, ok := err.Sys().(syscall.WaitStatus); ok { - os.Exit(status.ExitStatus()) - } else { - reporter.Logger.Fatalln("ExitError without WaitStatus:", err) - } - } else if err != nil { - reporter.Logger.Fatalln("error in cmd.Wait:", err) - } -} - -func runCommand(argv []string, logger *log.Logger) error { - cmd := exec.Command(argv[0], argv[1:]...) - - logger.Println("Running", argv) - - // Child process will use our stdin and stdout pipes - // (we close our copies below) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - - // Forward SIGINT and SIGTERM to child process - sigChan := make(chan os.Signal, 1) - go func(sig <-chan os.Signal) { - catch := <-sig - if cmd.Process != nil { - cmd.Process.Signal(catch) - } - logger.Println("notice: caught signal:", catch) - }(sigChan) - 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 - stderrPipe, err := cmd.StderrPipe() - if err != nil { - logger.Fatalln("error in StderrPipe:", err) - } - - // Run subprocess - if err := cmd.Start(); err != nil { - logger.Fatalln("error in cmd.Start:", err) - } - - // Close stdin/stdout in this (parent) process - os.Stdin.Close() - os.Stdout.Close() - - copyPipeToChildLog(stderrPipe, log.New(os.Stderr, "", 0)) - - 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 - for { - line, isPrefix, err := reader.ReadLine() - if err == io.EOF { - break - } else if err != nil { - logger.Fatal("error reading child stderr:", err) - } - var suffix string - if isPrefix { - suffix = "[...]" - } - logger.Print(prefix, string(line), suffix) - // Set up prefix for following line - if isPrefix { - prefix = "[...]" - } else { - prefix = "" - } - } - in.Close() -}