executil

package
v0.0.0-...-352abbd Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 17, 2026 License: Apache-2.0 Imports: 19 Imported by: 3

README

Package cloudeng.io/os/executil

import cloudeng.io/os/executil

Package executil provides utilities for working with os/exec.

Variables

ErrAlreadyRunning
ErrAlreadyRunning = errors.New("another instance of this process is already running")

ErrAlreadyRunning is returned when another orchestrator already holds the run lock.

Functions

Func AcquireRunLock
func AcquireRunLock(path string) (unlock func(), err error)

AcquireRunLock takes a non-blocking, exclusive file lock so that only one orchestrator runs at a time. The returned unlock function must be called when the run finishes; the lock is also released automatically when the process exits — including on crash or SIGKILL — so it never goes stale. If another instance already holds the lock, ErrAlreadyRunning is returned, annotated with the holder's PID and the lock path.

TryOpenFile is used rather than Mutex.TryLock so that the PID can be recorded through the locked handle itself. The Mutex's additional in-process guard is not needed: only RunCommand.Run acquires this lock, once per process.

Func AppendMissingPathComponents
func AppendMissingPathComponents(pathList string, paths ...string) string

AppendMissingPathComponents treats pathList as a path list, ie. a list of paths separated by os.PathListSeparator. It returns a copy of pathList with any of the specified paths that do not already exist in pathList appended to the end of the returned string.

Func ExecName
func ExecName(path string) string

ExecName returns path in a form suitable for use as an executable. For unix systems the path is unchanged. For windows a '.exe' suffix is added if not already present.

Func Getenv
func Getenv(env []string, key string) (string, bool)

Getenv retrieves the value of an environment variable from the provided slice.

Func GoBuild
func GoBuild(ctx context.Context, binary string, args ...string) (string, error)

GoBuild builds the specified Go binary using the provided context and arguments. It returns the path to the built binary or an error if the build fails.

Func GoBuildArgs
func GoBuildArgs(binary string, args ...string) (string, []string, error)

GoBuildArgs returns the Go executable path, the arguments for a Go build command that outputs the specified binary, or an error if the Go executable could not be found. GoBuildArgs("mybinary", "a", "b") might return "/usr/local/go/bin/go", ["build", "-o", "mybinary", "a", "b"], nil.

Func GoInstallArgs
func GoInstallArgs(args ...string) (string, []string, error)

GoInstallArgs returns the Go executable path and the arguments for a Go install command, or an error if the Go executable could not be found. GoInstall("a", "b") might return "/usr/local/go/bin/go", ["install", "a", "b"], nil.

Func IsStopped
func IsStopped(pid int) bool

IsStopped returns true if the process with the specified pid has stopped or does not exist. Wait must have been called on the process otherwise this function will return true on some systems since the process may still exist as a defunct process.

Func NewLabelingWriter
func NewLabelingWriter(w io.Writer, prefix []byte, separator rune) io.Writer
Func NewLineFilter
func NewLineFilter(forward io.Writer, ch chan<- []byte, res ...*regexp.Regexp) io.WriteCloser

NewLineFilter returns an io.WriteCloser that scans the contents of the supplied io.Writer and sends lines that match the regexp to the supplied channel. It can be used to filter the output of a command started by the exec package for example for specific output. If no regexps are supplied, all lines are sent. Close must be called on the returned io.WriteCloser to ensure that all resources are reclaimed.

Func ReplaceEnvVar
func ReplaceEnvVar(env []string, key, value string) []string

ReplaceEnvVar replaces the value of an environment variable in the provided slice. If the variable does not exist, it is added to the slice.

Func SignalAndWait
func SignalAndWait(ctx context.Context, perSignalOrWait time.Duration, cmd *exec.Cmd, sigs ...os.Signal) error

SignalAndWait provides a convenience function to signal a process to terminate by sending it one or more signals and waiting for it to terminate but with a timeout on calling Wait and on waiting for the process to stop after each signal. The perSignalOrWait duration is used as the timeout for both calling Wait and for waiting for the process to stop after each signal, hence the total time spent waiting may be up to len(sigs)+1 times perSignalOrWait. If the process stops after any signal, SignalAndWait returns immediately.

Func UserConfigDirPath
func UserConfigDirPath(path string) (string, error)

UserConfigDirPath appends path to the per-user config directory as returned by os.UserConfigDir.

Func WaitFor
func WaitFor(ctx context.Context, interval time.Duration, check func(ctx context.Context) (done bool, err error)) error

WaitFor repeatedly calls the provided check function until it returns done=true, or the context is done. It waits for the specified interval between calls. If check returns an error, it is ignored unless done=true, in which case it is returned immediately.

Func WaitForBackoff
func WaitForBackoff(ctx context.Context, backoff ratecontrol.Backoff, check func(ctx context.Context) (done bool, err error)) error

WaitForBackoff repeatedly calls the provided check function until it returns done=true, or the context is done. It waits for the next backoff delay between calls. If check returns an error, it is ignored unless done=true, in which case it is returned immediately.

Func WaitForStopped
func WaitForStopped(ctx context.Context, pid int, waitFor time.Duration) error

WaitForStopped waits for the process with the specified pid to stop within the specified duration. It assumes that Wait has been called.

Types

Type AsyncWait
type AsyncWait struct {
	// contains filtered or unexported fields
}

AsyncWait simplifies implementing asynchronous waiting for an exec.Cmd.

Functions
func NewAsyncWait(cmd *exec.Cmd) *AsyncWait

NewAsyncWait creates a new AsyncWait for the given exec.Cmd. It immediately starts a goroutine to wait for the cmd to complete.

Methods
func (aw *AsyncWait) Cmd() *exec.Cmd

Cmd returns the exec.Cmd being waited on.

func (aw *AsyncWait) Wait() error

Wait waits for the cmd to complete and returns the error from cmd.Wait(). If the cmd has already completed, it returns immediately with the error from cmd.Wait().

func (aw *AsyncWait) WaitDone() (bool, error)

WaitDone reports whether the cmd has already completed. If so, it returns true and the error from cmd.Wait(), otherwise it returns false and nil.

Type LabelingWriter
type LabelingWriter struct {
	// contains filtered or unexported fields
}

LabelingWriter is an io.Writer that prepends prefix to the data written from the underlying writer. It prepends the prefix to the beginning of the stream and after every separator character. For example, it can be used to insert labels in the output of an exec.Cmd without modifying the command itself when working with multiple outstanding commands.

Methods
func (pr *LabelingWriter) Write(p []byte) (n int, err error)

Write implements io.Writer. It writes the data to the underlying writer, inserting the prefix at the beginning of the stream and after every separator character. It returns the number of bytes from p that were written rather than the total number of bytes including the label.

Type TailWriter
type TailWriter struct {
	// contains filtered or unexported fields
}

TailWriter is an io.Writer that keeps only the last N bytes written to it. It is useful for capturing the output of a command while limiting memory usage.

Functions
func NewTailWriter(n int) *TailWriter

NewTailWriter creates a new TailWriter that keeps the last n bytes.

Methods
func (w *TailWriter) Bytes() []byte

Bytes returns the contents of the TailWriter.

func (w *TailWriter) Write(p []byte) (n int, err error)

Write implements the io.Writer interface.

Examples

Example
TODO
  • cnicolaou: make sure all goroutines shutdown.

Documentation

Overview

Package executil provides utilities for working with os/exec.

Example
go func() {
	time.Sleep(time.Second * 10)
	buf := make([]byte, 1024*1024)
	n := runtime.Stack(buf, true)
	fmt.Fprintf(os.Stderr, "%s\n", string(buf[:n]))
	os.Exit(1)
}()
ctx := context.Background()
all := &bytes.Buffer{}
cmd := exec.CommandContext(ctx, catHelper, filepath.Join("testdata", "example")) // #nosec G204
ch := make(chan []byte, 1)
filter := executil.NewLineFilter(all, ch, regexp.MustCompile("filter me:"))
cmd.Stdout = filter
var wg sync.WaitGroup
wg.Go(func() {
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	if err := cmd.Wait(); err != nil {
		panic(err)
	}
})

buf := <-ch
fmt.Println("filtered output")
fmt.Println(string(buf))
fmt.Println("all of the output")
wg.Wait()
if err := filter.Close(); err != nil {
	fmt.Println(err)
}
fmt.Println(all.String())
Output:
filtered output
filter me: 33
all of the output
some words
some more words
filter me: 33
and again more words

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrAlreadyRunning = errors.New("another instance of this process is already running")

ErrAlreadyRunning is returned when another orchestrator already holds the run lock.

Functions

func AcquireRunLock

func AcquireRunLock(path string) (unlock func(), err error)

AcquireRunLock takes a non-blocking, exclusive file lock so that only one orchestrator runs at a time. The returned unlock function must be called when the run finishes; the lock is also released automatically when the process exits — including on crash or SIGKILL — so it never goes stale. If another instance already holds the lock, ErrAlreadyRunning is returned, annotated with the holder's PID and the lock path.

TryOpenFile is used rather than Mutex.TryLock so that the PID can be recorded through the locked handle itself. The Mutex's additional in-process guard is not needed: only RunCommand.Run acquires this lock, once per process.

func AppendMissingPathComponents

func AppendMissingPathComponents(pathList string, paths ...string) string

AppendMissingPathComponents treats pathList as a path list, ie. a list of paths separated by os.PathListSeparator. It returns a copy of pathList with any of the specified paths that do not already exist in pathList appended to the end of the returned string.

func ExecName

func ExecName(path string) string

ExecName returns path in a form suitable for use as an executable. For unix systems the path is unchanged. For windows a '.exe' suffix is added if not already present.

func Getenv

func Getenv(env []string, key string) (string, bool)

Getenv retrieves the value of an environment variable from the provided slice.

func GoBuild

func GoBuild(ctx context.Context, binary string, args ...string) (string, error)

GoBuild builds the specified Go binary using the provided context and arguments. It returns the path to the built binary or an error if the build fails.

func GoBuildArgs

func GoBuildArgs(binary string, args ...string) (string, []string, error)

GoBuildArgs returns the Go executable path, the arguments for a Go build command that outputs the specified binary, or an error if the Go executable could not be found. GoBuildArgs("mybinary", "a", "b") might return "/usr/local/go/bin/go", ["build", "-o", "mybinary", "a", "b"], nil.

func GoInstallArgs

func GoInstallArgs(args ...string) (string, []string, error)

GoInstallArgs returns the Go executable path and the arguments for a Go install command, or an error if the Go executable could not be found. GoInstall("a", "b") might return "/usr/local/go/bin/go", ["install", "a", "b"], nil.

func IsStopped

func IsStopped(pid int) bool

IsStopped returns true if the process with the specified pid has stopped or does not exist. Wait must have been called on the process otherwise this function will return true on some systems since the process may still exist as a defunct process.

func NewLabelingWriter

func NewLabelingWriter(w io.Writer, prefix []byte, separator rune) io.Writer

func NewLineFilter

func NewLineFilter(forward io.Writer, ch chan<- []byte, res ...*regexp.Regexp) io.WriteCloser

NewLineFilter returns an io.WriteCloser that scans the contents of the supplied io.Writer and sends lines that match the regexp to the supplied channel. It can be used to filter the output of a command started by the exec package for example for specific output. If no regexps are supplied, all lines are sent. Close must be called on the returned io.WriteCloser to ensure that all resources are reclaimed.

func ReplaceEnvVar

func ReplaceEnvVar(env []string, key, value string) []string

ReplaceEnvVar replaces the value of an environment variable in the provided slice. If the variable does not exist, it is added to the slice.

func SignalAndWait

func SignalAndWait(ctx context.Context, perSignalOrWait time.Duration, cmd *exec.Cmd, sigs ...os.Signal) error

SignalAndWait provides a convenience function to signal a process to terminate by sending it one or more signals and waiting for it to terminate but with a timeout on calling Wait and on waiting for the process to stop after each signal. The perSignalOrWait duration is used as the timeout for both calling Wait and for waiting for the process to stop after each signal, hence the total time spent waiting may be up to len(sigs)+1 times perSignalOrWait. If the process stops after any signal, SignalAndWait returns immediately.

func UserConfigDirPath

func UserConfigDirPath(path string) (string, error)

UserConfigDirPath appends path to the per-user config directory as returned by os.UserConfigDir.

func WaitFor

func WaitFor(ctx context.Context, interval time.Duration, check func(ctx context.Context) (done bool, err error)) error

WaitFor repeatedly calls the provided check function until it returns done=true, or the context is done. It waits for the specified interval between calls. If check returns an error, it is ignored unless done=true, in which case it is returned immediately.

func WaitForBackoff

func WaitForBackoff(ctx context.Context, backoff ratecontrol.Backoff, check func(ctx context.Context) (done bool, err error)) error

WaitForBackoff repeatedly calls the provided check function until it returns done=true, or the context is done. It waits for the next backoff delay between calls. If check returns an error, it is ignored unless done=true, in which case it is returned immediately.

func WaitForStopped

func WaitForStopped(ctx context.Context, pid int, waitFor time.Duration) error

WaitForStopped waits for the process with the specified pid to stop within the specified duration. It assumes that Wait has been called.

Types

type AsyncWait

type AsyncWait struct {
	// contains filtered or unexported fields
}

AsyncWait simplifies implementing asynchronous waiting for an exec.Cmd.

func NewAsyncWait

func NewAsyncWait(cmd *exec.Cmd) *AsyncWait

NewAsyncWait creates a new AsyncWait for the given exec.Cmd. It immediately starts a goroutine to wait for the cmd to complete.

func (*AsyncWait) Cmd

func (aw *AsyncWait) Cmd() *exec.Cmd

Cmd returns the exec.Cmd being waited on.

func (*AsyncWait) Wait

func (aw *AsyncWait) Wait() error

Wait waits for the cmd to complete and returns the error from cmd.Wait(). If the cmd has already completed, it returns immediately with the error from cmd.Wait().

func (*AsyncWait) WaitDone

func (aw *AsyncWait) WaitDone() (bool, error)

WaitDone reports whether the cmd has already completed. If so, it returns true and the error from cmd.Wait(), otherwise it returns false and nil.

type LabelingWriter

type LabelingWriter struct {
	// contains filtered or unexported fields
}

LabelingWriter is an io.Writer that prepends prefix to the data written from the underlying writer. It prepends the prefix to the beginning of the stream and after every separator character. For example, it can be used to insert labels in the output of an exec.Cmd without modifying the command itself when working with multiple outstanding commands.

func (*LabelingWriter) Write

func (pr *LabelingWriter) Write(p []byte) (n int, err error)

Write implements io.Writer. It writes the data to the underlying writer, inserting the prefix at the beginning of the stream and after every separator character. It returns the number of bytes from p that were written rather than the total number of bytes including the label.

type TailWriter

type TailWriter struct {
	// contains filtered or unexported fields
}

TailWriter is an io.Writer that keeps only the last N bytes written to it. It is useful for capturing the output of a command while limiting memory usage.

func NewTailWriter

func NewTailWriter(n int) *TailWriter

NewTailWriter creates a new TailWriter that keeps the last n bytes.

func (*TailWriter) Bytes

func (w *TailWriter) Bytes() []byte

Bytes returns the contents of the TailWriter.

func (*TailWriter) Write

func (w *TailWriter) Write(p []byte) (n int, err error)

Write implements the io.Writer interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL