14 "git.curoverse.com/arvados.git/lib/crunchstat"
17 const MaxLogLine = 1 << 14 // Child stderr lines >16KiB will be split
20 reporter := crunchstat.Reporter{
21 Logger: log.New(os.Stderr, "crunchstat: ", 0),
24 flag.StringVar(&reporter.CgroupRoot, "cgroup-root", "", "Root of cgroup tree")
25 flag.StringVar(&reporter.CgroupParent, "cgroup-parent", "", "Name of container parent under cgroup")
26 flag.StringVar(&reporter.CIDFile, "cgroup-cid", "", "Path to container id file")
27 pollMsec := flag.Int64("poll", 1000, "Reporting interval, in milliseconds")
31 if reporter.CgroupRoot == "" {
32 reporter.Logger.Fatal("error: must provide -cgroup-root")
34 reporter.PollPeriod = time.Duration(*pollMsec) * time.Millisecond
37 err := runCommand(flag.Args(), reporter.Logger)
40 if err, ok := err.(*exec.ExitError); ok {
41 // The program has exited with an exit code != 0
43 // This works on both Unix and Windows. Although
44 // package syscall is generally platform dependent,
45 // WaitStatus is defined for both Unix and Windows and
46 // in both cases has an ExitStatus() method with the
48 if status, ok := err.Sys().(syscall.WaitStatus); ok {
49 os.Exit(status.ExitStatus())
51 reporter.Logger.Fatalln("ExitError without WaitStatus:", err)
53 } else if err != nil {
54 reporter.Logger.Fatalln("error in cmd.Wait:", err)
58 func runCommand(argv []string, logger *log.Logger) error {
59 cmd := exec.Command(argv[0], argv[1:]...)
61 logger.Println("Running", argv)
63 // Child process will use our stdin and stdout pipes
64 // (we close our copies below)
66 cmd.Stdout = os.Stdout
68 // Forward SIGINT and SIGTERM to child process
69 sigChan := make(chan os.Signal, 1)
70 go func(sig <-chan os.Signal) {
72 if cmd.Process != nil {
73 cmd.Process.Signal(catch)
75 logger.Println("notice: caught signal:", catch)
77 signal.Notify(sigChan, syscall.SIGTERM)
78 signal.Notify(sigChan, syscall.SIGINT)
80 // Funnel stderr through our channel
81 stderr_pipe, err := cmd.StderrPipe()
83 logger.Fatalln("error in StderrPipe:", err)
87 if err := cmd.Start(); err != nil {
88 logger.Fatalln("error in cmd.Start:", err)
91 // Close stdin/stdout in this (parent) process
95 copyPipeToChildLog(stderr_pipe, log.New(os.Stderr, "", 0))
100 func copyPipeToChildLog(in io.ReadCloser, logger *log.Logger) {
101 reader := bufio.NewReaderSize(in, MaxLogLine)
104 line, isPrefix, err := reader.ReadLine()
107 } else if err != nil {
108 logger.Fatal("error reading child stderr:", err)
114 logger.Print(prefix, string(line), suffix)
115 // Set up prefix for following line