reaper

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jul 27, 2025 License: MIT Imports: 7 Imported by: 25

README

go-reaper

Process (grim) reaper library for golang - this is useful for cleaning up zombie processes inside docker containers (which do not have an init process running as pid 1).

tl;dr

import reaper "github.com/ramr/go-reaper"

func main() {
        //  Start background reaping of orphaned child processes.
        go reaper.Reap()

        //  Rest of your code ...

        //  Note: If you also manage processes within your code aka
        //        exec commands or include some code that does do that,
        //        please refer to the section titled
        //        "[Into The Woods]"(https://github.com/ramr/go-reaper#into-the-woods)
}

How and Why

If you run a container without an init process (pid 1) which would normally reap zombie processes, you could well end up with a lot of zombie processes and eventually exhaust the max process limit on your system.

If you have a golang program that runs as pid 1, then this library allows the golang program to setup a background signal handling mechanism to handle the death of those orphaned children and not create a load of zombies inside the pid namespace your container runs in.

Usage

For basic usage, see the tl;dr section above. This should be the most commonly used route you'd need to take.

Road Less Traveled

But for those that prefer to go down "the road less traveled", you can control whether to disable pid 1 checks and/or control the options passed to the wait4 (or waitpid) system call by passing configuration to the reaper, enable subreaper functionality and optionally get notified when child processes are reaped.

import reaper "github.com/ramr/go-reaper"

func main() {
        config := reaper.Config{
                Pid:                  0,
                Options:              0,
                Debug:                true,
                DisablePid1Check:     false,
                EnableChildSubreaper: false,

                //  If you wish to get notified whenever a child process is
                //  reaped, use a `buffered` status channel.
                //      StatusChannel: make(chan reaper.Status, 42),
                StatusChannel: nil,
        }

        //  Only use this if you care about status notifications
        //  for reaped process (aka StatusChannel != nil).
        if config.StatusChannel != nil {
                go func() {
                        select {
                        case status, ok := <-config.StatusChannel:
                                if !ok {
                                        return
                                }
                                // process status (reaper.Status)
                        }
                }()
        }

        //  Start background reaping of orphaned child processes.
        go reaper.Start(config)

        //  Rest of your code ...
}

The Pid and Options fields in the configuration are the pid and options passed to the linux wait4 system call.

See the man pages for the wait4 or waitpid system call for details.

Into The Woods

And finally, this part is for those folks that want to go into the woods. This could be required when you need to manage the processes you invoke inside your code (ala with os.exec.Command* or syscall.ForkExec or any variants) or basically include some libraries/code that need to do the same. In such a case, it is better to run the reaper in a separate process as pid 1 and run your code inside a child process. This will still be part of the same code base but just forked off so that the reaper runs inside a different process ...

import (
        reaper "github.com/ramr/go-reaper"
)

func main() {
        config := reaper.MakeConfig()  //  or reaper.Config{ ... }

        //  Note: Any code before this line will run in both the parent and
        //        the forked child.
        reaper.RunForked(config)

        //  Code here will only run in the forked child process.
        //  Rest of your code goes here ...

}  /*  End of func  main.  */

There is also aWithReaper wrapper to run the above code scoped within a callback function (really just syntactic sugar around RunForked).

import (
        reaper "github.com/ramr/go-reaper"
)

func main() {
        config := reaper.MakeConfig()  //  or reaper.Config{ ... }

        reaper.WithReaper(config, func(err) int {
                if err != nil {
                        //  Failed to launch a child. Handle errors and
                        //  return a process exit code.

                        //  <insert-error-handling-code-here>
                        return 76 // EX_PROTOCOL
                }

                //  _NO_ errors, this is running in the child process ...
                //  Dew your code here and return child process exit code.

                //  <insert-your-run-in-child-process-code-here>
                //  Rest of your code goes here ...
                return 0  //  EX_OK!
        })

        // Any code here is never reached.

}  /*  End of func  main.  */

Documentation

Index

Constants

View Source
const (
	// Default env indicator for differentiating between the parent
	// and child processes.
	DEFAULT_ENV_INDICATOR = "GRIM_REAPER"
)

Variables

This section is empty.

Functions

func EnableChildSubReaper added in v0.3.1

func EnableChildSubReaper() error

Enable child subreaper.

func Reap

func Reap()

Normal entry point for the reaper code. Start reaping children in the background inside a goroutine.

func RunForked added in v0.3.0

func RunForked(config Config)

Run processes in forked mode patterned on "into the woods". The parent process starts up the reaper and a new child process and waits on the child process to terminate and exits. This call will return back only in the forked child process.

func Start

func Start(config Config)

Entry point for invoking the reaper code with a specific configuration. The config allows you to bypass the pid 1 checks, so handle with care. The child processes are reaped in the background inside a goroutine.

func WithReaper added in v0.3.0

func WithReaper(config Config, ep EntryPoint)

Wrapper to run reaper in forked mode with a "child entry point" ... sounds ELF-in but this is just some syntactic sugar around `RunForked` along with some more restrictions and "callback hell" attached to it!

Types

type Config

type Config struct {
	Pid                  int
	Options              int
	DisablePid1Check     bool
	EnableChildSubreaper bool
	StatusChannel        chan Status
	CloneEnvIndicator    string
	DisableCallerCheck   bool
	Debug                bool
}

Reaper configuration.

func MakeConfig added in v0.3.0

func MakeConfig() Config

Make and return the default config.

type EntryPoint added in v0.3.0

type EntryPoint func(err error) int

Callback entry point [function] for WithReaper.

type Status added in v0.2.2

type Status struct {
	Pid        int
	Err        error
	WaitStatus syscall.WaitStatus
}

Reaped child process status information.

Jump to

Keyboard shortcuts

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