// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package subproc import ( "bufio" "context" "fmt" "io" "os" "strings" "time" ) const ( // ExitSuccess indicates the subprocess completed successfully. ExitSuccess = 0 // ExitFailure indicates the subprocess terminated unsuccessfully. ExitFailure = 1 // ExitTimeout indicates the subprocess timed out before completion. ExitTimeout = 2 // ExitNotRunnable indicates a command cannot be run. ExitNotRunnable = 127 // bash-ism ) // MainFunc is the function that runs for this sub-process. // // The return value is a process exit code. type MainFunc func() int // Do f if nomad was launched as, "nomad [name]". This process will exit without // running any other part of Nomad. func Do(name string, f MainFunc) { if len(os.Args) > 1 && os.Args[1] == name { os.Exit(f()) } } // Print the given message to standard error. func Print(format string, args ...any) { _, _ = fmt.Fprintf(os.Stderr, format+"\n", args...) } // Log the given output to the logger. // // r should be a buffer containing output (typically combined stdin + stdout) // f should be an HCLogger Print method (e.g. log.Debug) func Log(r io.Reader, f func(msg string, args ...any)) string { scanner := bufio.NewScanner(r) lines := "" for scanner.Scan() { line := scanner.Text() lines += line + "\n" f("sub-process", "OUTPUT", line) } return strings.TrimSpace(lines) } // Context creates a context setup with the given timeout. func Context(timeout time.Duration) (context.Context, context.CancelFunc) { ctx := context.Background() return context.WithTimeout(ctx, timeout) } // SetExpiration is used to ensure the process terminates, once ctx // is complete. A short grace period is added to allow any cleanup // to happen first. func SetExpiration(ctx context.Context) { const graceful = 5 * time.Second go func() { <-ctx.Done() time.Sleep(graceful) os.Exit(ExitTimeout) }() }