Full Code of krallin/tini for AI

master 369448a167e8 cached
29 files
83.1 KB
28.4k tokens
36 symbols
1 requests
Download .txt
Repository: krallin/tini
Branch: master
Commit: 369448a167e8
Files: 29
Total size: 83.1 KB

Directory structure:
gitextract_u8tw2nf4/

├── .dockerignore
├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── README.md
├── ci/
│   ├── install_deps.sh
│   ├── run_build.sh
│   └── util/
│       └── rpmbuild
├── ddist.sh
├── dtest.sh
├── run_tests.sh
├── sign.key.enc
├── src/
│   ├── tini.c
│   ├── tiniConfig.h.in
│   └── tiniLicense.h
├── test/
│   ├── pdeathsignal/
│   │   ├── stage_1.py
│   │   └── stage_2.py
│   ├── pgroup/
│   │   └── stage_1.py
│   ├── reaping/
│   │   ├── stage_1.py
│   │   └── stage_2.py
│   ├── run_inner_tests.py
│   ├── run_outer_tests.py
│   ├── sigconf/
│   │   └── sigconf-test.c
│   ├── signals/
│   │   └── test.py
│   └── subreaper-proxy.py
└── tpl/
    ├── README.md.in
    └── VERSION.in

================================================
FILE CONTENTS
================================================

================================================
FILE: .dockerignore
================================================
./dist


================================================
FILE: .gitignore
================================================
dist
sign.key
.env
.mypy_cache


================================================
FILE: .travis.yml
================================================
sudo: required
services:
  - docker

language: generic

env:
  matrix:
    - ARCH_SUFFIX= CC=gcc ARCH_NATIVE=1 MINIMAL=
    - ARCH_SUFFIX=amd64 CC=gcc ARCH_NATIVE=1 MINIMAL=
    - ARCH_SUFFIX=amd64 CC=gcc ARCH_NATIVE=1 MINIMAL=1
    - ARCH_SUFFIX=x86_64 CC=gcc ARCH_NATIVE=1 MINIMAL=
    - ARCH_SUFFIX=x86_64 CC=gcc ARCH_NATIVE=1 MINIMAL=1
    - ARCH_SUFFIX=arm64 CC=aarch64-linux-gnu-gcc ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=armel CC=arm-linux-gnueabi-gcc ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=armhf CC=arm-linux-gnueabihf-gcc ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=i386 CFLAGS="-m32" ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=muslc-amd64 CC=musl-gcc ARCH_NATIVE=1 MINIMAL=
    - ARCH_SUFFIX=ppc64el CC=powerpc64le-linux-gnu-gcc ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=ppc64le CC=powerpc64le-linux-gnu-gcc ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=s390x CC=s390x-linux-gnu-gcc ARCH_NATIVE= MINIMAL=
    - ARCH_SUFFIX=mips64el CC=mips64el-linux-gnuabi64-gcc-5 ARCH_NATIVE= MINIMAL=
  global:
    - secure: "RKF9Z9gLxp6k/xITqn7ma1E9HfpYcDXuJFf4862WeH9EMnK9lDq+TWnGsQfkIlqh8h9goe7U+BvRiTibj9MiD5u7eluLo3dlwsLxPpYtyswYeLeC1wKKdT5LPGAXbRKomvBalRYMI+dDnGIM4w96mHgGGvx2zZXGkiAQhm6fJ3k="
    - DIST_DIR="${PWD}/dist"

before_install:
  - openssl aes-256-cbc -K $encrypted_2893fd5649e7_key -iv $encrypted_2893fd5649e7_iv -in sign.key.enc -out sign.key -d || echo "Encrypted signing key unavailable"

script:
  - ./ddist.sh
  - ls -lah "$DIST_DIR"
  - git diff --exit-code

deploy:
  provider: releases
  api_key:
    secure: VaYWmhdyhPYNvUy0tlGBYdjsdHIGHh/jwYzC96DBLS9BYIErtBkm21sdCLPKuNI1mGOwqoUjY+WywV2zJaBG10iBQCuFLpA9bblnN4fi257m79z7zqMbCvM145Up9x2jMQ0v03avd6pxCfQsr8WC9fnhWVYaD68Ir/hTpjZ60u8=
  file: "${DIST_DIR}/*"
  file_glob: true
  skip_cleanup: true
  on:
    repo: krallin/tini
    tags: true
    condition: '-z "$MINIMAL"'


================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required (VERSION 2.8.12...3.10)
project (tini C)

# Config
set (tini_VERSION_MAJOR 0)
set (tini_VERSION_MINOR 19)
set (tini_VERSION_PATCH 0)

# Build options
option(MINIMAL "Disable argument parsing and verbose output" OFF)

if(MINIMAL)
	add_definitions(-DTINI_MINIMAL=1)
endif()

# Extract git version and dirty-ness
execute_process (
  COMMAND git --git-dir "${PROJECT_SOURCE_DIR}/.git" --work-tree "${PROJECT_SOURCE_DIR}" log -n 1 --date=local --pretty=format:%h
  WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
  RESULT_VARIABLE git_version_check_ret
  OUTPUT_VARIABLE tini_VERSION_GIT
)

execute_process(
  COMMAND git --git-dir "${PROJECT_SOURCE_DIR}/.git" --work-tree "${PROJECT_SOURCE_DIR}" status --porcelain --untracked-files=no
  WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}"
  OUTPUT_VARIABLE git_dirty_check_out
)

if("${git_version_check_ret}" EQUAL 0)
  set(tini_VERSION_GIT " - git.${tini_VERSION_GIT}")
  if(NOT "${git_dirty_check_out}" STREQUAL "")
    set(tini_VERSION_GIT "${tini_VERSION_GIT}-dirty")
  endif()
else()
  set(tini_VERSION_GIT "")
endif()

# Flags
include(CheckCSourceCompiles)

check_c_source_compiles("
#ifndef _FORTIFY_SOURCE
#error \"Not defined: _FORTIFY_SOURCE\"
#endif
int main(void)                                                                                                              {
  return 0;
}
" HAS_BUILTIN_FORTIFY)

# Flags
if(NOT HAS_BUILTIN_FORTIFY)
	add_definitions(-D_FORTIFY_SOURCE=2)
endif()

set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Werror -Wextra -Wall -pedantic-errors -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat")
set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-s")

# Build

configure_file (
	"${PROJECT_SOURCE_DIR}/src/tiniConfig.h.in"
	"${PROJECT_BINARY_DIR}/tiniConfig.h"
	@ONLY
)

configure_file (
	"${PROJECT_SOURCE_DIR}/tpl/README.md.in"
	"${PROJECT_SOURCE_DIR}/README.md"
	@ONLY
)

configure_file (
	"${PROJECT_SOURCE_DIR}/tpl/VERSION.in"
	"${PROJECT_BINARY_DIR}/VERSION"
	@ONLY
)


include_directories ("${PROJECT_BINARY_DIR}")

add_executable (tini src/tini.c)

add_executable (tini-static src/tini.c)
set_target_properties (tini-static PROPERTIES LINK_FLAGS "-Wl,--no-export-dynamic -static")

# Installation
install (TARGETS tini DESTINATION bin)
install (TARGETS tini-static DESTINATION bin)

# Packaging
include (InstallRequiredSystemLibraries)
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "A tiny but valid init process for containers")
set (CPACK_PACKAGE_VENDOR "Thomas Orozco")
set (CPACK_PACKAGE_CONTACT "thomas@orozco.fr")
set (CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.md")
set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
set (CPACK_PACKAGE_VERSION_MAJOR "${tini_VERSION_MAJOR}")
set (CPACK_PACKAGE_VERSION_MINOR "${tini_VERSION_MINOR}")
set (CPACK_PACKAGE_VERSION_PATCH "${tini_VERSION_PATCH}")
set (CPACK_PACKAGE_EXECUTABLES "${CMAKE_PROJECT_NAME}")
set (CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
set (CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${tini_VERSION_MAJOR}.${tini_VERSION_MINOR}.${tini_VERSION_PATCH}")
set (CPACK_PACKAGE_VERSION "${tini_VERSION_MAJOR}.${tini_VERSION_MINOR}.${tini_VERSION_PATCH}")

set (CPACK_DEBIAN_PACKAGE_ARCHITECTURE "amd64")  # TODO
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.3.4)")

set (CPACK_RPM_PACKAGE_ARCHITECTURE "x86_64")

set (CPACK_GENERATOR "DEB" "RPM")

include (CPack)


================================================
FILE: Dockerfile
================================================
FROM ubuntu:xenial

ARG ARCH_SUFFIX

COPY ci/install_deps.sh /install_deps.sh
RUN /install_deps.sh

# Pre-install those here for faster local builds.
RUN CFLAGS="-DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37" python3 -m pip install psutil python-prctl bitmap

ARG ARCH_NATIVE
ARG CC

# Persist ARGs into the image

ENV ARCH_SUFFIX="$ARCH_SUFFIX" \
    ARCH_NATIVE="$ARCH_NATIVE" \
    CC="$CC"


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2015 Thomas Orozco <thomas@orozco.fr>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
<!--

#####################################
# THIS FILE IS AUTOGENERATED!       #
# Edit ./tpl/README.md.in instead   #
#####################################

-->


Tini - A tiny but valid `init` for containers
=============================================

[![Build Status](https://travis-ci.org/krallin/tini.svg?branch=master)](https://travis-ci.org/krallin/tini)

Tini is the simplest `init` you could think of.

All Tini does is spawn a single child (Tini is meant to be run in a container),
and wait for it to exit all the while reaping zombies and performing
signal forwarding.


Why Tini?
---------

Using Tini has several benefits:

- It protects you from software that accidentally creates zombie processes,
  which can (over time!) starve your entire system for PIDs (and make it
  unusable).
- It ensures that the *default signal handlers* work for the software you run
  in your Docker image. For example, with Tini, `SIGTERM` properly terminates
  your process even if you didn't explicitly install a signal handler for it.
- It does so completely transparently! Docker images that work without Tini
  will work with Tini without any changes.

If you'd like more detail on why this is useful, review this issue discussion:
[What is advantage of Tini?][0].


Using Tini
----------

*NOTE: If you are using Docker 1.13 or greater, Tini is included in Docker
itself. This includes all versions of Docker CE. To enable Tini, just [pass the
`--init` flag to `docker run`][5].*

*NOTE: There are [pre-built Docker images available for Tini][10]. If
you're currently using an Ubuntu or CentOS image as your base, you can use
one of those as a drop-in replacement.*

*NOTE: There are Tini packages for Alpine Linux and NixOS. See below for
installation instructions.*

Add Tini to your container, and make it executable. Then, just invoke Tini
and pass your program and its arguments as arguments to Tini.

In Docker, you will want to use an entrypoint so you don't have to remember
to manually invoke Tini:

    # Add Tini
    ENV TINI_VERSION=v0.19.0
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
    RUN chmod +x /tini
    ENTRYPOINT ["/tini", "--"]

    # Run your program under Tini
    CMD ["/your/program", "-and", "-its", "arguments"]
    # or docker run your-image /your/program ...

Note that you *can* skip the `--` under certain conditions, but you might
as well always include it to be safe. If you see an error message that
looks like `tini: invalid option -- 'c'`, then you *need* to add the `--`.

Arguments for Tini itself should be passed like `-v` in the following example:
`/tini -v -- /your/program`.

*NOTE: The binary linked above is a 64-bit dynamically-linked binary.*


### Signed binaries ###

The `tini` and `tini-static` binaries are signed using the key `595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7`.

You can verify their signatures using `gpg` (which you may install using
your package manager):

    ENV TINI_VERSION v0.19.0
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini.asc /tini.asc
    RUN gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 \
     && gpg --batch --verify /tini.asc /tini
    RUN chmod +x /tini


### Verifying binaries via checksum ###

The `tini` and `tini-static` binaries have generated checksums (`SHA1` and `SHA256`).

You can verify their checksums using `sha1sum` and `sha256sum` (which you may install using
your package manager):

    ENV TINI_VERSION v0.19.0
    RUN wget --no-check-certificate --no-cookies --quiet https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-amd64 \
        && wget --no-check-certificate --no-cookies --quiet https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-amd64.sha256sum \
        && echo "$(cat tini-amd64.sha256sum)" | sha256sum -c


### Alpine Linux Package ###

On Alpine Linux, you can use the following command to install Tini:

    RUN apk add --no-cache tini
    # Tini is now available at /sbin/tini
    ENTRYPOINT ["/sbin/tini", "--"]


### NixOS ###

Using Nix, you can use the following command to install Tini:

    nix-env --install tini


### Debian ###

On Debian (Buster or newer), you can use the following command to install Tini:

    apt-get install tini

Note that this installs `/usr/bin/tini` (and `/usr/bin/tini-static`), not `/tini`.


### Arch Linux ###

On Arch Linux, there is a package available on the [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository).
Install using the [official instructions](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages)
or use an [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers):

    pacaur -S tini


### Other Platforms ###

ARM and 32-bit binaries are available! You can find the complete list of
available binaries under [the releases tab][11].


Options
-------

### Verbosity ###

The `-v` argument can be used for extra verbose output (you can pass it up to
3 times, e.g. `-vvv`).


### Subreaping ###

By default, Tini needs to run as PID 1 so that it can reap zombies (by
running as PID 1, zombies get re-parented to Tini).

If for some reason, you cannot run Tini as PID 1, you should register Tini as
a process subreaper instead (only in Linux >= 3.4), by either:

  + Passing the `-s` argument to Tini (`tini -s -- ...`)
  + Setting the environment variable `TINI_SUBREAPER`
    (e.g. `export TINI_SUBREAPER=`).

This will ensure that zombies get re-parented to Tini despite Tini not running
as PID 1.

*NOTE: Tini will issue a warning if it detects that it isn't running as PID 1
and isn't registered as a subreaper. If you don't see a warning, you're fine.*


### Remapping exit codes ###

Tini will reuse the child's exit code when exiting, but occasionally, this may
not be exactly what you want (e.g. if your child exits with 143 after receiving
SIGTERM). Notably, this can be an issue with Java apps.

In this case, you can use the `-e` flag to remap an arbitrary exit code to 0.
You can pass the flag multiple times if needed.

For example:

```
tini -e 143 -- ...
```


### Process group killing ###

By default, Tini only kills its immediate child process.  This can be
inconvenient if sending a signal to that process does not have the desired
effect.  For example, if you do

    docker run --rm krallin/ubuntu-tini sh -c 'sleep 10'

and ctrl-C it, nothing happens: SIGINT is sent to the 'sh' process,
but that shell won't react to it while it is waiting for the 'sleep'
to finish.

You can configure Tini to kill the child process group, so that every process
in the group gets the signal, by either:

  + Passing the `-g` argument to Tini (`tini -g -- ...`)
  + Setting the environment variable `TINI_KILL_PROCESS_GROUP`
    (e.g. `export TINI_KILL_PROCESS_GROUP=`).

This corresponds more closely to what happens when you do ctrl-C etc. in a
terminal: The signal is sent to the foreground process group.

    docker run --rm --entrypoint tini krallin/ubuntu-tini -g -- sh -c 'sleep 10'

### Parent Death Signal ###

Tini can set its parent death signal, which is the signal Tini should receive
when *its* parent exits. To set the parent death signal, use the `-p` flag with
the name of the signal Tini should receive when its parent exits:

```
tini -p SIGTERM -- ...
```

*NOTE: See [this PR discussion][12] to learn more about the parent death signal
and use cases.*


More
----

### Existing Entrypoint ###

Tini can also be used with an existing entrypoint in your container!

Assuming your entrypoint was `/docker-entrypoint.sh`, then you would use:

    ENTRYPOINT ["/tini", "--", "/docker-entrypoint.sh"]


### Statically-Linked Version ###

Tini has very few dependencies (it only depends on libc), but if your
container fails to start, you might want to consider using the statically-built
version instead:

    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini


### Size Considerations ###

Tini is a very small file (in the 10KB range), so it doesn't add much weight
to your container.

The statically-linked version is bigger, but still < 1M.


Building Tini
-------------

If you'd rather not download the binary, you can build Tini by running
`cmake . && make`.

Before building, you probably also want to run:

    export CFLAGS="-DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37"

This ensure that even if you're building on a system that has old Linux Kernel
headers (< 3.4), Tini will be built with child subreaper support. This is
usually what you want if you're going to use Tini with Docker (if your host
Kernel supports Docker, it should also support child subreapers).


Understanding Tini
------------------

After spawning your process, Tini will wait for signals and forward those
to the child process, and periodically reap zombie processes that may be
created within your container.

When the "first" child process exits (`/your/program` in the examples above),
Tini exits as well, with the exit code of the child process (so you can
check your container's exit code to know whether the child exited
successfully).


Debugging
---------

If something isn't working just like you expect, consider increasing the
verbosity level (up to 3):

    tini -v    -- bash -c 'exit 1'
    tini -vv   -- true
    tini -vvv  -- pwd


Authors
=======

Maintainer:

  + [Thomas Orozco][20]

Contributors:

  + [Tianon Gravi][30]
  + [David Wragg][31]
  + [Michael Crosby][32]
  + [Wyatt Preul][33]
  + [Patrick Steinhardt][34]

Special thanks to:

  + [Danilo Bürger][40] for packaging Tini for Alpine
  + [Asko Soukka][41] for packaging Tini for Nix
  + [nfnty][42] for packaging Tini for Arch Linux


  [0]: https://github.com/krallin/tini/issues/8
  [5]: https://docs.docker.com/engine/reference/commandline/run/
  [10]: https://github.com/krallin/tini-images
  [11]: https://github.com/krallin/tini/releases
  [12]: https://github.com/krallin/tini/pull/114
  [20]: https://github.com/krallin/
  [30]: https://github.com/tianon
  [31]: https://github.com/dpw
  [32]: https://github.com/crosbymichael
  [33]: https://github.com/geek
  [34]: https://github.com/pks-t
  [40]: https://github.com/danilobuerger
  [41]: https://github.com/datakurre
  [42]: https://github.com/nfnty/pkgbuilds/tree/master/tini/tini


================================================
FILE: ci/install_deps.sh
================================================
#!/bin/bash
set -o errexit
set -o nounset
set -o xtrace

DEPS=(
  build-essential git gdb valgrind cmake rpm file
  libcap-dev python3-dev python3-pip python3-setuptools
  hardening-includes gnupg
)

case "${ARCH_SUFFIX-}" in
  amd64|x86_64|'') ;;
  arm64) DEPS+=(gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu libc6-dev-arm64-cross) ;;
  armel) DEPS+=(gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi libc6-dev-armel-cross) ;;
  armhf) DEPS+=(gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf libc6-dev-armhf-cross) ;;
  i386) DEPS+=(libc6-dev-i386  gcc-multilib) ;;
  muslc-amd64) DEPS+=(musl-tools) ;;
  ppc64el|ppc64le) DEPS+=(gcc-powerpc64le-linux-gnu binutils-powerpc64le-linux-gnu libc6-dev-ppc64el-cross) ;;
  s390x) DEPS+=(gcc-s390x-linux-gnu binutils-s390x-linux-gnu libc6-dev-s390x-cross) ;;
  mips64el) DEPS+=(gcc-5-mips64el-linux-gnuabi64 binutils-mips64el-linux-gnuabi64 libc6-dev-mips64el-cross) ;;
  *) echo "Unknown ARCH_SUFFIX=${ARCH_SUFFIX-}"; exit 1 ;;
esac

apt-get update
apt-get install --no-install-recommends --yes "${DEPS[@]}"
rm -rf /var/lib/apt/lists/*

python3 -m pip install --upgrade pip
python3 -m pip install virtualenv


================================================
FILE: ci/run_build.sh
================================================
#!/bin/bash
# Should be run from the root dir, or SOURCE_DIR should be set.
set -o errexit
set -o nounset
set -o pipefail

# Default compiler
: ${CC:="gcc"}


# Paths
: ${SOURCE_DIR:="."}
: ${DIST_DIR:="${SOURCE_DIR}/dist"}
: ${BUILD_DIR:="/tmp/build"}

# GPG Configuration
: ${GPG_PASSPHRASE:=""}


# Make those paths absolute, and export them for the Python tests to consume.
export SOURCE_DIR="$(readlink -f "${SOURCE_DIR}")"
export DIST_DIR="$(readlink -f "${DIST_DIR}")"
export BUILD_DIR="$(readlink -f "${BUILD_DIR}")"

# Configuration
: ${FORCE_SUBREAPER:="1"}
export FORCE_SUBREAPER


# Our build platform doesn't have those newer Linux flags, but we want Tini to have subreaper support
# We also use those in our tests
CFLAGS="${CFLAGS-} -DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37"
if [[ "${FORCE_SUBREAPER}" -eq 1 ]]; then
  # If FORCE_SUBREAPER is requested, then we set those CFLAGS for the Tini build
  export CFLAGS
fi

echo "CC=${CC}"
echo "CFLAGS=${CFLAGS}"
echo "MINIMAL=${MINIMAL-}"
echo "ARCH_SUFFIX=${ARCH_SUFFIX-}"
echo "ARCH_NATIVE=${ARCH_NATIVE-}"

# Ensure Python output is not buffered (to make tests output clearer)
export PYTHONUNBUFFERED=1

# Set path to prioritize our utils
export REAL_PATH="${PATH}"
export PATH="${SOURCE_DIR}/ci/util:${PATH}"

# Build
CMAKE_ARGS=(-B"${BUILD_DIR}" -H"${SOURCE_DIR}")
if [[ -n "${MINIMAL:-}" ]]; then
  CMAKE_ARGS+=(-DMINIMAL=ON)
fi
cmake "${CMAKE_ARGS[@]}"

pushd "${BUILD_DIR}"
make clean
make
if [[ -n "${ARCH_NATIVE-}" ]]; then
  make package
fi
popd

pkg_version="$(cat "${BUILD_DIR}/VERSION")"

if [[ -n "${ARCH_NATIVE-}" ]]; then
  echo "Built native package (ARCH_NATIVE=${ARCH_NATIVE-})"
  echo "Running smoke and internal tests"

  BIN_TEST_DIR="${BUILD_DIR}/bin-test"
  mkdir -p "$BIN_TEST_DIR"
  export PATH="${BIN_TEST_DIR}:${PATH}"

  # Smoke tests (actual tests need Docker to run; they don't run within the CI environment)
  for tini in "${BUILD_DIR}/tini" "${BUILD_DIR}/tini-static"; do
    echo "Smoke test for ${tini}"
    "$tini" --version

    echo "Testing ${tini} --version"
    "$tini" --version | grep -q "tini version"

    echo "Testing ${tini} without arguments exits with 1"
    ! "$tini" 2>/dev/null

    echo "Testing ${tini} shows help message"
    {
      ! "$tini" 2>&1
    } | grep -q "supervision of a valid init process"

    if [[ -n "${MINIMAL:-}" ]]; then
      echo "Testing $tini with: true"
      "${tini}" true

      echo "Testing $tini with: false"
      if "${tini}" false; then
        exit 1
      fi

      echo "Testing ${tini} does not reference options that don't exist"
      ! {
        ! "$tini" 2>&1
      } | grep -q "more verbose"

      # We try running binaries named after flags (both valid and invalid
      # flags) and test that they run.
      for flag in h s w x; do
        bin="-${flag}"
        echo "Testing $tini can run binary: ${bin}"
        cp "$(which true)" "${BIN_TEST_DIR}/${bin}"
        "$tini" "$bin"
      done

      echo "Testing $tini can run binary --version if args are given"
      cp "$(which true)" "${BIN_TEST_DIR}/--version"
      if "$tini" "--version" --foo | grep -q "tini version"; then
        exit 1
      fi
    else
      echo "Testing ${tini} -h"
      "${tini}" -h

      echo "Testing $tini for license"
      "$tini" -l | diff - "${SOURCE_DIR}/LICENSE"

      echo "Testing $tini with: true"
      "${tini}" -vvv true

      echo "Testing $tini with: false"
      if "${tini}" -vvv false; then
        exit 1
      fi

      echo "Testing ${tini} references options that exist"
      {
        ! "$tini" 2>&1
      } | grep -q "more verbose"

      echo "Testing $tini with: -- true (should succeed)"
      "${tini}" -vvv -- true

      echo "Testing $tini with: -- -- true (should fail)"
      if "${tini}" -vvv -- -- true; then
        exit 1
      fi
    fi

    echo "Testing ${tini} supports TINI_VERBOSITY"
    TINI_VERBOSITY=3 "$tini" true 2>&1 | grep -q 'Received SIGCHLD'

    echo "Testing ${tini} exits with 127 if the command does not exist"
    "$tini" foobar123 && rc="$?" || rc="$?"
    if [[ "$rc" != 127 ]]; then
      echo "Exit code was: ${rc}"
      exit 1
    fi

    echo "Testing ${tini} exits with 126 if the command is not executable"
    "$tini" /etc && rc="$?" || rc="$?"
    if [[ "$rc" != 126 ]]; then
      echo "Exit code was: ${rc}"
      exit 1
    fi

    # Test stdin / stdout are handed over to child
    echo "Testing ${tini} does not break pipes"
    echo "exit 0" | "${tini}" sh
    if [[ ! "$?" -eq "0" ]]; then
      echo "Pipe test failed"
      exit 1
    fi

    echo "Checking hardening on $tini"
    hardening_skip=(--nopie --nostackprotector --nobindnow)
    if [[ "$CC" == "musl-gcc" ]]; then
      # FORTIFY_SOURCE is a glibc thing
      hardening_skip=("${hardening_skip[@]}" --nofortify)
    fi
    hardening-check  "${hardening_skip[@]}" "${tini}"
  done

  # Quick package audit
  if which rpm >/dev/null; then
    echo "Contents for RPM:"
    rpm -qlp "${BUILD_DIR}/tini_${pkg_version}.rpm"
    echo "--"
  fi

  if which dpkg >/dev/null; then
    echo "Contents for DEB:"
    dpkg --contents "${BUILD_DIR}/tini_${pkg_version}.deb"
    echo "--"
  fi

  # Compile test code
  "${CC}" -o "${BUILD_DIR}/sigconf-test" "${SOURCE_DIR}/test/sigconf/sigconf-test.c"

  # Create virtual environment to run tests.
  # Accept system site packages for faster local builds.
  VENV="${BUILD_DIR}/venv"
  virtualenv --system-site-packages "${VENV}"

  # Don't use activate because it does not play nice with nounset
  export PATH="${VENV}/bin:${PATH}"
  export CFLAGS  # We need them to build our test suite, regardless of FORCE_SUBREAPER

  # Install test dependencies
  CC=gcc python3 -m pip install psutil python-prctl bitmap

  # Run tests
  python3 "${SOURCE_DIR}/test/run_inner_tests.py"
else
  echo "Not a native build, skipping smoke and internal tests"
fi

# Now, copy over files to DIST_DIR, with appropriate names depending on the
# architecture.
# Handle the DEB / RPM
mkdir -p "${DIST_DIR}"

DIST_TINIS=()

SUFFIX=""
if [[ -n "$ARCH_SUFFIX" ]]; then
  SUFFIX="-${ARCH_SUFFIX}"
elif [[ -z "$ARCH_NATIVE" ]]; then
  echo "Refusing to publish a non-native build without suffix!"
  exit 1
fi

for build_tini in tini tini-static; do
  dist_tini="${build_tini}${SUFFIX}"
  cp "${BUILD_DIR}/${build_tini}" "${DIST_DIR}/${dist_tini}"
  DIST_TINIS+=("$dist_tini")
done

if [[ -n "${ARCH_NATIVE-}" ]]; then
  for pkg_format in deb rpm; do
    build_tini="tini_${pkg_version}.${pkg_format}"
    dist_tini="tini_${pkg_version}${SUFFIX}.${pkg_format}"
    cp "${BUILD_DIR}/${build_tini}" "${DIST_DIR}/${dist_tini}"
    DIST_TINIS+=("$dist_tini")
  done
fi

echo "Tinis: ${DIST_TINIS[*]}"

pushd "$DIST_DIR"

for tini in "${DIST_TINIS[@]}"; do
  echo "${tini}:"

  for sum in sha1sum sha256sum; do
    "$sum" "$tini" | tee "${tini}.${sum}"
  done

  file "$tini"
  echo "--"
done

# If a signing key and passphrase are made available, then use it to sign the
# binaries
if [[ -n "$GPG_PASSPHRASE" ]] && [[ -f "${SOURCE_DIR}/sign.key" ]]; then
  echo "Signing tinis"
  GPG_SIGN_HOMEDIR="${BUILD_DIR}/gpg-sign"
  GPG_VERIFY_HOMEDIR="${BUILD_DIR}/gpg-verify"
  PGP_KEY_FINGERPRINT="595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7"
  PGP_KEYSERVER="ha.pool.sks-keyservers.net"

  mkdir "${GPG_SIGN_HOMEDIR}" "${GPG_VERIFY_HOMEDIR}"
  chmod 700 "${GPG_SIGN_HOMEDIR}" "${GPG_VERIFY_HOMEDIR}"

  gpg --homedir "${GPG_SIGN_HOMEDIR}" --import "${SOURCE_DIR}/sign.key"
  gpg --homedir "${GPG_VERIFY_HOMEDIR}" --keyserver "$PGP_KEYSERVER" --recv-keys "$PGP_KEY_FINGERPRINT"

  for tini in "${DIST_TINIS[@]}"; do
    echo "${GPG_PASSPHRASE}" | gpg --homedir "${GPG_SIGN_HOMEDIR}" --passphrase-fd 0 --armor --detach-sign "${tini}"
    gpg --homedir "${GPG_VERIFY_HOMEDIR}" --verify "${tini}.asc"
  done
fi

popd


================================================
FILE: ci/util/rpmbuild
================================================
#!/bin/bash
# Wrapper for rpm build that first removes files that should be in there
# We need this for compatibility with CMake <= 2.8.10 (which is the only version we have in Travis)
# See: http://www.cmake.org/pipermail/cmake-commits/2013-April/014818.html
set -o nounset
set -o errexit
set -o pipefail

# Remove PATH hack so we can find the real rpmbuild
export PATH="${REAL_PATH}"

echo "Using local rpmbuild"

specFile="${!#}"  # Last argument

for removeLine in '"/usr"' '"/usr/bin"'; do
  sed -i "s|${removeLine}||g" "${specFile}"
done

# Passthrough to rpmbuild
exec rpmbuild "${@}"


================================================
FILE: ddist.sh
================================================
#!/bin/bash
set -o errexit
set -o nounset

REL_HERE=$(dirname "${BASH_SOURCE}")
HERE=$(cd "${REL_HERE}"; pwd)

IMG="tini-build"

if [[ -n "${ARCH_SUFFIX-}" ]]; then
  IMG="${IMG}_${ARCH_SUFFIX}"
fi

if [[ -n "${ARCH_NATIVE-}" ]]; then
  IMG="${IMG}_native"
fi

if [[ -n "${CC-}" ]]; then
  IMG="${IMG}_${CC}"
fi

# Cleanup the build dir
rm -f "${HERE}/dist"/*

# Create the build image
echo "build: ${IMG}"

docker build \
  --build-arg "ARCH_SUFFIX=${ARCH_SUFFIX-}" \
  --build-arg "ARCH_NATIVE=${ARCH_NATIVE-}" \
  --build-arg "CC=${CC-gcc}" \
  -t "${IMG}" \
  .

# Build new Tini
SRC="/tini"

docker run -it --rm \
  --volume="${HERE}:${SRC}" \
  -e BUILD_DIR=/tmp/tini-build \
  -e SOURCE_DIR="${SRC}" \
  -e FORCE_SUBREAPER="${FORCE_SUBREAPER-1}" \
  -e GPG_PASSPHRASE="${GPG_PASSPHRASE-}" \
  -e CFLAGS="${CFLAGS-}" \
  -e MINIMAL="${MINIMAL-}" \
  -u "$(id -u):$(id -g)" \
  "${IMG}" "${SRC}/ci/run_build.sh"


================================================
FILE: dtest.sh
================================================
#!/bin/bash
set -o errexit
set -o nounset

IMG="tini"

if [[ "$#" != 1 ]]; then
  echo "Usage: $0 ARCH_SUFFIX"
  exit 1
fi
suffix="$1"

IMG="tini-build-${suffix}"
python test/run_outer_tests.py "${IMG}"


================================================
FILE: run_tests.sh
================================================
#!/bin/bash
set -o errexit
set -o nounset

REL_HERE=$(dirname "${BASH_SOURCE}")
HERE=$(cd "${REL_HERE}"; pwd)

for i in $(seq 0 1); do
  export FORCE_SUBREAPER="${i}"
  echo "Testing with FORCE_SUBREAPER=${FORCE_SUBREAPER}"
  "${HERE}/ddist.sh"
  "${HERE}/dtest.sh"
done


================================================
FILE: src/tini.c
================================================
/* See LICENSE file for copyright and license details. */
#define _GNU_SOURCE

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/prctl.h>

#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <libgen.h>

#include "tiniConfig.h"
#include "tiniLicense.h"

#if TINI_MINIMAL
#define PRINT_FATAL(...)                         fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
#define PRINT_WARNING(...)  if (verbosity > 0) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#define PRINT_INFO(...)     if (verbosity > 1) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_DEBUG(...)    if (verbosity > 2) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_TRACE(...)    if (verbosity > 3) { fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define DEFAULT_VERBOSITY 0
#else
#define PRINT_FATAL(...)                         fprintf(stderr, "[FATAL tini (%i)] ", getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");
#define PRINT_WARNING(...)  if (verbosity > 0) { fprintf(stderr, "[WARN  tini (%i)] ", getpid()); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#define PRINT_INFO(...)     if (verbosity > 1) { fprintf(stdout, "[INFO  tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_DEBUG(...)    if (verbosity > 2) { fprintf(stdout, "[DEBUG tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define PRINT_TRACE(...)    if (verbosity > 3) { fprintf(stdout, "[TRACE tini (%i)] ", getpid()); fprintf(stdout, __VA_ARGS__); fprintf(stdout, "\n"); }
#define DEFAULT_VERBOSITY 1
#endif

#define ARRAY_LEN(x)  (sizeof(x) / sizeof((x)[0]))

#define INT32_BITFIELD_SET(F, i)     ( F[(i / 32)] |=  (1 << (i % 32)) )
#define INT32_BITFIELD_CLEAR(F, i)   ( F[(i / 32)] &= ~(1 << (i % 32)) )
#define INT32_BITFIELD_TEST(F, i)    ( F[(i / 32)] &   (1 << (i % 32)) )
#define INT32_BITFIELD_CHECK_BOUNDS(F, i) do {  assert(i >= 0); assert(ARRAY_LEN(F) > (uint) (i / 32)); } while(0)

#define STATUS_MAX 255
#define STATUS_MIN 0

typedef struct {
   sigset_t* const sigmask_ptr;
   struct sigaction* const sigttin_action_ptr;
   struct sigaction* const sigttou_action_ptr;
} signal_configuration_t;

static const struct {
   char *const name;
   int number;
} signal_names[] = {
   { "SIGHUP", SIGHUP },
   { "SIGINT", SIGINT },
   { "SIGQUIT", SIGQUIT },
   { "SIGILL", SIGILL },
   { "SIGTRAP", SIGTRAP },
   { "SIGABRT", SIGABRT },
   { "SIGBUS", SIGBUS },
   { "SIGFPE", SIGFPE },
   { "SIGKILL", SIGKILL },
   { "SIGUSR1", SIGUSR1 },
   { "SIGSEGV", SIGSEGV },
   { "SIGUSR2", SIGUSR2 },
   { "SIGPIPE", SIGPIPE },
   { "SIGALRM", SIGALRM },
   { "SIGTERM", SIGTERM },
   { "SIGCHLD", SIGCHLD },
   { "SIGCONT", SIGCONT },
   { "SIGSTOP", SIGSTOP },
   { "SIGTSTP", SIGTSTP },
   { "SIGTTIN", SIGTTIN },
   { "SIGTTOU", SIGTTOU },
   { "SIGURG", SIGURG },
   { "SIGXCPU", SIGXCPU },
   { "SIGXFSZ", SIGXFSZ },
   { "SIGVTALRM", SIGVTALRM },
   { "SIGPROF", SIGPROF },
   { "SIGWINCH", SIGWINCH },
   { "SIGSYS", SIGSYS },
};

static unsigned int verbosity = DEFAULT_VERBOSITY;

static int32_t expect_status[(STATUS_MAX - STATUS_MIN + 1) / 32];

#ifdef PR_SET_CHILD_SUBREAPER
#define HAS_SUBREAPER 1
#define OPT_STRING "p:hvwgle:s"
#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
#else
#define HAS_SUBREAPER 0
#define OPT_STRING "p:hvwgle:"
#endif

#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
#define KILL_PROCESS_GROUP_GROUP_ENV_VAR "TINI_KILL_PROCESS_GROUP"

#define TINI_VERSION_STRING "tini version " TINI_VERSION TINI_GIT


#if HAS_SUBREAPER
static unsigned int subreaper = 0;
#endif
static unsigned int parent_death_signal = 0;
static unsigned int kill_process_group = 0;

static unsigned int warn_on_reap = 0;

static struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };

static const char reaper_warning[] = "Tini is not running as PID 1 "
#if HAS_SUBREAPER
       "and isn't registered as a child subreaper"
#endif
".\n\
Zombie processes will not be re-parented to Tini, so zombie reaping won't work.\n\
To fix the problem, "
#if HAS_SUBREAPER
#ifndef TINI_MINIMAL
"use the -s option or "
#endif
"set the environment variable " SUBREAPER_ENV_VAR " to register Tini as a child subreaper, or "
#endif
"run Tini as PID 1.";

int restore_signals(const signal_configuration_t* const sigconf_ptr) {
	if (sigprocmask(SIG_SETMASK, sigconf_ptr->sigmask_ptr, NULL)) {
		PRINT_FATAL("Restoring child signal mask failed: '%s'", strerror(errno));
		return 1;
	}

	if (sigaction(SIGTTIN, sigconf_ptr->sigttin_action_ptr, NULL)) {
		PRINT_FATAL("Restoring SIGTTIN handler failed: '%s'", strerror((errno)));
		return 1;
	}

	if (sigaction(SIGTTOU, sigconf_ptr->sigttou_action_ptr, NULL)) {
		PRINT_FATAL("Restoring SIGTTOU handler failed: '%s'", strerror((errno)));
		return 1;
	}

	return 0;
}

int isolate_child(void) {
	// Put the child into a new process group.
	if (setpgid(0, 0) < 0) {
		PRINT_FATAL("setpgid failed: %s", strerror(errno));
		return 1;
	}

	// If there is a tty, allocate it to this new process group. We
	// can do this in the child process because we're blocking
	// SIGTTIN / SIGTTOU.

	// Doing it in the child process avoids a race condition scenario
	// if Tini is calling Tini (in which case the grandparent may make the
	// parent the foreground process group, and the actual child ends up...
	// in the background!)
	if (tcsetpgrp(STDIN_FILENO, getpgrp())) {
		if (errno == ENOTTY) {
			PRINT_DEBUG("tcsetpgrp failed: no tty (ok to proceed)");
		} else if (errno == ENXIO) {
			// can occur on lx-branded zones
			PRINT_DEBUG("tcsetpgrp failed: no such device (ok to proceed)");
		} else {
			PRINT_FATAL("tcsetpgrp failed: %s", strerror(errno));
			return 1;
		}
	}

	return 0;
}


int spawn(const signal_configuration_t* const sigconf_ptr, char* const argv[], int* const child_pid_ptr) {
	pid_t pid;

	// TODO: check if tini was a foreground process to begin with (it's not OK to "steal" the foreground!")

	pid = fork();
	if (pid < 0) {
		PRINT_FATAL("fork failed: %s", strerror(errno));
		return 1;
	} else if (pid == 0) {

		// Put the child in a process group and make it the foreground process if there is a tty.
		if (isolate_child()) {
			return 1;
		}

		// Restore all signal handlers to the way they were before we touched them.
		if (restore_signals(sigconf_ptr)) {
			return 1;
		}

		execvp(argv[0], argv);

		// execvp will only return on an error so make sure that we check the errno
		// and exit with the correct return status for the error that we encountered
		// See: http://www.tldp.org/LDP/abs/html/exitcodes.html#EXITCODESREF
		int status = 1;
		switch (errno) {
			case ENOENT:
				status = 127;
				break;
			case EACCES:
				status = 126;
				break;
		}
		PRINT_FATAL("exec %s failed: %s", argv[0], strerror(errno));
		return status;
	} else {
		// Parent
		PRINT_INFO("Spawned child process '%s' with pid '%i'", argv[0], pid);
		*child_pid_ptr = pid;
		return 0;
	}
}

void print_usage(char* const name, FILE* const file) {
	char *dirc, *bname;

	dirc = strdup(name);
	bname = basename(dirc);

	fprintf(file, "%s (%s)\n", bname, TINI_VERSION_STRING);

#if TINI_MINIMAL
	fprintf(file, "Usage: %s PROGRAM [ARGS] | --version\n\n", bname);
#else
	fprintf(file, "Usage: %s [OPTIONS] PROGRAM -- [ARGS] | --version\n\n", bname);
#endif
	fprintf(file, "Execute a program under the supervision of a valid init process (%s)\n\n", bname);

	fprintf(file, "Command line options:\n\n");

	fprintf(file, "  --version: Show version and exit.\n");

#if TINI_MINIMAL
#else
	fprintf(file, "  -h: Show this help message and exit.\n");
#if HAS_SUBREAPER
	fprintf(file, "  -s: Register as a process subreaper (requires Linux >= 3.4).\n");
#endif
	fprintf(file, "  -p SIGNAL: Trigger SIGNAL when parent dies, e.g. \"-p SIGKILL\".\n");
	fprintf(file, "  -v: Generate more verbose output. Repeat up to 3 times.\n");
	fprintf(file, "  -w: Print a warning when processes are getting reaped.\n");
	fprintf(file, "  -g: Send signals to the child's process group.\n");
	fprintf(file, "  -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0 (can be repeated).\n");
	fprintf(file, "  -l: Show license and exit.\n");
#endif

	fprintf(file, "\n");

	fprintf(file, "Environment variables:\n\n");
#if HAS_SUBREAPER
	fprintf(file, "  %s: Register as a process subreaper (requires Linux >= 3.4).\n", SUBREAPER_ENV_VAR);
#endif
	fprintf(file, "  %s: Set the verbosity level (default: %d).\n", VERBOSITY_ENV_VAR, DEFAULT_VERBOSITY);
	fprintf(file, "  %s: Send signals to the child's process group.\n", KILL_PROCESS_GROUP_GROUP_ENV_VAR);

	fprintf(file, "\n");
	free(dirc);
}

void print_license(FILE* const file) {
    if(LICENSE_len > fwrite(LICENSE, sizeof(char), LICENSE_len, file)) {
        // Don't handle this error for now, since parse_args won't care
        // about the return value. We do need to check it to compile with
        // older glibc, though.
        // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=25509
        // See: http://sourceware.org/bugzilla/show_bug.cgi?id=11959
    }
}

int set_pdeathsig(char* const arg) {
	size_t i;

	for (i = 0; i < ARRAY_LEN(signal_names); i++) {
		if (strcmp(signal_names[i].name, arg) == 0) {
			/* Signals start at value "1" */
			parent_death_signal = signal_names[i].number;
			return 0;
		}
	}

	return 1;
}

int add_expect_status(char* arg) {
	long status = 0;
	char* endptr = NULL;
	status = strtol(arg, &endptr, 10);

	if ((endptr == NULL) || (*endptr != 0)) {
		return 1;
	}

	if ((status < STATUS_MIN) || (status > STATUS_MAX)) {
		return 1;
	}

	INT32_BITFIELD_CHECK_BOUNDS(expect_status, status);
	INT32_BITFIELD_SET(expect_status, status);
	return 0;
}

int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[], int* const parse_fail_exitcode_ptr) {
	char* name = argv[0];

	// We handle --version if it's the *only* argument provided.
	if (argc == 2 && strcmp("--version", argv[1]) == 0) {
		*parse_fail_exitcode_ptr = 0;
		fprintf(stdout, "%s\n", TINI_VERSION_STRING);
		return 1;
	}

#ifndef TINI_MINIMAL
	int c;
	while ((c = getopt(argc, argv, OPT_STRING)) != -1) {
		switch (c) {
			case 'h':
				print_usage(name, stdout);
				*parse_fail_exitcode_ptr = 0;
				return 1;
#if HAS_SUBREAPER
			case 's':
				subreaper++;
				break;
#endif
			case 'p':
				if (set_pdeathsig(optarg)) {
					PRINT_FATAL("Not a valid option for -p: %s", optarg);
					*parse_fail_exitcode_ptr = 1;
					return 1;
				}
				break;

			case 'v':
				verbosity++;
				break;

			case 'w':
				warn_on_reap++;
				break;

			case 'g':
				kill_process_group++;
				break;

			case 'e':
				if (add_expect_status(optarg)) {
					PRINT_FATAL("Not a valid option for -e: %s", optarg);
					*parse_fail_exitcode_ptr = 1;
					return 1;
				}
				break;

			case 'l':
				print_license(stdout);
				*parse_fail_exitcode_ptr = 0;
				return 1;

			case '?':
				print_usage(name, stderr);
				return 1;
			default:
				/* Should never happen */
				return 1;
		}
	}
#endif

	*child_args_ptr_ptr = calloc(argc-optind+1, sizeof(char*));
	if (*child_args_ptr_ptr == NULL) {
		PRINT_FATAL("Failed to allocate memory for child args: '%s'", strerror(errno));
		return 1;
	}

	int i;
	for (i = 0; i < argc - optind; i++) {
		(**child_args_ptr_ptr)[i] = argv[optind+i];
	}
	(**child_args_ptr_ptr)[i] = NULL;

	if (i == 0) {
		/* User forgot to provide args! */
		print_usage(name, stderr);
		return 1;
	}

	return 0;
}

int parse_env(void) {
#if HAS_SUBREAPER
	if (getenv(SUBREAPER_ENV_VAR) != NULL) {
		subreaper++;
	}
#endif

	if (getenv(KILL_PROCESS_GROUP_GROUP_ENV_VAR) != NULL) {
		kill_process_group++;
	}

	char* env_verbosity = getenv(VERBOSITY_ENV_VAR);
	if (env_verbosity != NULL) {
		verbosity = atoi(env_verbosity);
	}

	return 0;
}


#if HAS_SUBREAPER
int register_subreaper (void) {
	if (subreaper > 0) {
		if (prctl(PR_SET_CHILD_SUBREAPER, 1)) {
			if (errno == EINVAL) {
				PRINT_FATAL("PR_SET_CHILD_SUBREAPER is unavailable on this platform. Are you using Linux >= 3.4?")
			} else {
				PRINT_FATAL("Failed to register as child subreaper: %s", strerror(errno))
			}
			return 1;
		} else {
			PRINT_TRACE("Registered as child subreaper");
		}
	}
	return 0;
}
#endif


void reaper_check (void) {
	/* Check that we can properly reap zombies */
#if HAS_SUBREAPER
	int bit = 0;
#endif

	if (getpid() == 1) {
		return;
	}

#if HAS_SUBREAPER
	if (prctl(PR_GET_CHILD_SUBREAPER, &bit)) {
		PRINT_DEBUG("Failed to read child subreaper attribute: %s", strerror(errno));
	} else if (bit == 1) {
		return;
	}
#endif

	PRINT_WARNING(reaper_warning);
}


int configure_signals(sigset_t* const parent_sigset_ptr, const signal_configuration_t* const sigconf_ptr) {
	/* Block all signals that are meant to be collected by the main loop */
	if (sigfillset(parent_sigset_ptr)) {
		PRINT_FATAL("sigfillset failed: '%s'", strerror(errno));
		return 1;
	}

	// These ones shouldn't be collected by the main loop
	uint i;
	int signals_for_tini[] = {SIGFPE, SIGILL, SIGSEGV, SIGBUS, SIGABRT, SIGTRAP, SIGSYS, SIGTTIN, SIGTTOU};
	for (i = 0; i < ARRAY_LEN(signals_for_tini); i++) {
		if (sigdelset(parent_sigset_ptr, signals_for_tini[i])) {
			PRINT_FATAL("sigdelset failed: '%i'", signals_for_tini[i]);
			return 1;
		}
	}

	if (sigprocmask(SIG_SETMASK, parent_sigset_ptr, sigconf_ptr->sigmask_ptr)) {
		PRINT_FATAL("sigprocmask failed: '%s'", strerror(errno));
		return 1;
	}

	// Handle SIGTTIN and SIGTTOU separately. Since Tini makes the child process group
	// the foreground process group, there's a chance Tini can end up not controlling the tty.
	// If TOSTOP is set on the tty, this could block Tini on writing debug messages. We don't
	// want that. Ignore those signals.
	struct sigaction ign_action;
	memset(&ign_action, 0, sizeof ign_action);

	ign_action.sa_handler = SIG_IGN;
	sigemptyset(&ign_action.sa_mask);

	if (sigaction(SIGTTIN, &ign_action, sigconf_ptr->sigttin_action_ptr)) {
		PRINT_FATAL("Failed to ignore SIGTTIN");
		return 1;
	}

	if (sigaction(SIGTTOU, &ign_action, sigconf_ptr->sigttou_action_ptr)) {
		PRINT_FATAL("Failed to ignore SIGTTOU");
		return 1;
	}

	return 0;
}

int wait_and_forward_signal(sigset_t const* const parent_sigset_ptr, pid_t const child_pid) {
	siginfo_t sig;

	if (sigtimedwait(parent_sigset_ptr, &sig, &ts) == -1) {
		switch (errno) {
			case EAGAIN:
				break;
			case EINTR:
				break;
			default:
				PRINT_FATAL("Unexpected error in sigtimedwait: '%s'", strerror(errno));
				return 1;
		}
	} else {
		/* There is a signal to handle here */
		switch (sig.si_signo) {
			case SIGCHLD:
				/* Special-cased, as we don't forward SIGCHLD. Instead, we'll
				 * fallthrough to reaping processes.
				 */
				PRINT_DEBUG("Received SIGCHLD");
				break;
			default:
				PRINT_DEBUG("Passing signal: '%s'", strsignal(sig.si_signo));
				/* Forward anything else */
				if (kill(kill_process_group ? -child_pid : child_pid, sig.si_signo)) {
					if (errno == ESRCH) {
						PRINT_WARNING("Child was dead when forwarding signal");
					} else {
						PRINT_FATAL("Unexpected error when forwarding signal: '%s'", strerror(errno));
						return 1;
					}
				}
				break;
		}
	}

	return 0;
}

int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
	pid_t current_pid;
	int current_status;

	while (1) {
		current_pid = waitpid(-1, &current_status, WNOHANG);

		switch (current_pid) {

			case -1:
				if (errno == ECHILD) {
					PRINT_TRACE("No child to wait");
					break;
				}
				PRINT_FATAL("Error while waiting for pids: '%s'", strerror(errno));
				return 1;

			case 0:
				PRINT_TRACE("No child to reap");
				break;

			default:
				/* A child was reaped. Check whether it's the main one. If it is, then
				 * set the exit_code, which will cause us to exit once we've reaped everyone else.
				 */
				PRINT_DEBUG("Reaped child with pid: '%i'", current_pid);
				if (current_pid == child_pid) {
					if (WIFEXITED(current_status)) {
						/* Our process exited normally. */
						PRINT_INFO("Main child exited normally (with status '%i')", WEXITSTATUS(current_status));
						*child_exitcode_ptr = WEXITSTATUS(current_status);
					} else if (WIFSIGNALED(current_status)) {
						/* Our process was terminated. Emulate what sh / bash
						 * would do, which is to return 128 + signal number.
						 */
						PRINT_INFO("Main child exited with signal (with signal '%s')", strsignal(WTERMSIG(current_status)));
						*child_exitcode_ptr = 128 + WTERMSIG(current_status);
					} else {
						PRINT_FATAL("Main child exited for unknown reason");
						return 1;
					}

					// Be safe, ensure the status code is indeed between 0 and 255.
					*child_exitcode_ptr = *child_exitcode_ptr % (STATUS_MAX - STATUS_MIN + 1);

					// If this exitcode was remapped, then set it to 0.
					INT32_BITFIELD_CHECK_BOUNDS(expect_status, *child_exitcode_ptr);
					if (INT32_BITFIELD_TEST(expect_status, *child_exitcode_ptr)) {
						*child_exitcode_ptr = 0;
					}
				} else if (warn_on_reap > 0) {
					PRINT_WARNING("Reaped zombie process with pid=%i", current_pid);
				}

				// Check if other childs have been reaped.
				continue;
		}

		/* If we make it here, that's because we did not continue in the switch case. */
		break;
	}

	return 0;
}


int main(int argc, char *argv[]) {
	pid_t child_pid;

	// Those are passed to functions to get an exitcode back.
	int child_exitcode = -1;  // This isn't a valid exitcode, and lets us tell whether the child has exited.
	int parse_exitcode = 1;   // By default, we exit with 1 if parsing fails.

	/* Parse command line arguments */
	char* (*child_args_ptr)[];
	int parse_args_ret = parse_args(argc, argv, &child_args_ptr, &parse_exitcode);
	if (parse_args_ret) {
		return parse_exitcode;
	}

	/* Parse environment */
	if (parse_env()) {
		return 1;
	}

	/* Configure signals */
	sigset_t parent_sigset, child_sigset;
	struct sigaction sigttin_action, sigttou_action;
	memset(&sigttin_action, 0, sizeof sigttin_action);
	memset(&sigttou_action, 0, sizeof sigttou_action);

	signal_configuration_t child_sigconf = {
		.sigmask_ptr = &child_sigset,
		.sigttin_action_ptr = &sigttin_action,
		.sigttou_action_ptr = &sigttou_action,
	};

	if (configure_signals(&parent_sigset, &child_sigconf)) {
		return 1;
	}

	/* Trigger signal on this process when the parent process exits. */
	if (parent_death_signal && prctl(PR_SET_PDEATHSIG, parent_death_signal)) {
		PRINT_FATAL("Failed to set up parent death signal");
		return 1;
	 }

#if HAS_SUBREAPER
	/* If available and requested, register as a subreaper */
	if (register_subreaper()) {
		return 1;
	};
#endif

	/* Are we going to reap zombies properly? If not, warn. */
	reaper_check();

	/* Go on */
	int spawn_ret = spawn(&child_sigconf, *child_args_ptr, &child_pid);
	if (spawn_ret) {
		return spawn_ret;
	}
	free(child_args_ptr);

	while (1) {
		/* Wait for one signal, and forward it */
		if (wait_and_forward_signal(&parent_sigset, child_pid)) {
			return 1;
		}

		/* Now, reap zombies */
		if (reap_zombies(child_pid, &child_exitcode)) {
			return 1;
		}

		if (child_exitcode != -1) {
			PRINT_TRACE("Exiting: child has exited");
			return child_exitcode;
		}
	}
}


================================================
FILE: src/tiniConfig.h.in
================================================
#define TINI_VERSION "@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@"
#define TINI_GIT     "@tini_VERSION_GIT@"


================================================
FILE: src/tiniLicense.h
================================================
unsigned char LICENSE[] = {
  0x54, 0x68, 0x65, 0x20, 0x4d, 0x49, 0x54, 0x20, 0x4c, 0x69, 0x63, 0x65,
  0x6e, 0x73, 0x65, 0x20, 0x28, 0x4d, 0x49, 0x54, 0x29, 0x0a, 0x0a, 0x43,
  0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29,
  0x20, 0x32, 0x30, 0x31, 0x35, 0x20, 0x54, 0x68, 0x6f, 0x6d, 0x61, 0x73,
  0x20, 0x4f, 0x72, 0x6f, 0x7a, 0x63, 0x6f, 0x20, 0x3c, 0x74, 0x68, 0x6f,
  0x6d, 0x61, 0x73, 0x40, 0x6f, 0x72, 0x6f, 0x7a, 0x63, 0x6f, 0x2e, 0x66,
  0x72, 0x3e, 0x0a, 0x0a, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69,
  0x6f, 0x6e, 0x20, 0x69, 0x73, 0x20, 0x68, 0x65, 0x72, 0x65, 0x62, 0x79,
  0x20, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x65, 0x64, 0x2c, 0x20, 0x66, 0x72,
  0x65, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x63, 0x68, 0x61, 0x72, 0x67, 0x65,
  0x2c, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x70, 0x65, 0x72,
  0x73, 0x6f, 0x6e, 0x20, 0x6f, 0x62, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e,
  0x67, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x0a, 0x6f, 0x66, 0x20,
  0x74, 0x68, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72,
  0x65, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x61, 0x73, 0x73, 0x6f, 0x63, 0x69,
  0x61, 0x74, 0x65, 0x64, 0x20, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, 0x6e,
  0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x73,
  0x20, 0x28, 0x74, 0x68, 0x65, 0x20, 0x22, 0x53, 0x6f, 0x66, 0x74, 0x77,
  0x61, 0x72, 0x65, 0x22, 0x29, 0x2c, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x65,
  0x61, 0x6c, 0x0a, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6f,
  0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,
  0x75, 0x74, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69, 0x63, 0x74, 0x69,
  0x6f, 0x6e, 0x2c, 0x20, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x69, 0x6e,
  0x67, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x6c, 0x69,
  0x6d, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65,
  0x20, 0x72, 0x69, 0x67, 0x68, 0x74, 0x73, 0x0a, 0x74, 0x6f, 0x20, 0x75,
  0x73, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x2c, 0x20, 0x6d, 0x6f,
  0x64, 0x69, 0x66, 0x79, 0x2c, 0x20, 0x6d, 0x65, 0x72, 0x67, 0x65, 0x2c,
  0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x2c, 0x20, 0x64, 0x69,
  0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x2c, 0x20, 0x73, 0x75,
  0x62, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c, 0x20, 0x61, 0x6e,
  0x64, 0x2f, 0x6f, 0x72, 0x20, 0x73, 0x65, 0x6c, 0x6c, 0x0a, 0x63, 0x6f,
  0x70, 0x69, 0x65, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20,
  0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2c, 0x20, 0x61, 0x6e,
  0x64, 0x20, 0x74, 0x6f, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x20,
  0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x73, 0x20, 0x74, 0x6f, 0x20, 0x77,
  0x68, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74,
  0x77, 0x61, 0x72, 0x65, 0x20, 0x69, 0x73, 0x0a, 0x66, 0x75, 0x72, 0x6e,
  0x69, 0x73, 0x68, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x64, 0x6f, 0x20,
  0x73, 0x6f, 0x2c, 0x20, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20,
  0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x66, 0x6f, 0x6c, 0x6c, 0x6f,
  0x77, 0x69, 0x6e, 0x67, 0x20, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69,
  0x6f, 0x6e, 0x73, 0x3a, 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x61, 0x62,
  0x6f, 0x76, 0x65, 0x20, 0x63, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68,
  0x74, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x20, 0x61, 0x6e, 0x64,
  0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73,
  0x73, 0x69, 0x6f, 0x6e, 0x20, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x20,
  0x73, 0x68, 0x61, 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x69, 0x6e, 0x63,
  0x6c, 0x75, 0x64, 0x65, 0x64, 0x20, 0x69, 0x6e, 0x0a, 0x61, 0x6c, 0x6c,
  0x20, 0x63, 0x6f, 0x70, 0x69, 0x65, 0x73, 0x20, 0x6f, 0x72, 0x20, 0x73,
  0x75, 0x62, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x20, 0x70,
  0x6f, 0x72, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x6f, 0x66, 0x20, 0x74,
  0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x2e,
  0x0a, 0x0a, 0x54, 0x48, 0x45, 0x20, 0x53, 0x4f, 0x46, 0x54, 0x57, 0x41,
  0x52, 0x45, 0x20, 0x49, 0x53, 0x20, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44,
  0x45, 0x44, 0x20, 0x22, 0x41, 0x53, 0x20, 0x49, 0x53, 0x22, 0x2c, 0x20,
  0x57, 0x49, 0x54, 0x48, 0x4f, 0x55, 0x54, 0x20, 0x57, 0x41, 0x52, 0x52,
  0x41, 0x4e, 0x54, 0x59, 0x20, 0x4f, 0x46, 0x20, 0x41, 0x4e, 0x59, 0x20,
  0x4b, 0x49, 0x4e, 0x44, 0x2c, 0x20, 0x45, 0x58, 0x50, 0x52, 0x45, 0x53,
  0x53, 0x20, 0x4f, 0x52, 0x0a, 0x49, 0x4d, 0x50, 0x4c, 0x49, 0x45, 0x44,
  0x2c, 0x20, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x49, 0x4e, 0x47, 0x20,
  0x42, 0x55, 0x54, 0x20, 0x4e, 0x4f, 0x54, 0x20, 0x4c, 0x49, 0x4d, 0x49,
  0x54, 0x45, 0x44, 0x20, 0x54, 0x4f, 0x20, 0x54, 0x48, 0x45, 0x20, 0x57,
  0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x49, 0x45, 0x53, 0x20, 0x4f, 0x46,
  0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41, 0x4e, 0x54, 0x41, 0x42, 0x49,
  0x4c, 0x49, 0x54, 0x59, 0x2c, 0x0a, 0x46, 0x49, 0x54, 0x4e, 0x45, 0x53,
  0x53, 0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54,
  0x49, 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55, 0x52, 0x50, 0x4f,
  0x53, 0x45, 0x20, 0x41, 0x4e, 0x44, 0x20, 0x4e, 0x4f, 0x4e, 0x49, 0x4e,
  0x46, 0x52, 0x49, 0x4e, 0x47, 0x45, 0x4d, 0x45, 0x4e, 0x54, 0x2e, 0x20,
  0x49, 0x4e, 0x20, 0x4e, 0x4f, 0x20, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x20,
  0x53, 0x48, 0x41, 0x4c, 0x4c, 0x20, 0x54, 0x48, 0x45, 0x0a, 0x41, 0x55,
  0x54, 0x48, 0x4f, 0x52, 0x53, 0x20, 0x4f, 0x52, 0x20, 0x43, 0x4f, 0x50,
  0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x48, 0x4f, 0x4c, 0x44, 0x45,
  0x52, 0x53, 0x20, 0x42, 0x45, 0x20, 0x4c, 0x49, 0x41, 0x42, 0x4c, 0x45,
  0x20, 0x46, 0x4f, 0x52, 0x20, 0x41, 0x4e, 0x59, 0x20, 0x43, 0x4c, 0x41,
  0x49, 0x4d, 0x2c, 0x20, 0x44, 0x41, 0x4d, 0x41, 0x47, 0x45, 0x53, 0x20,
  0x4f, 0x52, 0x20, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x0a, 0x4c, 0x49, 0x41,
  0x42, 0x49, 0x4c, 0x49, 0x54, 0x59, 0x2c, 0x20, 0x57, 0x48, 0x45, 0x54,
  0x48, 0x45, 0x52, 0x20, 0x49, 0x4e, 0x20, 0x41, 0x4e, 0x20, 0x41, 0x43,
  0x54, 0x49, 0x4f, 0x4e, 0x20, 0x4f, 0x46, 0x20, 0x43, 0x4f, 0x4e, 0x54,
  0x52, 0x41, 0x43, 0x54, 0x2c, 0x20, 0x54, 0x4f, 0x52, 0x54, 0x20, 0x4f,
  0x52, 0x20, 0x4f, 0x54, 0x48, 0x45, 0x52, 0x57, 0x49, 0x53, 0x45, 0x2c,
  0x20, 0x41, 0x52, 0x49, 0x53, 0x49, 0x4e, 0x47, 0x20, 0x46, 0x52, 0x4f,
  0x4d, 0x2c, 0x0a, 0x4f, 0x55, 0x54, 0x20, 0x4f, 0x46, 0x20, 0x4f, 0x52,
  0x20, 0x49, 0x4e, 0x20, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x49,
  0x4f, 0x4e, 0x20, 0x57, 0x49, 0x54, 0x48, 0x20, 0x54, 0x48, 0x45, 0x20,
  0x53, 0x4f, 0x46, 0x54, 0x57, 0x41, 0x52, 0x45, 0x20, 0x4f, 0x52, 0x20,
  0x54, 0x48, 0x45, 0x20, 0x55, 0x53, 0x45, 0x20, 0x4f, 0x52, 0x20, 0x4f,
  0x54, 0x48, 0x45, 0x52, 0x20, 0x44, 0x45, 0x41, 0x4c, 0x49, 0x4e, 0x47,
  0x53, 0x20, 0x49, 0x4e, 0x0a, 0x54, 0x48, 0x45, 0x20, 0x53, 0x4f, 0x46,
  0x54, 0x57, 0x41, 0x52, 0x45, 0x2e, 0x0a
};
unsigned int LICENSE_len = 1099;


================================================
FILE: test/pdeathsignal/stage_1.py
================================================
#!/usr/bin/env python
from __future__ import print_function

import os
import sys
import subprocess


def main():
    pid = os.getpid()

    tini = sys.argv[1]
    ret = sys.argv[2]
    stage_2 = os.path.join(os.path.dirname(__file__), "stage_2.py")

    cmd = [
        tini,
        "-vvv",
        "-p",
        "SIGUSR1",
        "--",
        stage_2,
        str(pid),
        ret
    ]

    subprocess.Popen(cmd).wait()

if __name__ == "__main__":
    main()


================================================
FILE: test/pdeathsignal/stage_2.py
================================================
#!/usr/bin/env python
from __future__ import print_function

import os
import sys
import signal
import time


def main():
    ret = sys.argv[2]

    def handler(*args):
        with open(ret, "w") as f:
            f.write("ok")
        sys.exit(0)

    signal.signal(signal.SIGUSR1, handler)
    pid = int(sys.argv[1])

    os.kill(pid, signal.SIGKILL)
    time.sleep(5)

if __name__ == "__main__":
    main()


================================================
FILE: test/pgroup/stage_1.py
================================================
#!/usr/bin/env python
import os
import subprocess
import signal


def reset_sig_handler():
    signal.signal(signal.SIGUSR1, signal.SIG_DFL)


if __name__ == "__main__":
    signal.signal(signal.SIGUSR1, signal.SIG_IGN)
    p = subprocess.Popen(
            ["sleep", "1000"],
            preexec_fn=reset_sig_handler
        )
    p.wait()



================================================
FILE: test/reaping/stage_1.py
================================================
#!/usr/bin/env python3
import os
import sys
import subprocess
import time

import psutil


def in_group_or_reaped(pid):
    try:
        return os.getpgid(pid) == os.getpgid(0)
    except OSError:
        return True


def main():
    stage_2 = os.path.join(os.path.dirname(__file__), "stage_2.py")
    subprocess.Popen([stage_2]).wait()

    # In tests, we assume this process is the direct child of init
    this_process = psutil.Process(os.getpid())
    init_process = this_process.parent()

    print("Reaping test: stage_1 is pid{0}, init is pid{1}".format(
        this_process.pid, init_process.pid))

    # The only child PID that should persist is this one.
    expected_pids = [this_process.pid]

    print("Expecting pids to remain: {0}".format(
        ", ".join(str(pid) for pid in expected_pids)))

    while 1:
        pids = [p.pid for p in init_process.children(recursive=True)]
        print("Has pids: {0}".format(", ".join(str(pid) for pid in pids)))
        for pid in pids:
            assert in_group_or_reaped(pid), "Child had unexpected pgid"
        if set(pids) == set(expected_pids):
            break
        time.sleep(1)

    # Now, check if there are any zombies. For each of the potential zombies,
    # we check that the pgid is ours.  NOTE: We explicitly test that this test
    # fails if subreaping is disabled, so we can be confident this doesn't turn
    # a failure into a success.
    for process in psutil.process_iter():
        if process.pid == this_process.pid:
            continue
        if not in_group_or_reaped(process.pid):
            continue
        print("Not reaped: pid {0}: {1}".format(process.pid, process.name()))
        sys.exit(1)

    sys.exit(0)


if __name__ == "__main__":
    main()


================================================
FILE: test/reaping/stage_2.py
================================================
#!/usr/bin/env python
from __future__ import print_function
import subprocess
import os
import random


if __name__ == "__main__":
    # Spawn lots of process
    for i in range(0, 100):
        cmd = ["sleep", str(1 + i % 2 + random.random())]
        proc = subprocess.Popen(cmd)


================================================
FILE: test/run_inner_tests.py
================================================
#!/usr/bin/env python3
# coding:utf-8
import os
import sys
import signal
import subprocess
import time
import psutil
import bitmap
import re
import itertools
import tempfile

DEVNULL = open(os.devnull, "wb")

SIGNUM_TO_SIGNAME = dict(
    (v, k) for k, v in signal.__dict__.items() if re.match("^SIG[A-Z]+$", k)
)


def busy_wait(condition_callable, timeout):
    checks = 100
    increment = float(timeout) / checks

    for _ in range(checks):
        if condition_callable():
            return
        time.sleep(increment)

    assert False, "Condition was never met"


def main():
    src = os.environ["SOURCE_DIR"]
    build = os.environ["BUILD_DIR"]

    args_disabled = os.environ.get("MINIMAL")

    proxy = os.path.join(src, "test", "subreaper-proxy.py")
    tini = os.path.join(build, "tini")

    subreaper_support = bool(int(os.environ["FORCE_SUBREAPER"]))

    # Run the exit code test. We use POSIXLY_CORRECT here to not need --
    # until that's the default in Tini anyways.
    if not args_disabled:
        print("Running exit code test for {0}".format(tini))
        for code in range(0, 256):
            p = subprocess.Popen(
                [tini, "-e", str(code), "--", "sh", "-c", "exit {0}".format(code)],
                stdout=DEVNULL,
                stderr=DEVNULL,
                universal_newlines=True,
            )
            ret = p.wait()
            assert ret == 0, "Inclusive exit code test failed for %s, exit: %s" % (
                code,
                ret,
            )

            other_codes = [x for x in range(0, 256) if x != code]
            args = list(itertools.chain(*[["-e", str(x)] for x in other_codes]))

            p = subprocess.Popen(
                [tini] + args + ["sh", "-c", "exit {0}".format(code)],
                env=dict(os.environ, POSIXLY_CORRECT="1"),
                stdout=DEVNULL,
                stderr=DEVNULL,
                universal_newlines=True,
            )
            ret = p.wait()
            assert ret == code, "Exclusive exit code test failed for %s, exit: %s" % (
                code,
                ret,
            )

    tests = [([proxy, tini], {})]

    if subreaper_support:
        if not args_disabled:
            tests.append(([tini, "-s"], {}))
        tests.append(([tini], {"TINI_SUBREAPER": ""}))

    for target, env in tests:
        # Run the reaping test
        print("Running reaping test ({0} with env {1})".format(" ".join(target), env))
        p = subprocess.Popen(
            target + [os.path.join(src, "test", "reaping", "stage_1.py")],
            env=dict(os.environ, **env),
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
        )

        out, err = p.communicate()

        if subreaper_support:
            # If subreaper support sin't available, Tini won't looku p its subreaper bit
            # and will output the error message here.
            assert "zombie reaping won't work" not in err, "Warning message was output!"
        ret = p.wait()
        assert (
            "Reaped zombie process with pid=" not in err
        ), "Warning message was output!"
        assert ret == 0, "Reaping test failed!\nOUT: %s\nERR: %s" % (out, err)

        if not args_disabled:
            print(
                "Running reaping display test ({0} with env {1})".format(
                    " ".join(target), env
                )
            )
            p = subprocess.Popen(
                target + ["-w", os.path.join(src, "test", "reaping", "stage_1.py")],
                env=dict(os.environ, **env),
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
            )

            out, err = p.communicate()
            ret = p.wait()
            assert (
                "Reaped zombie process with pid=" in err
            ), "Warning message was output!"
            assert ret == 0, "Reaping display test failed!\nOUT: %s\nERR: %s" % (
                out,
                err,
            )

        # Run the signals test
        for signum in [signal.SIGTERM, signal.SIGUSR1, signal.SIGUSR2]:
            print(
                "running signal test for: {0} ({1} with env {2})".format(
                    signum, " ".join(target), env
                )
            )
            p = subprocess.Popen(
                target + [os.path.join(src, "test", "signals", "test.py")],
                env=dict(os.environ, **env),
                universal_newlines=True,
            )
            busy_wait(
                lambda: len(psutil.Process(p.pid).children(recursive=True)) > 1, 10
            )
            p.send_signal(signum)
            ret = p.wait()
            assert (
                ret == 128 + signum
            ), "Signals test failed (ret was {0}, expected {1})".format(
                ret, 128 + signum
            )

    # Run the process group test
    # This test has Tini spawn a process that ignores SIGUSR1 and spawns a child that doesn't (and waits on the child)
    # We send SIGUSR1 to Tini, and expect the grand-child to terminate, then the child, and then Tini.
    if not args_disabled:
        print("Running process group test (arguments)")
        p = subprocess.Popen(
            [tini, "-g", os.path.join(src, "test", "pgroup", "stage_1.py")],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
        )

        busy_wait(lambda: len(psutil.Process(p.pid).children(recursive=True)) == 2, 10)
        p.send_signal(signal.SIGUSR1)
        busy_wait(lambda: p.poll() is not None, 10)

    print("Running process group test (environment variable)")
    p = subprocess.Popen(
        [tini, os.path.join(src, "test", "pgroup", "stage_1.py")],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        env=dict(os.environ, TINI_KILL_PROCESS_GROUP="1"),
        universal_newlines=True,
    )

    busy_wait(lambda: len(psutil.Process(p.pid).children(recursive=True)) == 2, 10)
    p.send_signal(signal.SIGUSR1)
    busy_wait(lambda: p.poll() is not None, 10)

    # Run failing test. Force verbosity to 1 so we see the subreaper warning
    # regardless of whether MINIMAL is set.
    print("Running zombie reaping failure test (Tini should warn)")
    p = subprocess.Popen(
        [tini, os.path.join(src, "test", "reaping", "stage_1.py")],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        env=dict(os.environ, TINI_VERBOSITY="1"),
        universal_newlines=True,
    )
    out, err = p.communicate()
    assert "zombie reaping won't work" in err, "No warning message was output!"
    ret = p.wait()
    assert ret == 1, "Reaping test succeeded (it should have failed)!"

    # Test that the signals are properly in place here.
    print("Running signal configuration test")

    p = subprocess.Popen(
        [os.path.join(build, "sigconf-test"), tini, "cat", "/proc/self/status"],
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        universal_newlines=True,
    )
    out, err = p.communicate()

    # Extract the signal properties, and add a zero at the end.
    props = [line.split(":") for line in out.splitlines()]
    props = [(k.strip(), v.strip()) for (k, v) in props]
    props = [
        (k, bitmap.BitMap.fromstring(bin(int(v, 16))[2:].zfill(32)))
        for (k, v) in props
        if k in ["SigBlk", "SigIgn", "SigCgt"]
    ]
    props = dict(props)

    # Print actual handling configuration
    for k, bmp in props.items():
        print(
            "{0}: {1}".format(
                k,
                ", ".join(
                    [
                        "{0} ({1})".format(SIGNUM_TO_SIGNAME[n + 1], n + 1)
                        for n in bmp.nonzero()
                    ]
                ),
            )
        )

    for signal_set_name, signals_to_test_for in [
        ("SigIgn", [signal.SIGTTOU, signal.SIGSEGV, signal.SIGINT]),
        ("SigBlk", [signal.SIGTTIN, signal.SIGILL, signal.SIGTERM]),
    ]:
        for signum in signals_to_test_for:
            # Use signum - 1 because the bitmap is 0-indexed but represents signals strting at 1
            assert (signum - 1) in props[
                signal_set_name
            ].nonzero(), "{0} ({1}) is missing in {2}!".format(
                SIGNUM_TO_SIGNAME[signum], signum, signal_set_name
            )

    # Test parent death signal handling.
    if not args_disabled:
        print("Running parent death signal test")
        f = tempfile.NamedTemporaryFile()
        try:
            p = subprocess.Popen(
                [os.path.join(src, "test", "pdeathsignal", "stage_1.py"), tini, f.name],
                stdout=DEVNULL,
                stderr=DEVNULL,
                universal_newlines=True,
            )
            p.wait()

            busy_wait(lambda: open(f.name).read() == "ok", 10)
        finally:
            f.close()

    print("---------------------------")
    print("All done, tests as expected")
    print("---------------------------")


if __name__ == "__main__":
    main()


================================================
FILE: test/run_outer_tests.py
================================================
#coding:utf-8
import os
import sys
import time
import pipes
import subprocess
import threading
import pexpect
import signal


class ReturnContainer():
    def __init__(self):
        self.value = None


class Command(object):
    def __init__(self, cmd, fail_cmd, post_cmd=None, post_delay=0):
        self.cmd = cmd
        self.fail_cmd = fail_cmd
        self.post_cmd = post_cmd
        self.post_delay = post_delay
        self.proc = None

    def run(self, timeout=None, retcode=0):
        print "Testing '{0}'...".format(" ".join(pipes.quote(s) for s in self.cmd)),
        sys.stdout.flush()

        err = None
        pipe_kwargs = {"stdout": subprocess.PIPE, "stderr": subprocess.PIPE, "stdin": subprocess.PIPE}

        def target():
            self.proc = subprocess.Popen(self.cmd, **pipe_kwargs)
            self.stdout, self.stderr = self.proc.communicate()

        thread = threading.Thread(target=target)
        thread.daemon = True

        thread.start()

        if self.post_cmd is not None:
            time.sleep(self.post_delay)
            subprocess.check_call(self.post_cmd, **pipe_kwargs)

        thread.join(timeout - self.post_delay if timeout is not None else timeout)

        # Checks
        if thread.is_alive():
            subprocess.check_call(self.fail_cmd, **pipe_kwargs)
            err = Exception("Test failed with timeout!")

        elif self.proc.returncode != retcode:
            err = Exception("Test failed with unexpected returncode (expected {0}, got {1})".format(retcode, self.proc.returncode))

        if err is not None:
            print "FAIL"
            print "--- STDOUT ---"
            print getattr(self, "stdout", "no stdout")
            print "--- STDERR ---"
            print getattr(self, "stderr", "no stderr")
            print "--- ... ---"
            raise err
        else:
            print "OK"


def attach_and_type_exit_0(name):
    print "Attaching to {0} to exit 0".format(name)
    p = pexpect.spawn("docker attach {0}".format(name))
    p.sendline('')
    p.sendline('exit 0')
    p.close()


def attach_and_issue_ctrl_c(name):
    print "Attaching to {0} to CTRL+C".format(name)
    p = pexpect.spawn("docker attach {0}".format(name))
    p.expect_exact('#')
    p.sendintr()
    p.close()


def test_tty_handling(img, name, base_cmd, fail_cmd, container_command, exit_function, expect_exit_code):
    print "Testing TTY handling (using container command '{0}' and exit function '{1}')".format(container_command, exit_function.__name__)
    rc = ReturnContainer()

    shell_ready_event = threading.Event()

    def spawn():
        cmd = base_cmd + ["--tty", "--interactive", img, "/tini/dist/tini"]
        if os.environ.get("MINIMAL") is None:
            cmd.append("--")
        cmd.append(container_command)
        p = pexpect.spawn(" ".join(cmd))
        p.expect_exact("#")
        shell_ready_event.set()
        rc.value = p.wait()

    thread = threading.Thread(target=spawn)
    thread.daemon = True

    thread.start()

    if not shell_ready_event.wait(2):
        raise Exception("Timeout waiting for shell to spawn")

    exit_function(name)

    thread.join(timeout=2)

    if thread.is_alive():
        subprocess.check_call(fail_cmd)
        raise Exception("Timeout waiting for container to exit!")

    if rc.value != expect_exit_code:
        raise Exception("Return code is: {0} (expected {1})".format(rc.value, expect_exit_code))



def main():
    img = sys.argv[1]
    name = "{0}-test".format(img)
    args_disabled = os.environ.get("MINIMAL")

    root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))

    base_cmd = [
        "docker",
        "run",
        "--rm",
        "--volume={0}:/tini".format(root),
        "--name={0}".format(name),
    ]

    fail_cmd = ["docker", "kill", "-s", "KILL", name]

    # Funtional tests
    for entrypoint in ["/tini/dist/tini", "/tini/dist/tini-static"]:
        functional_base_cmd = base_cmd + [
            "--entrypoint={0}".format(entrypoint),
            "-e", "TINI_VERBOSITY=3",
            img,
        ]

        # Reaping test
        Command(functional_base_cmd + ["/tini/test/reaping/stage_1.py"], fail_cmd).run(timeout=10)

        # Signals test
        for sig, retcode in [("TERM", 143), ("USR1", 138), ("USR2", 140)]:
            Command(
                functional_base_cmd + ["/tini/test/signals/test.py"],
                fail_cmd,
                ["docker", "kill", "-s", sig, name],
                2
            ).run(timeout=10, retcode=retcode)

        # Exit code test
        Command(functional_base_cmd + ["-z"], fail_cmd).run(retcode=127 if args_disabled else 1)
        Command(functional_base_cmd + ["-h"], fail_cmd).run(retcode=127 if args_disabled else 0)
        Command(functional_base_cmd + ["zzzz"], fail_cmd).run(retcode=127)
        Command(functional_base_cmd + ["-w"], fail_cmd).run(retcode=127 if args_disabled else 0)

    # Valgrind test (we only run this on the dynamic version, because otherwise Valgrind may bring up plenty of errors that are
    # actually from libc)
    Command(base_cmd + [img, "valgrind", "--leak-check=full", "--error-exitcode=1", "/tini/dist/tini", "ls"], fail_cmd).run()

    # Test tty handling
    test_tty_handling(img, name, base_cmd, fail_cmd, "dash", attach_and_type_exit_0, 0)
    test_tty_handling(img, name, base_cmd, fail_cmd, "dash -c 'while true; do echo \#; sleep 0.1; done'", attach_and_issue_ctrl_c, 128 + signal.SIGINT)

    # Installation tests (sh -c is used for globbing and &&)
    for image, pkg_manager, extension in [
            ["ubuntu:precise", "dpkg", "deb"],
            ["ubuntu:trusty", "dpkg", "deb"],
            ["centos:6", "rpm", "rpm"],
            ["centos:7", "rpm", "rpm"],
    ]:
        Command(base_cmd + [image, "sh", "-c", "{0} -i /tini/dist/*.{1} && /usr/bin/tini true".format(pkg_manager, extension)], fail_cmd).run()


if __name__ == "__main__":
    main()


================================================
FILE: test/sigconf/sigconf-test.c
================================================
/*
Test program to:
+ Ignore a few signals
+ Block a few signals
+ Exec whatever the test runner asked for
*/

#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


int main(int argc, char *argv[]) {
	// Signals to ignore
	signal(SIGTTOU, SIG_IGN);  // This one should still be in SigIgn (Tini touches it to ignore it, and should restore it)
	signal(SIGSEGV, SIG_IGN);  // This one should still be in SigIgn (Tini shouldn't touch it)
	signal(SIGINT,  SIG_IGN);  // This one should still be in SigIgn (Tini should block it to forward it, and restore it)

	// Signals to block
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set, SIGTTIN);  // This one should still be in SigIgn (Tini touches it to ignore it, and should restore it)
	sigaddset(&set, SIGILL);   // This one should still be in SigIgn (Tini shouldn't touch it)
	sigaddset(&set, SIGTERM);  // This one should still be in SigIgn (Tini should block it to forward it, and restore it)
	sigprocmask(SIG_BLOCK, &set, NULL);

	// Run whatever we were asked to run
	execvp(argv[1], argv+1);
}


================================================
FILE: test/signals/test.py
================================================
#!/usr/bin/env python
import signal
import os


def main():
    signal.signal(signal.SIGTERM, signal.SIG_DFL)
    signal.signal(signal.SIGUSR1, signal.SIG_DFL)
    signal.signal(signal.SIGUSR2, signal.SIG_DFL)
    os.system("sleep 100")

if __name__ == "__main__":
    main()


================================================
FILE: test/subreaper-proxy.py
================================================
#!/usr/bin/env python3
#coding:utf-8
import os
import sys

import prctl


def main():
    args = sys.argv[1:]

    print("subreaper-proxy: running '%s'" % (" ".join(args)))

    prctl.set_child_subreaper(1)
    os.execv(args[0], args)


if __name__ == '__main__':
    main()


================================================
FILE: tpl/README.md.in
================================================
<!--

#####################################
# THIS FILE IS AUTOGENERATED!       #
# Edit ./tpl/README.md.in instead   #
#####################################

-->


Tini - A tiny but valid `init` for containers
=============================================

[![Build Status](https://travis-ci.org/krallin/tini.svg?branch=master)](https://travis-ci.org/krallin/tini)

Tini is the simplest `init` you could think of.

All Tini does is spawn a single child (Tini is meant to be run in a container),
and wait for it to exit all the while reaping zombies and performing
signal forwarding.


Why Tini?
---------

Using Tini has several benefits:

- It protects you from software that accidentally creates zombie processes,
  which can (over time!) starve your entire system for PIDs (and make it
  unusable).
- It ensures that the *default signal handlers* work for the software you run
  in your Docker image. For example, with Tini, `SIGTERM` properly terminates
  your process even if you didn't explicitly install a signal handler for it.
- It does so completely transparently! Docker images that work without Tini
  will work with Tini without any changes.

If you'd like more detail on why this is useful, review this issue discussion:
[What is advantage of Tini?][0].


Using Tini
----------

*NOTE: If you are using Docker 1.13 or greater, Tini is included in Docker
itself. This includes all versions of Docker CE. To enable Tini, just [pass the
`--init` flag to `docker run`][5].*

*NOTE: There are [pre-built Docker images available for Tini][10]. If
you're currently using an Ubuntu or CentOS image as your base, you can use
one of those as a drop-in replacement.*

*NOTE: There are Tini packages for Alpine Linux and NixOS. See below for
installation instructions.*

Add Tini to your container, and make it executable. Then, just invoke Tini
and pass your program and its arguments as arguments to Tini.

In Docker, you will want to use an entrypoint so you don't have to remember
to manually invoke Tini:

    # Add Tini
    ENV TINI_VERSION=v@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
    RUN chmod +x /tini
    ENTRYPOINT ["/tini", "--"]

    # Run your program under Tini
    CMD ["/your/program", "-and", "-its", "arguments"]
    # or docker run your-image /your/program ...

Note that you *can* skip the `--` under certain conditions, but you might
as well always include it to be safe. If you see an error message that
looks like `tini: invalid option -- 'c'`, then you *need* to add the `--`.

Arguments for Tini itself should be passed like `-v` in the following example:
`/tini -v -- /your/program`.

*NOTE: The binary linked above is a 64-bit dynamically-linked binary.*


### Signed binaries ###

The `tini` and `tini-static` binaries are signed using the key `595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7`.

You can verify their signatures using `gpg` (which you may install using
your package manager):

    ENV TINI_VERSION v@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini.asc /tini.asc
    RUN gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 595E85A6B1B4779EA4DAAEC70B588DFF0527A9B7 \
     && gpg --batch --verify /tini.asc /tini
    RUN chmod +x /tini


### Verifying binaries via checksum ###

The `tini` and `tini-static` binaries have generated checksums (`SHA1` and `SHA256`).

You can verify their checksums using `sha1sum` and `sha256sum` (which you may install using
your package manager):

    ENV TINI_VERSION v@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@
    RUN wget --no-check-certificate --no-cookies --quiet https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-amd64 \
        && wget --no-check-certificate --no-cookies --quiet https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-amd64.sha256sum \
        && echo "$(cat tini-amd64.sha256sum)" | sha256sum -c


### Alpine Linux Package ###

On Alpine Linux, you can use the following command to install Tini:

    RUN apk add --no-cache tini
    # Tini is now available at /sbin/tini
    ENTRYPOINT ["/sbin/tini", "--"]


### NixOS ###

Using Nix, you can use the following command to install Tini:

    nix-env --install tini


### Debian ###

On Debian (Buster or newer), you can use the following command to install Tini:

    apt-get install tini

Note that this installs `/usr/bin/tini` (and `/usr/bin/tini-static`), not `/tini`.


### Arch Linux ###

On Arch Linux, there is a package available on the [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository).
Install using the [official instructions](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages)
or use an [AUR helper](https://wiki.archlinux.org/index.php/AUR_helpers):

    pacaur -S tini


### Other Platforms ###

ARM and 32-bit binaries are available! You can find the complete list of
available binaries under [the releases tab][11].


Options
-------

### Verbosity ###

The `-v` argument can be used for extra verbose output (you can pass it up to
3 times, e.g. `-vvv`).


### Subreaping ###

By default, Tini needs to run as PID 1 so that it can reap zombies (by
running as PID 1, zombies get re-parented to Tini).

If for some reason, you cannot run Tini as PID 1, you should register Tini as
a process subreaper instead (only in Linux >= 3.4), by either:

  + Passing the `-s` argument to Tini (`tini -s -- ...`)
  + Setting the environment variable `TINI_SUBREAPER`
    (e.g. `export TINI_SUBREAPER=`).

This will ensure that zombies get re-parented to Tini despite Tini not running
as PID 1.

*NOTE: Tini will issue a warning if it detects that it isn't running as PID 1
and isn't registered as a subreaper. If you don't see a warning, you're fine.*


### Remapping exit codes ###

Tini will reuse the child's exit code when exiting, but occasionally, this may
not be exactly what you want (e.g. if your child exits with 143 after receiving
SIGTERM). Notably, this can be an issue with Java apps.

In this case, you can use the `-e` flag to remap an arbitrary exit code to 0.
You can pass the flag multiple times if needed.

For example:

```
tini -e 143 -- ...
```


### Process group killing ###

By default, Tini only kills its immediate child process.  This can be
inconvenient if sending a signal to that process does not have the desired
effect.  For example, if you do

    docker run --rm krallin/ubuntu-tini sh -c 'sleep 10'

and ctrl-C it, nothing happens: SIGINT is sent to the 'sh' process,
but that shell won't react to it while it is waiting for the 'sleep'
to finish.

You can configure Tini to kill the child process group, so that every process
in the group gets the signal, by either:

  + Passing the `-g` argument to Tini (`tini -g -- ...`)
  + Setting the environment variable `TINI_KILL_PROCESS_GROUP`
    (e.g. `export TINI_KILL_PROCESS_GROUP=`).

This corresponds more closely to what happens when you do ctrl-C etc. in a
terminal: The signal is sent to the foreground process group.

    docker run --rm --entrypoint tini krallin/ubuntu-tini -g -- sh -c 'sleep 10'

### Parent Death Signal ###

Tini can set its parent death signal, which is the signal Tini should receive
when *its* parent exits. To set the parent death signal, use the `-p` flag with
the name of the signal Tini should receive when its parent exits:

```
tini -p SIGTERM -- ...
```

*NOTE: See [this PR discussion][12] to learn more about the parent death signal
and use cases.*


More
----

### Existing Entrypoint ###

Tini can also be used with an existing entrypoint in your container!

Assuming your entrypoint was `/docker-entrypoint.sh`, then you would use:

    ENTRYPOINT ["/tini", "--", "/docker-entrypoint.sh"]


### Statically-Linked Version ###

Tini has very few dependencies (it only depends on libc), but if your
container fails to start, you might want to consider using the statically-built
version instead:

    ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini


### Size Considerations ###

Tini is a very small file (in the 10KB range), so it doesn't add much weight
to your container.

The statically-linked version is bigger, but still < 1M.


Building Tini
-------------

If you'd rather not download the binary, you can build Tini by running
`cmake . && make`.

Before building, you probably also want to run:

    export CFLAGS="-DPR_SET_CHILD_SUBREAPER=36 -DPR_GET_CHILD_SUBREAPER=37"

This ensure that even if you're building on a system that has old Linux Kernel
headers (< 3.4), Tini will be built with child subreaper support. This is
usually what you want if you're going to use Tini with Docker (if your host
Kernel supports Docker, it should also support child subreapers).


Understanding Tini
------------------

After spawning your process, Tini will wait for signals and forward those
to the child process, and periodically reap zombie processes that may be
created within your container.

When the "first" child process exits (`/your/program` in the examples above),
Tini exits as well, with the exit code of the child process (so you can
check your container's exit code to know whether the child exited
successfully).


Debugging
---------

If something isn't working just like you expect, consider increasing the
verbosity level (up to 3):

    tini -v    -- bash -c 'exit 1'
    tini -vv   -- true
    tini -vvv  -- pwd


Authors
=======

Maintainer:

  + [Thomas Orozco][20]

Contributors:

  + [Tianon Gravi][30]
  + [David Wragg][31]
  + [Michael Crosby][32]
  + [Wyatt Preul][33]
  + [Patrick Steinhardt][34]

Special thanks to:

  + [Danilo Bürger][40] for packaging Tini for Alpine
  + [Asko Soukka][41] for packaging Tini for Nix
  + [nfnty][42] for packaging Tini for Arch Linux


  [0]: https://github.com/krallin/tini/issues/8
  [5]: https://docs.docker.com/engine/reference/commandline/run/
  [10]: https://github.com/krallin/tini-images
  [11]: https://github.com/krallin/tini/releases
  [12]: https://github.com/krallin/tini/pull/114
  [20]: https://github.com/krallin/
  [30]: https://github.com/tianon
  [31]: https://github.com/dpw
  [32]: https://github.com/crosbymichael
  [33]: https://github.com/geek
  [34]: https://github.com/pks-t
  [40]: https://github.com/danilobuerger
  [41]: https://github.com/datakurre
  [42]: https://github.com/nfnty/pkgbuilds/tree/master/tini/tini


================================================
FILE: tpl/VERSION.in
================================================
@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@
Download .txt
gitextract_u8tw2nf4/

├── .dockerignore
├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── README.md
├── ci/
│   ├── install_deps.sh
│   ├── run_build.sh
│   └── util/
│       └── rpmbuild
├── ddist.sh
├── dtest.sh
├── run_tests.sh
├── sign.key.enc
├── src/
│   ├── tini.c
│   ├── tiniConfig.h.in
│   └── tiniLicense.h
├── test/
│   ├── pdeathsignal/
│   │   ├── stage_1.py
│   │   └── stage_2.py
│   ├── pgroup/
│   │   └── stage_1.py
│   ├── reaping/
│   │   ├── stage_1.py
│   │   └── stage_2.py
│   ├── run_inner_tests.py
│   ├── run_outer_tests.py
│   ├── sigconf/
│   │   └── sigconf-test.c
│   ├── signals/
│   │   └── test.py
│   └── subreaper-proxy.py
└── tpl/
    ├── README.md.in
    └── VERSION.in
Download .txt
SYMBOL INDEX (36 symbols across 10 files)

FILE: src/tini.c
  type signal_configuration_t (line 48) | typedef struct {
  type timespec (line 115) | struct timespec
  function restore_signals (line 132) | int restore_signals(const signal_configuration_t* const sigconf_ptr) {
  function isolate_child (line 151) | int isolate_child(void) {
  function spawn (line 182) | int spawn(const signal_configuration_t* const sigconf_ptr, char* const a...
  function print_usage (line 227) | void print_usage(char* const name, FILE* const file) {
  function print_license (line 273) | void print_license(FILE* const file) {
  function set_pdeathsig (line 283) | int set_pdeathsig(char* const arg) {
  function add_expect_status (line 297) | int add_expect_status(char* arg) {
  function parse_args (line 315) | int parse_args(const int argc, char* const argv[], char* (**child_args_p...
  function parse_env (line 402) | int parse_env(void) {
  function register_subreaper (line 423) | int register_subreaper (void) {
  function reaper_check (line 441) | void reaper_check (void) {
  function configure_signals (line 463) | int configure_signals(sigset_t* const parent_sigset_ptr, const signal_co...
  function wait_and_forward_signal (line 508) | int wait_and_forward_signal(sigset_t const* const parent_sigset_ptr, pid...
  function reap_zombies (line 548) | int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
  function main (line 614) | int main(int argc, char *argv[]) {

FILE: test/pdeathsignal/stage_1.py
  function main (line 9) | def main():

FILE: test/pdeathsignal/stage_2.py
  function main (line 10) | def main():

FILE: test/pgroup/stage_1.py
  function reset_sig_handler (line 7) | def reset_sig_handler():

FILE: test/reaping/stage_1.py
  function in_group_or_reaped (line 10) | def in_group_or_reaped(pid):
  function main (line 17) | def main():

FILE: test/run_inner_tests.py
  function busy_wait (line 21) | def busy_wait(condition_callable, timeout):
  function main (line 33) | def main():

FILE: test/run_outer_tests.py
  class ReturnContainer (line 12) | class ReturnContainer():
    method __init__ (line 13) | def __init__(self):
  class Command (line 17) | class Command(object):
    method __init__ (line 18) | def __init__(self, cmd, fail_cmd, post_cmd=None, post_delay=0):
    method run (line 25) | def run(self, timeout=None, retcode=0):
  function attach_and_type_exit_0 (line 67) | def attach_and_type_exit_0(name):
  function attach_and_issue_ctrl_c (line 75) | def attach_and_issue_ctrl_c(name):
  function test_tty_handling (line 83) | def test_tty_handling(img, name, base_cmd, fail_cmd, container_command, ...
  function main (line 120) | def main():

FILE: test/sigconf/sigconf-test.c
  function main (line 13) | int main(int argc, char *argv[]) {

FILE: test/signals/test.py
  function main (line 6) | def main():

FILE: test/subreaper-proxy.py
  function main (line 9) | def main():
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (91K chars).
[
  {
    "path": ".dockerignore",
    "chars": 7,
    "preview": "./dist\n"
  },
  {
    "path": ".gitignore",
    "chars": 31,
    "preview": "dist\nsign.key\n.env\n.mypy_cache\n"
  },
  {
    "path": ".travis.yml",
    "chars": 1830,
    "preview": "sudo: required\nservices:\n  - docker\n\nlanguage: generic\n\nenv:\n  matrix:\n    - ARCH_SUFFIX= CC=gcc ARCH_NATIVE=1 MINIMAL=\n"
  },
  {
    "path": "CMakeLists.txt",
    "chars": 3435,
    "preview": "cmake_minimum_required (VERSION 2.8.12...3.10)\nproject (tini C)\n\n# Config\nset (tini_VERSION_MAJOR 0)\nset (tini_VERSION_M"
  },
  {
    "path": "Dockerfile",
    "chars": 404,
    "preview": "FROM ubuntu:xenial\n\nARG ARCH_SUFFIX\n\nCOPY ci/install_deps.sh /install_deps.sh\nRUN /install_deps.sh\n\n# Pre-install those "
  },
  {
    "path": "LICENSE",
    "chars": 1099,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2015 Thomas Orozco <thomas@orozco.fr>\n\nPermission is hereby granted, free of charge"
  },
  {
    "path": "README.md",
    "chars": 10458,
    "preview": "<!--\n\n#####################################\n# THIS FILE IS AUTOGENERATED!       #\n# Edit ./tpl/README.md.in instead   #\n"
  },
  {
    "path": "ci/install_deps.sh",
    "chars": 1158,
    "preview": "#!/bin/bash\nset -o errexit\nset -o nounset\nset -o xtrace\n\nDEPS=(\n  build-essential git gdb valgrind cmake rpm file\n  libc"
  },
  {
    "path": "ci/run_build.sh",
    "chars": 7814,
    "preview": "#!/bin/bash\n# Should be run from the root dir, or SOURCE_DIR should be set.\nset -o errexit\nset -o nounset\nset -o pipefai"
  },
  {
    "path": "ci/util/rpmbuild",
    "chars": 592,
    "preview": "#!/bin/bash\n# Wrapper for rpm build that first removes files that should be in there\n# We need this for compatibility wi"
  },
  {
    "path": "ddist.sh",
    "chars": 917,
    "preview": "#!/bin/bash\nset -o errexit\nset -o nounset\n\nREL_HERE=$(dirname \"${BASH_SOURCE}\")\nHERE=$(cd \"${REL_HERE}\"; pwd)\n\nIMG=\"tini"
  },
  {
    "path": "dtest.sh",
    "chars": 203,
    "preview": "#!/bin/bash\nset -o errexit\nset -o nounset\n\nIMG=\"tini\"\n\nif [[ \"$#\" != 1 ]]; then\n  echo \"Usage: $0 ARCH_SUFFIX\"\n  exit 1\n"
  },
  {
    "path": "run_tests.sh",
    "chars": 271,
    "preview": "#!/bin/bash\nset -o errexit\nset -o nounset\n\nREL_HERE=$(dirname \"${BASH_SOURCE}\")\nHERE=$(cd \"${REL_HERE}\"; pwd)\n\nfor i in "
  },
  {
    "path": "src/tini.c",
    "chars": 19273,
    "preview": "/* See LICENSE file for copyright and license details. */\n#define _GNU_SOURCE\n\n#include <sys/types.h>\n#include <sys/wait"
  },
  {
    "path": "src/tiniConfig.h.in",
    "chars": 128,
    "preview": "#define TINI_VERSION \"@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@\"\n#define TINI_GIT     \"@tini_VERSIO"
  },
  {
    "path": "src/tiniLicense.h",
    "chars": 6841,
    "preview": "unsigned char LICENSE[] = {\n  0x54, 0x68, 0x65, 0x20, 0x4d, 0x49, 0x54, 0x20, 0x4c, 0x69, 0x63, 0x65,\n  0x6e, 0x73, 0x65"
  },
  {
    "path": "test/pdeathsignal/stage_1.py",
    "chars": 466,
    "preview": "#!/usr/bin/env python\nfrom __future__ import print_function\n\nimport os\nimport sys\nimport subprocess\n\n\ndef main():\n    pi"
  },
  {
    "path": "test/pdeathsignal/stage_2.py",
    "chars": 411,
    "preview": "#!/usr/bin/env python\nfrom __future__ import print_function\n\nimport os\nimport sys\nimport signal\nimport time\n\n\ndef main()"
  },
  {
    "path": "test/pgroup/stage_1.py",
    "chars": 342,
    "preview": "#!/usr/bin/env python\nimport os\nimport subprocess\nimport signal\n\n\ndef reset_sig_handler():\n    signal.signal(signal.SIGU"
  },
  {
    "path": "test/reaping/stage_1.py",
    "chars": 1753,
    "preview": "#!/usr/bin/env python3\nimport os\nimport sys\nimport subprocess\nimport time\n\nimport psutil\n\n\ndef in_group_or_reaped(pid):\n"
  },
  {
    "path": "test/reaping/stage_2.py",
    "chars": 282,
    "preview": "#!/usr/bin/env python\nfrom __future__ import print_function\nimport subprocess\nimport os\nimport random\n\n\nif __name__ == \""
  },
  {
    "path": "test/run_inner_tests.py",
    "chars": 9137,
    "preview": "#!/usr/bin/env python3\n# coding:utf-8\nimport os\nimport sys\nimport signal\nimport subprocess\nimport time\nimport psutil\nimp"
  },
  {
    "path": "test/run_outer_tests.py",
    "chars": 5960,
    "preview": "#coding:utf-8\nimport os\nimport sys\nimport time\nimport pipes\nimport subprocess\nimport threading\nimport pexpect\nimport sig"
  },
  {
    "path": "test/sigconf/sigconf-test.c",
    "chars": 1053,
    "preview": "/*\nTest program to:\n+ Ignore a few signals\n+ Block a few signals\n+ Exec whatever the test runner asked for\n*/\n\n#include "
  },
  {
    "path": "test/signals/test.py",
    "chars": 276,
    "preview": "#!/usr/bin/env python\nimport signal\nimport os\n\n\ndef main():\n    signal.signal(signal.SIGTERM, signal.SIG_DFL)\n    signal"
  },
  {
    "path": "test/subreaper-proxy.py",
    "chars": 275,
    "preview": "#!/usr/bin/env python3\n#coding:utf-8\nimport os\nimport sys\n\nimport prctl\n\n\ndef main():\n    args = sys.argv[1:]\n\n    print"
  },
  {
    "path": "tpl/README.md.in",
    "chars": 10626,
    "preview": "<!--\n\n#####################################\n# THIS FILE IS AUTOGENERATED!       #\n# Edit ./tpl/README.md.in instead   #\n"
  },
  {
    "path": "tpl/VERSION.in",
    "chars": 63,
    "preview": "@tini_VERSION_MAJOR@.@tini_VERSION_MINOR@.@tini_VERSION_PATCH@\n"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the krallin/tini GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (83.1 KB), approximately 28.4k tokens, and a symbol index with 36 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!