├── .github └── CODEOWNERS ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.rst ├── docs ├── 404.html ├── CNAME ├── favicon.ico ├── google747986e3861ca881.html ├── images │ ├── trivialrc.svg │ └── trivialrc.xml ├── index.html ├── pages │ ├── index.html │ └── info │ │ └── index.html ├── robots.txt ├── sitemap.xml ├── theme │ ├── bootstrap-pygments.bundle.min.css │ ├── bootstrap.min.css │ ├── bootstrap.min.responsive.css │ ├── icons │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ ├── local.min.css │ └── pygments.min.css ├── trc └── trc.sha256 ├── examples ├── README.md ├── config-files │ ├── README.md │ └── trc.d │ │ ├── async.info │ │ ├── boot.funcs │ │ ├── boot.setvars │ │ ├── halt.delfile │ │ ├── sync.1 │ │ └── sync.2 ├── docker-base-images │ ├── Dockerfile.alpine │ ├── Dockerfile.centos │ └── README.md ├── docker-config-templates │ ├── Dockerfile │ ├── README.md │ ├── haproxy.cfg.ftpl │ └── trc.d │ │ ├── async.haproxy │ │ └── boot.make-conf ├── docker-service-discovery │ ├── Dockerfile │ ├── README.md │ └── trc.d │ │ ├── boot.sd-reg │ │ ├── halt.sd-unreg │ │ └── sync.app ├── docker-two-apps │ ├── Dockerfile │ ├── README.md │ ├── app │ │ ├── app.py │ │ └── favicon.ico │ └── etc │ │ ├── nginx │ │ └── nginx.conf │ │ ├── trc.d │ │ ├── async.nginx │ │ └── async.uwsgi │ │ └── uwsgi.ini ├── one-liners │ └── README.md ├── process-manager │ ├── README.md │ ├── trc-example-procmgr.service │ └── trc.d │ │ ├── async.netlistener │ │ ├── async.netlistener-logger │ │ ├── async.netmon │ │ ├── async.netmon-logger │ │ ├── async.netsender │ │ ├── boot.create-logs │ │ └── halt.remove-logs ├── reliable-tests-for-docker-images │ └── README.md ├── self-configuring │ ├── README.md │ └── trc.boot.make-self-conf └── sync-run-of-async-groups │ ├── README.md │ ├── sub-trc │ └── trc.boot.async-run-cmds │ └── trc.boot.serial-run-groups ├── src.docs ├── .gitignore ├── Makefile ├── content │ ├── images │ │ ├── trivialrc.svg │ │ └── trivialrc.xml │ ├── pages │ │ ├── 404.rst │ │ └── info.rst │ └── static │ │ ├── favicon.ico │ │ ├── google747986e3861ca881.html │ │ └── robots.txt ├── pelicanconf.py └── publishconf.py ├── tests ├── config-test.sh ├── exitcodes-test.sh ├── modes-test.sh ├── trc.d │ ├── async.info │ ├── boot.funcs │ ├── boot.setvars │ ├── halt.goodbye │ └── sync.sleep └── verbose-test.sh ├── trc └── version /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * vorakl@protonmail.com 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: bash 2 | 3 | before_script: 4 | - curl -sSLf https://github.com/bmizerany/roundup/tarball/v0.0.5 | tar xvzf - 5 | - cd bmizerany-roundup-* 6 | - ./configure 7 | - make 8 | - sudo make install 9 | - cd .. 10 | 11 | script: make test 12 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * 1.1.10 2 | * FIX: many README files have been fixed. Thanks to Oren Schulman. 3 | * 1.1.7 4 | * ADD: more examples 5 | * DEL: deprecated names of configuration files have been removed (`trc.fg.*`, `trc.bg.*`, `trc.sd.*`) 6 | * 1.1.6 7 | * FIX: now it ignores errors in the main exit hook 8 | * ADD: more examples 9 | * ADD: sha256 chaeck sum for the release 10 | * 1.1.5 11 | * ADD: new command line options `-v` and `--version` to print the version 12 | * ADD: new command line options `-h` and `--help` to print a short help message 13 | * ADD: new command line options `-w` and `--workdir` to set the directory with config files 14 | * 1.1.4 15 | * ADD: a new wait policy `wait_err` which waits for a first failed command 16 | * ADD: a new stage "halt" (`-H` command line option or `halt.*` files) that runs on exit from the main process 17 | * 1.1.3 18 | * ADD: a new command line option `-F` that runs commands in the foreground (to run more than one foreground processes) 19 | * 1.1.2 20 | * ADD: on exiting from sub-process it tries to kill all possible child processes if any 21 | * 1.1.1 22 | * FIX: fixed the bug in catching exit code of foregraound processes in "wait_any" mode 23 | * 1.1.0 24 | * ADD: a new stage "boot" (`-B` command line option or `boot.*` files) that runs in the main namespace and results (like environment variables) are "visible" in all other sub-processes 25 | * ADD: more ways to present configs: 26 | * ./trc.d/{boot,sync,async,halt}.* 27 | * ./trc.{boot,sync,async,halt}.* 28 | * ADD: examples 29 | * ADD: tests 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright 2016-2020 Oleksii Tsvietnov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # (c) Oleksii Tsvietnov, vorakl@protonmail.com 2 | # 3 | # Variables: 4 | ECHO_BIN ?= echo 5 | CP_BIN ?= cp 6 | SED_BIN ?= sed 7 | PWD_BIN ?= pwd 8 | BASENAME_BIN ?= basename 9 | GIT_BIN ?= git 10 | SHA256SUM_BIN ?= sha256sum 11 | 12 | # ------------------------------------------------------------------------- 13 | # Set a default target 14 | .MAIN: usage 15 | 16 | DIR = $(shell ${PWD_BIN} -P) 17 | SELF = $(shell ${BASENAME_BIN} ${DIR}) 18 | VER = $(shell ${SED_BIN} -n '1s/^[[:space:]]*//; 1s/[[:space:]]*$$//; 1p' ${DIR}/version) 19 | VERSION ?= ${VER} 20 | LAST_COMMIT = $(shell ${GIT_BIN} log -1 | sed -n '/^commit/s/^commit //p') 21 | 22 | usage: 23 | @${ECHO_BIN} "Usage: make [target] ..." 24 | @${ECHO_BIN} "" 25 | @${ECHO_BIN} "Examples: make setver" 26 | @${ECHO_BIN} " make VERSION=v1.15.2 setver" 27 | @${ECHO_BIN} " make settag" 28 | @${ECHO_BIN} " make push" 29 | @${ECHO_BIN} " make release" 30 | @${ECHO_BIN} " make VERSION=v1.1.14 release" 31 | @${ECHO_BIN} "" 32 | @${ECHO_BIN} "Description:" 33 | @${ECHO_BIN} " setver Set a new version (is taken from environment or file)." 34 | @${ECHO_BIN} " settag Set a new version as a tag to the last commit." 35 | @${ECHO_BIN} " push Push to the repo (with tags)." 36 | @${ECHO_BIN} " release Set a version, tag and push to the repo." 37 | @${ECHO_BIN} " test Run all tests" 38 | @${ECHO_BIN} "" 39 | 40 | test: 41 | @(cd tests && roundup) 42 | 43 | setver: 44 | @${ECHO_BIN} "Setting version to ${VERSION}" 45 | @${SED_BIN} -i "s/trc_version=\".*$$/trc_version=\"${VERSION}\"/" ${DIR}/trc 46 | @${SED_BIN} -i "1s/.*/${VERSION}/" ${DIR}/version 47 | 48 | settag: 49 | @${ECHO_BIN} "Setting ${VERSION} as a tag to ${LAST_COMMIT}" 50 | @${GIT_BIN} tag ${VERSION} ${LAST_COMMIT} 2>/dev/null || true 51 | 52 | push: 53 | @${ECHO_BIN} "Pushing commits..." 54 | @${GIT_BIN} push origin 55 | @${ECHO_BIN} "Pushing tags..." 56 | @${GIT_BIN} push origin ${VERSION} 57 | 58 | publish: 59 | @${CP_BIN} -f trc docs/ 60 | @(cd docs && ${SHA256SUM_BIN} trc > trc.sha256) 61 | 62 | cirelease: test setver settag publish 63 | @${GIT_BIN} add . 64 | @${GIT_BIN} ci -m "Release new version: ${VERSION}" 65 | 66 | release: cirelease push 67 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | The minimalistic Run-time Configuration (RC) system and process manager 3 | ####################################################################### 4 | 5 | :slug: info 6 | :summary: The minimalistic Run-time Configuration (RC) system and process manager 7 | 8 | * Community_ 9 | * Introduction_ 10 | * Installation_ 11 | * `The installation on top of CentOS Linux base image`_ 12 | * `The installation on top of Alpine Linux base image`_ 13 | * `How to get started?`_ 14 | * `Command line options`_ 15 | * `Run stages`_ 16 | * `Wait policies`_ 17 | * `Verbose levels`_ 18 | * `Integrated functions`_ 19 | * `Useful global variables`_ 20 | 21 | Community 22 | ========= 23 | 24 | There are a few options for the communication: 25 | 26 | * ``IRC``: #trivialrc channel on the OFTC network (irc.oftc.net) 27 | * ``Mailing list``: trivialrc-dev@freelists.org, subscribe_ 28 | 29 | Introduction 30 | ============ 31 | 32 | The minimalistic Run-time Configuration (RC) system and process manager is written in pure BASH and uses just a few external utilities like ``ls``, ``ps``, ``date`` and ``sleep``. Minimally, installation of TrivialRC consists of only one file which can be downloaded directly from the Github. Originaly, it was designed for use in containers but it also can be well used for running a group of processes asynchronously and synchronously, as well as managing their running order and exit codes. 33 | 34 | TrivialRC is not a replacement for an init process that usually resides in ``/sbin/init`` and has a PID 1. In containers for this purpose projects like dumb-init_ or tini_ can be used, although in most cases, having only TrivialRC as a first/main process (PID 1) in containers is quite enough. In terms of Docker, the best place for it is ENTRYPOINT. 35 | 36 | TrivialRC is an equivalent to well known ``/etc/rc`` for xBSD users. The RC system that is used for managing startup and shutdown processes. It can start and stop one or more processes, in parallel or sequentially, on back- or foreground, react differently in case of process failures, etc. All commands can be specified in the command line if they are relatively simple, or in separate files if a more comprehensive scenario is needed. That's why it can be used as a simplest tool for managing a group of process and be a lightweight replacement for solutions like Supervisor_. 37 | 38 | For instance, in docker images when TrivialRC is used as an Entrypoint, it doesn't reveal itself by default, does not affect any configuration and behaves absolutely transparently. So, you can add it into any Dockerfiles which do not have an entrypoint yet and get by this the full control under processes with fairly detailed logs of what's is going on inside a container. Please, take a look at examples__ for more information. 39 | 40 | __ https://github.com/vorakl/TrivialRC/tree/master/examples 41 | 42 | 43 | Installation 44 | ============ 45 | 46 | Basically, all you need to install TrivialRC is download the latest release of the script from ``http://trivialrc.vorakl.com/trc`` 47 | and give it an execute permission. By default, it looks for configuration files in the same directory from which it was invoked but this behavior can be changed by setting a work directory (``-w|--workdir`` parametr). So, if you are going to use configuration files and keep them in ``/etc/``, then you would probably want to install the script to /etc/ as well and simply run it without specifying any parametrs. 48 | 49 | Another option in this case could be to install the script in a more appropriate path but don't forget to specify ``--workdir /etc`` parametr every time when you invoke this rc system. Both options are possible and depend more on a particular use case. 50 | For instance, in case of using in a docker container, I personaly prefer to have all configuration in separate files in ``trc.d/`` sub-directory and copy it together with the script in ``/etc/``. 51 | 52 | 53 | The installation on top of CentOS Linux base image 54 | -------------------------------------------------- 55 | 56 | This is an example of how it would look in a Dockerfile with `centos:latest`_ as base image: 57 | 58 | .. code-block:: bash 59 | 60 | FROM centos:latest 61 | 62 | RUN curl -sSLfo /etc/trc http://trivialrc.vorakl.com/trc && \ 63 | ( cd /etc && curl -sSLf http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \ 64 | chmod +x /etc/trc && \ 65 | /etc/trc --version 66 | 67 | # Uncomment this if you have configuration files in trc.d/ 68 | # COPY trc.d/ /etc/trc.d/ 69 | 70 | ENTRYPOINT ["/etc/trc"] 71 | 72 | 73 | The installation on top of Alpine Linux base image 74 | -------------------------------------------------- 75 | 76 | **Attention**! The Alpine Linux comes with Busybox but its functionality as a shell and as a few emulated tools ``is not enough`` for TrivialRC. To work in this distribution it requires two extra packages: ``bash`` and ``procps``. 77 | As a result, Dockerfile for the `alpine:latest`_ base image would look like: 78 | 79 | .. code-block:: bash 80 | 81 | FROM alpine:latest 82 | 83 | RUN apk add --no-cache bash procps 84 | 85 | RUN wget -qP /etc/ http://trivialrc.vorakl.com/trc && \ 86 | ( cd /etc && wget -qO - http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \ 87 | chmod +x /etc/trc && \ 88 | /etc/trc --version 89 | 90 | # Uncomment this if you have configuration files in trc.d/ 91 | # COPY trc.d/ /etc/trc.d/ 92 | 93 | ENTRYPOINT ["/etc/trc"] 94 | 95 | 96 | How to get started? 97 | =================== 98 | 99 | To get started and find out some features, basically, I suggest to go through `all available examples`_ and read their readmes plus comments along the code but to start from `one-liners`_ which show most common use cases and features. 100 | 101 | Command line options 102 | ==================== 103 | 104 | It is important to notice that the order of command line options **is not** equal to their run order. 105 | In general it looks like: 106 | 107 | .. code-block:: bash 108 | 109 | $ trc [-h|--help] [-v|--version] [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [command [args]] 110 | 111 | 112 | Where 113 | 114 | * ``-h`` or ``--help``, prints a short help message 115 | * ``-v`` or ``--version``, prints a current version 116 | * ``-w 'directory'`` or ``--workdir 'directory'``, sets a location with configuration files 117 | * ``-B 'command1; command2; ...'``, boot commands 118 | * ``-H 'command1; command2; ...'``, halt commands 119 | * ``-D 'command1; command2; ...'``, async commands 120 | * ``-F 'command1; command2; ...'``, sync commands 121 | * ``command [args]``, a sync command 122 | 123 | So, command line options have to be supplied in the next order 124 | 125 | 1. ``-B``, zero or more 126 | 2. ``-H``, zero or more 127 | 3. ``-D``, zero or more 128 | 4. ``-F``, zero or more 129 | 5. ``command with arguments`` (without an option), zero or only one 130 | 131 | Examples: 132 | 133 | .. code-block:: bash 134 | 135 | $ trc -B 'name=$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, ${name}!' 136 | 137 | $ RC_WAIT_POLICY=wait_all trc -D 'echo Hello' -D 'sleep 2; echo World' echo waiting... 138 | 139 | $ RC_VERBOSE=true trc -F 'echo -n "Hello "; echo World' 140 | 141 | $ trc --workdir /opt/app 142 | 143 | 144 | Run stages 145 | ========== 146 | 147 | The life cycle of TrivialRC consists of different stages, with different isolation. 148 | By default, all configuration files (or trc.d/ directory with them) are searched in the directory from which was executed ``trc`` itself. For instance, if you've installed trc in /usr/bin/ and run it by using only its name, like ``trc``, then configuration will also be searched in /usr/bin/. Though, you can place configuration files anywhere you like and specify their location in the ``-w|--workdir`` option, like ``trc -w /etc/``. 149 | 150 | Let's check: 151 | 152 | .. code-block:: bash 153 | 154 | $ which trc 155 | /usr/bin/trc 156 | 157 | $ trc -B 'echo $work_dir' 158 | /usr/bin 159 | 160 | $ trc -w /etc -B 'echo $work_dir' 161 | /etc 162 | 163 | 164 | All stages are executed through in the next order: 165 | 166 | 1. ``boot`` 167 | **Execution order**: trc.boot.* -> trc.d/boot.* -> [-B 'cmds' [...]] 168 | 169 | Commands run in a same environment as the main process and that's why it has to be used with caution. 170 | It's useful for setting up global variables which are seen in all other isolated environments. 171 | 2. ``async`` 172 | **Execution order**: trc.async.* -> trc.d/async.* -> [-D 'cmds' [...]] 173 | 174 | Commands run in the separate environment, asynchronously (all run in parallel), in the background and do not affect the main process. 175 | If you are going to run more than one async commands, don't forget that default RC_WAIT_POLICY is set to 'wait_any' and the executing process will be stopped after the first finished command and only if there wasn't any running foreground (sync) command that could block the reaction on the TERM signal. So, there are two options: 176 | 177 | * to wait until all async commands have finished, you need to set RC_WAIT_POLICY to 'wait_all'. 178 | * to wait for the first finished command, do not change the default value of RC_WAIT_POLICY but run only async commands. 179 | 3. ``sync`` 180 | **Execution order**: trc.sync.* -> trc.d/sync.* -> [-F 'cmds' [...]] -> [cmd] 181 | 182 | Commands run in the separate environment, synchronously (one by one), in the foreground and do not affect the main process. 183 | if you are going to run more than one sync commands, don't forget to change RC_WAIT_POLICY to 'wait_all' or 'wait_err', otherwise, the executing process will be stopped after the first command. 184 | 4. ``halt`` 185 | **Execution order**: trc.halt.* -> trc.d/halt.* -> [-H 'cmds' [...]] 186 | 187 | Commands run in the separate environment, synchronously (one by one) when the main process is finishing (on exit). 188 | An exit status from the last halt command has precedence under an exit status from the main process which was supplied as ${_exit_status} variable. So you are able to keep a main exit status (by finishing as **exit ${_exit_status}**) or rewrite it to something else but anyway, if you have at least one halt command, TrivialRC will finish with an exit status of this halt command. 189 | 190 | 191 | Wait policies 192 | ============= 193 | 194 | The rc system reacts differently when one of controlled processes finishes. 195 | Depending on the value of **RC_WAIT_POLICY** environment variable it makes a decision when exactly it should stop itself. 196 | The possible values are: 197 | 198 | * ``wait_all`` 199 | stops after exiting all commands and it doesn't matter whether they are synchronous or asynchronous. Just keep in mind, if you need to catch a signal in the main process, it doesn't have to be blocked by some foreground (sync) process. For example, this mode can be helpful if you need to troubleshoot a container (with `wait_any` policy) where some async task fails and the whole container gets stopped by this immediately. In this case, you can change a policy to `wait_all` and run BASH in the foreground like ``docker -e RC_WAIT_POLICY=wait_all some-container bash`` 200 | * ``wait_any`` [default] 201 | stops after exiting any of background commands and if there are no foreground commands working at that moment. It makes sense to use this mode if all commands are **asynchronous** (background). For example, if you need to start more than one process in the docker container, they all have to be asynchronous. Then, the main processed will be able to catch signals (for instance, from a docker daemon) and wait for finishing all other async processes. 202 | * ``wait_err`` 203 | stops after the first failed command. It make sense to use this mode with **synchronous** (foreground) commands only. For example, if you need to iterate sequentially over the list of commands and to stop only if one of them has failed. 204 | * ``wait_forever`` 205 | there is a special occasion when a process has doubled forked to become a daemon, it's still running but for the parent shell such process is considered as finished. So, in this mode, TrivialRC will keep working even if all processes have finished and it has to be stopped by the signal from its parent process (such as docker daemon for example). 206 | 207 | 208 | Verbose levels 209 | ============== 210 | 211 | By default, TrivailRC doesn't print any service messages at all. 212 | It only sends ``stdout`` and ``stderr`` of all isolated sub-shells to the same terminal. 213 | If another behavior is needed, you can redirect any of them inside each sub-shell separately. 214 | To increase the verbosity of rc system there are provided a few environment variables: 215 | 216 | * ``RC_DEBUG`` (true|false) [false] 217 | Prints out all commands which are being executed 218 | * ``RC_VERBOSE`` (true|false) [false] 219 | Prints out service information 220 | * ``RC_VERBOSE_EXTRA`` (true|false) [false] 221 | Prints out additional service information 222 | 223 | 224 | Integrated functions 225 | ==================== 226 | 227 | You can also use some of internal functions in async/sync tasks: 228 | 229 | * ``say`` 230 | prints only if RC_VERBOSE is set 231 | * ``log`` 232 | does the same as ``say`` but add additional info about time, PID, namespace, etc 233 | * ``warn`` 234 | does the say as ``log`` but sends a mesage to stderr 235 | * ``err`` 236 | does the same as ``warn`` but exits with an error (exit status = 1) 237 | * ``debug`` 238 | does the same as ``log`` but only if RC_VERBOSE_EXTRA is set 239 | * ``run`` 240 | launches builtin or external commands without checking functions with the same name 241 | For instance, if you wanna run only external command from the standart PATH list, use ``run -p 'command'`` 242 | Or, if you need to check existence of the command, try ``run -v 'command'`` 243 | 244 | 245 | Useful global variables 246 | ======================= 247 | 248 | * ``MAINPID``, for sending signals to the main process (see `Testing of Docker images`_) 249 | * ``_exit_status``, for checking or rewriting an exit status of the whole script (see `Process Manager`_, `Service Discovery`_) 250 | 251 | .. Links 252 | 253 | .. |build-status| image:: https://travis-ci.org/vorakl/TrivialRC.svg?branch=master 254 | :target: https://travis-ci.org/vorakl/TrivialRC 255 | :alt: Travis CI: continuous integration status 256 | .. _dumb-init: https://github.com/Yelp/dumb-init 257 | .. _tini: https://github.com/krallin/tini 258 | .. _Supervisor: https://github.com/Supervisor/supervisor 259 | .. _`centos:latest`: https://hub.docker.com/_/centos/ 260 | .. _`alpine:latest`: https://hub.docker.com/_/alpine/ 261 | .. _`all available examples`: https://github.com/vorakl/TrivialRC/tree/master/examples 262 | .. _`one-liners`: https://github.com/vorakl/TrivialRC/blob/master/examples/one-liners 263 | .. _`Testing of Docker images`: https://github.com/vorakl/TrivialRC/tree/master/examples/reliable-tests-for-docker-images 264 | .. _`Process Manager`: https://github.com/vorakl/TrivialRC/blob/master/examples/process-manager/trc.d/halt.remove-logs 265 | .. _`Service Discovery`: https://github.com/vorakl/TrivialRC/blob/master/examples/docker-service-discovery/trc.d/halt.sd-unreg 266 | .. _subscribe: https://www.freelists.org/list/trivialrc-dev 267 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | Not Found :: TrivialRC

Not Found

The requested item could not be located.

The requested item could not be located.

-------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | trivialrc.vorakl.com -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/docs/favicon.ico -------------------------------------------------------------------------------- /docs/google747986e3861ca881.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google747986e3861ca881.html -------------------------------------------------------------------------------- /docs/images/trivialrc.svg: -------------------------------------------------------------------------------- 1 | 2 |
start
start
version
or
help
[Not supported by viewer]
work dir
work dir
boot
boot
halt
halt
Process
Manager
Process<br>Manager
bare
bare
No
No
No
No
No
No
No
No
No
No
stop
stop
Yes
Yes
set
$work_dir
[Not supported by viewer]
Yes
Yes
exec
a command
<b>exec</b> <br>a command
run commands
<b>run</b> commands
Yes
Yes
Yes
Yes
No
No
async
async
sync
sync
No
No
collect commands
<b>collect</b> commands
Yes
Yes
wait
accordingly to the $RC_WAIT_POLICY
[Not supported by viewer]
run commands
<b>run</b> commands
Yes
Yes
Yes
Yes
halt
halt
No
No
Yes
Yes
PID 1
print_version()
or
print_usage()
[Not supported by viewer]
Yes
Yes
No
No
Process ManagerPID X..YPID I..K
run commands
<b>run</b> commands
PID N..M
run commands
<b>run</b> commands
-------------------------------------------------------------------------------- /docs/images/trivialrc.xml: -------------------------------------------------------------------------------- 1 | 3V1bc6M2FP41nmlfPAhxfdxNt22mu9tMtzO7ecoQm7XpYssDuE766wtGwnCOiAnIwiEPiS0EiI/vXHTOkTKjN5un35Jgt/7ElmE8M43l04z+MjNNQnwz/1O0PJctjmmVDaskWvJOp4Yv0X8hbzR46z5ahmmjY8ZYnEW7ZuOCbbfhImu0BUnCDs1u31ncvOsuWIWo4csiiHHr12iZrcVzGcbpwO9htFrzW3s2P/AYLH6sErbf8vvNTPr9+FMe3gTiWrx/ug6W7FBroh9m9CZhLCs/bZ5uwrjAVsBWnvdry9Fq3Em4zbqcYPEXlWbP4tnDZQ4F/8qSbM1WbBvEH06t74/PFxZXMPJv62wT5x9J/jG/afL8rWif2+LrPe/2T5hlz/xNB/uM5U2nq39kbMevkWYJ+1GhTvOWcoTFsFofkjelbJ8sRC+H8yZIViHvZtIK4py6IduE+QDzPkkYB1n0b/P6AefQqup3wjH/wKFseQ/85v8G8Z5fNc0HkmGw4zjndAHqYR1l4ZddcBz/IRerJrT8emGShU8vw4Afj59g2eUZXCYtTsHDieAub1rXqC2YOgQO078MyxocO1JON8tKRgGWWbpYRpVKbz/siFLsCG3hsHLwxN1rIppLVxqx7cx04nw47x+T/NMqO8KA29ZhvEPoJ2u2edynesTZaIizh8XZk4izp0CcLe/ajEZPAlqYgJariX/2FcjuAL0ngc60dYmuhUT3wJIfhb8ZJaPKJPFHE0pK0JO/JT7ZEj45mvjkXJ0TrA5ER5s9tZFQPhbTmDHFkY5nIz37Mpy6ApdXMrHyDPl7UM4y/wq03AABHarR+Kl3LMrvUtHcNps0Nx1A4PJV8bNOYL9LkuC51m1XdEjR66gG2k0P4KnvOojH1QOWN55ZppPVA67EZHua9AC5VEhhbhEXWu3jl7swifJhhskrTbky3SGB29flIYmb14T6LmGLME3xxPhTsA1W4bh+uDee4SdE6exYO82GSrDcRBGDABvldrNRfSyQh8j6mD/0qIT03fEsEJ4tf2YIjPzRsuZTl4y6YTErVN6WbQuefo/iGDQFcbTa5l8XORSFdnxfABUtgvgdP7CJlssjyWUwK8CWAGpVWZoauJYEXBWRbYpnPdMCt/IlxwAXu5LTAtcak7nYpE8LXMcdEVxsgqYFrj8ic8V1G8lVhpMxY+ZWiY3guFRy1cJa8j5Mp0Q24MybOrmGtWQaFn4tnvdULacGq0hLPByTEuWx/Ha1w9AfbcwB9CcrKlN/xim1FeBqYwU5MdIidDWy1vYRulg7bpfvikqpAqM4SNNoAUIiT1H2rfb5FGia9ZpYtiVWa3DYL5Bt4PwTurEUwlwOFU0/0YWoAQyfgLZ3rLXbfURC4cSA8op9J8mEKA2g6Q5TOEMTDh3DFBbkibowhYPdGGRCwqdwIbcdEgMUzIqCyM0m2C5Hty0w3mG6tJttsYdrP4ecBzbZb1tw5QhiW6QbQpi81GmeHVwcNboBactk6zEgFiS0B5ja1YBYNjQgQ5N13e6j2oA4k592IAHU6ME5eN4xMXSRhdCIrjv1wCIxAHd1xmdcBbMPnKRuNydV8rbK5A41NSKD1CgMbfE49Zgf/5zV6Gp+4IUsmGJSZH6I4clH3DYwdIJlqjVYnoNZOI1KDAFcoyJLV92fuHlN2oP0ebtAYOtMtzpg9qyz8M+dLM3ESrx6wY+ucnkPT+pGZ5lrjscybGOn5cHY0OBpdGB8rNJQAGHB4rhYeXrlQQRY+agziCAWAU93DoPQ1UlSBSEaHI3t4GUbc7exrqF/ENeX+S2+HHE9brbtNvO4lPR0sx2zeaHhJdktA4YMVBzl8fFMGanCQxC16UFJkHqxYMky2q7i4jFzpyX/tQ7rXWvp079uHr6+u/374e7Pj7c391eZRPUosFM+VgHVZK3OWEiIXjoAR4neYqTbBKlSm+gzUsTAjuXErBScDOm0UsTABZITgxfWmhOJi3UxeAlm7+irbSAgtKM4K6m+F6hexi0yFOWy+Hsbz80BoTajp5tDQFlF13T5q6OJsHzDbgQTh1dDmFiOpjWf9kBhA6UatZRkwb4CoewqemNJGXJrRFXwYCmjeqRMDFidlGFnQKFy1pEZqgxuYxneqKVtDjC3Tt/UkAP52nGJ1mtp5r+szM/2F/5sa4WFMay/TRWX2kmWRE/MB4bLAajIoGqxLrJZsDqXT6wI5pqFzA0QDkO7fBRd4XLhfurGl+y1MLLv6PnwVffNRMPymsutCCXCYNbXL9/mv8Xkv8aU9BBt4uAoVk1SmGpEBS2cQYJyyoTXX59lqZAViv3cXZID/MD3Pvvp5267n5Un7dNgFRanwNmm+gASbSm/EL4WCLdaeP2NLwFVRRCOiD0037R6P4OvC2krCXNeSsF7E1hMdw5eoFQ1oksk68eqvR2Mts0ckJYUmM6KlQvFj1R/LoN0fdQLygTfsmnTHGHkqFjEVYeOqFGnF9rdRdX2uf1zc5Wl6FIDd4GdTSWkPBrsb/P5vXabDc1LVQxeY5kplnw2WAbzh/3AkOy+cgTjdj7/QzsYljsmGL4slHSVKS/aEhUQs+2zuzFfLOUlWZ1e8unzfP5JO59cMiqfZCGp6+RTy848V8An5QXfzQm4XZ9+z14uiuxn7WQVtH7LNt6aUjQgKOX2nWbDQjrnQlE9GIYUCYbWcZnD+iuP0km2lVDIYlWr4MZlpQtZ2bc+Cl7oUqvgHJTSsF8cF+x/jpXwOUB/BayULWZ9W7pVljFp2SpcE4tB0ZwLrWBnFiN2XYbF+D6qdZ/mYtKeum9U1sAchwuDh11Z45l6dJ/nvU73wf5id7KuzwH6v5aV+dfT/z0qu5/+uRT98D8= -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | :: TrivialRC -------------------------------------------------------------------------------- /docs/pages/index.html: -------------------------------------------------------------------------------- 1 | Pages :: TrivialRC

Pages

info The minimalistic Run-time Configuration (RC) system and process manager
-------------------------------------------------------------------------------- /docs/pages/info/index.html: -------------------------------------------------------------------------------- 1 | The minimalistic Run-time Configuration (RC) system and process manager :: TrivialRC

The minimalistic Run-time Configuration (RC) system and process manager

The minimalistic Run-time Configuration (RC) system and process manager


Community

There are a few options for the communication:


Introduction

The minimalistic Run-time Configuration (RC) system and process manager is written in pure BASH and uses just a few external utilities like ls, ps, date and sleep. Minimally, installation of TrivialRC consists of only one file which can be downloaded directly from the Github. Originaly, it was designed for use in containers but it also can be well used for running a group of processes asynchronously and synchronously, as well as managing their running order and exit codes.

TrivialRC is not a replacement for an init process that usually resides in /sbin/init and has a PID 1. In containers for this purpose projects like dumb-init or tini can be used, although in most cases, having only TrivialRC as a first/main process (PID 1) in containers is quite enough. In terms of Docker, the best place for it is ENTRYPOINT.

TrivialRC is an equivalent to well known /etc/rc for xBSD users. The RC system that is used for managing startup and shutdown processes. It can start and stop one or more processes, in parallel or sequentially, on back- or foreground, react differently in case of process failures, etc. All commands can be specified in the command line if they are relatively simple, or in separate files if a more comprehensive scenario is needed. That's why it can be used as a simplest tool for managing a group of process and be a lightweight replacement for solutions like Supervisor.

For instance, in docker images when TrivialRC is used as an Entrypoint, it doesn't reveal itself by default, does not affect any configuration and behaves absolutely transparently. So, you can add it into any Dockerfiles which do not have an entrypoint yet and get by this the full control under processes with fairly detailed logs of what's is going on inside a container. Please, take a look at examples for more information.


Installation

Basically, all you need to install TrivialRC is download the latest release of the script from http://trivialrc.vorakl.com/trc and give it an execute permission. By default, it looks for configuration files in the same directory from which it was invoked but this behavior can be changed by setting a work directory (-w|--workdir parametr). So, if you are going to use configuration files and keep them in /etc/, then you would probably want to install the script to /etc/ as well and simply run it without specifying any parametrs.

Another option in this case could be to install the script in a more appropriate path but don't forget to specify --workdir /etc parametr every time when you invoke this rc system. Both options are possible and depend more on a particular use case. For instance, in case of using in a docker container, I personaly prefer to have all configuration in separate files in trc.d/ sub-directory and copy it together with the script in /etc/.


The installation on top of CentOS Linux base image

This is an example of how it would look in a Dockerfile with centos:latest as base image:

FROM centos:latest
 2 | 
 3 | RUN curl -sSLfo /etc/trc http://trivialrc.vorakl.com/trc && \
 4 |     ( cd /etc && curl -sSLf http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \
 5 |     chmod +x /etc/trc && \
 6 |     /etc/trc --version
 7 | 
 8 | # Uncomment this if you have configuration files in trc.d/
 9 | # COPY trc.d/ /etc/trc.d/
10 | 
11 | ENTRYPOINT ["/etc/trc"]
12 | 

The installation on top of Alpine Linux base image

Attention! The Alpine Linux comes with Busybox but its functionality as a shell and as a few emulated tools is not enough for TrivialRC. To work in this distribution it requires two extra packages: bash and procps. As a result, Dockerfile for the alpine:latest base image would look like:

FROM alpine:latest
13 | 
14 | RUN apk add --no-cache bash procps
15 | 
16 | RUN wget -qP /etc/ http://trivialrc.vorakl.com/trc && \
17 |     ( cd /etc && wget -qO - http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \
18 |     chmod +x /etc/trc && \
19 |     /etc/trc --version
20 | 
21 | # Uncomment this if you have configuration files in trc.d/
22 | # COPY trc.d/ /etc/trc.d/
23 | 
24 | ENTRYPOINT ["/etc/trc"]
25 | 

How to get started?

To get started and find out some features, basically, I suggest to go through all available examples and read their readmes plus comments along the code but to start from one-liners which show most common use cases and features.


Command line options

It is important to notice that the order of command line options is not equal to their run order. In general it looks like:

$ trc [-h|--help] [-v|--version] [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [command [args]]
26 | 

Where

  • -h or --help, prints a short help message
  • -v or --version, prints a current version
  • -w 'directory' or --workdir 'directory', sets a location with configuration files
  • -B 'command1; command2; ...', boot commands
  • -H 'command1; command2; ...', halt commands
  • -D 'command1; command2; ...', async commands
  • -F 'command1; command2; ...', sync commands
  • command [args], a sync command

So, command line options have to be supplied in the next order

  1. -B, zero or more
  2. -H, zero or more
  3. -D, zero or more
  4. -F, zero or more
  5. command with arguments (without an option), zero or only one

Examples:

$ trc -B 'name=$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, ${name}!'
27 | 
28 | $ RC_WAIT_POLICY=wait_all trc -D 'echo Hello' -D 'sleep 2; echo World' echo waiting...
29 | 
30 | $ RC_VERBOSE=true trc -F 'echo -n "Hello "; echo World'
31 | 
32 | $ trc --workdir /opt/app
33 | 

Run stages

The life cycle of TrivialRC consists of different stages, with different isolation. By default, all configuration files (or trc.d/ directory with them) are searched in the directory from which was executed trc itself. For instance, if you've installed trc in /usr/bin/ and run it by using only its name, like trc, then configuration will also be searched in /usr/bin/. Though, you can place configuration files anywhere you like and specify their location in the -w|--workdir option, like trc -w /etc/.

Let's check:

$ which trc
34 | /usr/bin/trc
35 | 
36 | $ trc -B 'echo $work_dir'
37 | /usr/bin
38 | 
39 | $ trc -w /etc -B 'echo $work_dir'
40 | /etc
41 | 

All stages are executed through in the next order:

  1. boot

    Execution order: trc.boot.* -> trc.d/boot.* -> [-B 'cmds' [...]]

    Commands run in a same environment as the main process and that's why it has to be used with caution. It's useful for setting up global variables which are seen in all other isolated environments.

  2. async

    Execution order: trc.async.* -> trc.d/async.* -> [-D 'cmds' [...]]

    Commands run in the separate environment, asynchronously (all run in parallel), in the background and do not affect the main process. If you are going to run more than one async commands, don't forget that default RC_WAIT_POLICY is set to 'wait_any' and the executing process will be stopped after the first finished command and only if there wasn't any running foreground (sync) command that could block the reaction on the TERM signal. So, there are two options:

    • to wait until all async commands have finished, you need to set RC_WAIT_POLICY to 'wait_all'.
    • to wait for the first finished command, do not change the default value of RC_WAIT_POLICY but run only async commands.
  3. sync

    Execution order: trc.sync.* -> trc.d/sync.* -> [-F 'cmds' [...]] -> [cmd]

    Commands run in the separate environment, synchronously (one by one), in the foreground and do not affect the main process. if you are going to run more than one sync commands, don't forget to change RC_WAIT_POLICY to 'wait_all' or 'wait_err', otherwise, the executing process will be stopped after the first command.

  4. halt

    Execution order: trc.halt.* -> trc.d/halt.* -> [-H 'cmds' [...]]

    Commands run in the separate environment, synchronously (one by one) when the main process is finishing (on exit). An exit status from the last halt command has precedence under an exit status from the main process which was supplied as ${_exit_status} variable. So you are able to keep a main exit status (by finishing as exit ${_exit_status}) or rewrite it to something else but anyway, if you have at least one halt command, TrivialRC will finish with an exit status of this halt command.


Wait policies

The rc system reacts differently when one of controlled processes finishes. Depending on the value of RC_WAIT_POLICY environment variable it makes a decision when exactly it should stop itself. The possible values are:

  • wait_all
    stops after exiting all commands and it doesn't matter whether they are synchronous or asynchronous. Just keep in mind, if you need to catch a signal in the main process, it doesn't have to be blocked by some foreground (sync) process. For example, this mode can be helpful if you need to troubleshoot a container (with wait_any policy) where some async task fails and the whole container gets stopped by this immediately. In this case, you can change a policy to wait_all and run BASH in the foreground like docker -e RC_WAIT_POLICY=wait_all some-container bash
  • wait_any [default]
    stops after exiting any of background commands and if there are no foreground commands working at that moment. It makes sense to use this mode if all commands are asynchronous (background). For example, if you need to start more than one process in the docker container, they all have to be asynchronous. Then, the main processed will be able to catch signals (for instance, from a docker daemon) and wait for finishing all other async processes.
  • wait_err
    stops after the first failed command. It make sense to use this mode with synchronous (foreground) commands only. For example, if you need to iterate sequentially over the list of commands and to stop only if one of them has failed.
  • wait_forever
    there is a special occasion when a process has doubled forked to become a daemon, it's still running but for the parent shell such process is considered as finished. So, in this mode, TrivialRC will keep working even if all processes have finished and it has to be stopped by the signal from its parent process (such as docker daemon for example).

Verbose levels

By default, TrivailRC doesn't print any service messages at all. It only sends stdout and stderr of all isolated sub-shells to the same terminal. If another behavior is needed, you can redirect any of them inside each sub-shell separately. To increase the verbosity of rc system there are provided a few environment variables:

  • RC_DEBUG (true|false) [false]
    Prints out all commands which are being executed
  • RC_VERBOSE (true|false) [false]
    Prints out service information
  • RC_VERBOSE_EXTRA (true|false) [false]
    Prints out additional service information

Integrated functions

You can also use some of internal functions in async/sync tasks:

  • say
    prints only if RC_VERBOSE is set
  • log
    does the same as say but add additional info about time, PID, namespace, etc
  • warn
    does the say as log but sends a mesage to stderr
  • err
    does the same as warn but exits with an error (exit status = 1)
  • debug
    does the same as log but only if RC_VERBOSE_EXTRA is set
  • run
    launches builtin or external commands without checking functions with the same name For instance, if you wanna run only external command from the standart PATH list, use run -p 'command' Or, if you need to check existence of the command, try run -v 'command'

Useful global variables

-------------------------------------------------------------------------------- /docs/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /unknown/ 3 | 4 | Sitemap: https://trivialrc.cf/sitemap.xml 5 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | https://trivialrc.vorakl.com/ 5 | daily 6 | 7 | 8 | https://trivialrc.vorakl.com/pages/info/ 9 | weekly 10 | 2023-12-19 11 | 12 | -------------------------------------------------------------------------------- /docs/theme/bootstrap.min.responsive.css: -------------------------------------------------------------------------------- 1 | 2 | .hidden{display:none;visibility:hidden;} 3 | @media (max-width:480px){.nav-collapse{-webkit-transform:translate3d(0, 0, 0);} .page-header h1 small{display:block;line-height:18px;} input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;height:28px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;-ms-box-sizing:border-box;box-sizing:border-box;} .input-prepend input[class*="span"],.input-append input[class*="span"]{width:auto;} input[type="checkbox"],input[type="radio"]{border:1px solid #ccc;} .form-horizontal .control-group>label{float:none;width:auto;padding-top:0;text-align:left;} .form-horizontal .controls{margin-left:0;} .form-horizontal .control-list{padding-top:0;} .form-horizontal .form-actions{padding-left:10px;padding-right:10px;} .modal{position:absolute;top:10px;left:10px;right:10px;width:auto;margin:0;}.modal.fade.in{top:auto;} .modal-header .close{padding:10px;margin:-10px;} .carousel-caption{position:static;}}@media (max-width:768px){.container{width:auto;padding:0 20px;} .row-fluid{width:100%;} .row{margin-left:0;} .row>[class*="span"],.row-fluid>[class*="span"]{float:none;display:block;width:auto;margin:0;}}@media (min-width:768px) and (max-width:980px){.row{margin-left:-20px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:20px;} .span1{width:42px;} .span2{width:104px;} .span3{width:166px;} .span4{width:228px;} .span5{width:290px;} .span6{width:352px;} .span7{width:414px;} .span8{width:476px;} .span9{width:538px;} .span10{width:600px;} .span11{width:662px;} .span12,.container{width:724px;} .offset1{margin-left:82px;} .offset2{margin-left:144px;} .offset3{margin-left:206px;} .offset4{margin-left:268px;} .offset5{margin-left:330px;} .offset6{margin-left:392px;} .offset7{margin-left:454px;} .offset8{margin-left:516px;} .offset9{margin-left:578px;} .offset10{margin-left:640px;} .offset11{margin-left:702px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.762430939%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.801104972%;} .row-fluid .span2{width:14.364640883%;} .row-fluid .span3{width:22.928176794%;} .row-fluid .span4{width:31.491712705%;} .row-fluid .span5{width:40.055248616%;} .row-fluid .span6{width:48.618784527%;} .row-fluid .span7{width:57.182320438000005%;} .row-fluid .span8{width:65.74585634900001%;} .row-fluid .span9{width:74.30939226%;} .row-fluid .span10{width:82.87292817100001%;} .row-fluid .span11{width:91.436464082%;} .row-fluid .span12{width:99.999999993%;} input.span1,textarea.span1,.uneditable-input.span1{width:32px;} input.span2,textarea.span2,.uneditable-input.span2{width:94px;} input.span3,textarea.span3,.uneditable-input.span3{width:156px;} input.span4,textarea.span4,.uneditable-input.span4{width:218px;} input.span5,textarea.span5,.uneditable-input.span5{width:280px;} input.span6,textarea.span6,.uneditable-input.span6{width:342px;} input.span7,textarea.span7,.uneditable-input.span7{width:404px;} input.span8,textarea.span8,.uneditable-input.span8{width:466px;} input.span9,textarea.span9,.uneditable-input.span9{width:528px;} input.span10,textarea.span10,.uneditable-input.span10{width:590px;} input.span11,textarea.span11,.uneditable-input.span11{width:652px;} input.span12,textarea.span12,.uneditable-input.span12{width:714px;}}@media (max-width:675px){body{padding-top:0;} .navbar-fixed-top{position:static;margin-bottom:18px;} .navbar-fixed-top .navbar-inner{padding:5px;} .navbar .container{width:auto;padding:0;} .navbar .brand{padding-left:10px;padding-right:10px;margin:0 0 0 -5px;} .navbar .nav-collapse{clear:left;} .navbar .nav{float:none;margin:0 0 9px;} .navbar .nav>li{float:none;} .navbar .nav>li>a{margin-bottom:2px;} .navbar .nav>.divider-vertical{display:none;} .navbar .nav>li>a,.navbar .dropdown-menu a{padding:6px 15px;font-weight:bold;color:#999999;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} .navbar .dropdown-menu li+li a{margin-bottom:2px;} .navbar .nav>li>a:hover,.navbar .dropdown-menu a:hover{background-color:#222222;} .navbar .dropdown-menu{position:static;top:auto;left:auto;float:none;display:block;max-width:none;margin:0 15px;padding:0;background-color:transparent;border:none;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} .navbar .dropdown-menu:before,.navbar .dropdown-menu:after{display:none;} .navbar .dropdown-menu .divider{display:none;} .navbar-form,.navbar-search{float:none;padding:9px 15px;margin:9px 0;border-top:1px solid #222222;border-bottom:1px solid #222222;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.1),0 1px 0 rgba(255, 255, 255, 0.1);} .navbar .nav.pull-right{float:none;margin-left:0;} .navbar-static .navbar-inner{padding-left:10px;padding-right:10px;} .btn-navbar{display:block;} .nav-collapse{overflow:hidden;height:0;}}@media (min-width:980px){.nav-collapse.collapse{height:auto !important;}}@media (min-width:1200px){.row{margin-left:-30px;*zoom:1;}.row:before,.row:after{display:table;content:"";} .row:after{clear:both;} [class*="span"]{float:left;margin-left:30px;} .span1{width:70px;} .span2{width:170px;} .span3{width:270px;} .span4{width:370px;} .span5{width:470px;} .span6{width:570px;} .span7{width:670px;} .span8{width:770px;} .span9{width:870px;} .span10{width:970px;} .span11{width:1070px;} .span12,.container{width:1170px;} .offset1{margin-left:130px;} .offset2{margin-left:230px;} .offset3{margin-left:330px;} .offset4{margin-left:430px;} .offset5{margin-left:530px;} .offset6{margin-left:630px;} .offset7{margin-left:730px;} .offset8{margin-left:830px;} .offset9{margin-left:930px;} .offset10{margin-left:1030px;} .offset11{margin-left:1130px;} .row-fluid{width:100%;*zoom:1;}.row-fluid:before,.row-fluid:after{display:table;content:"";} .row-fluid:after{clear:both;} .row-fluid>[class*="span"]{float:left;margin-left:2.564102564%;} .row-fluid>[class*="span"]:first-child{margin-left:0;} .row-fluid .span1{width:5.982905983%;} .row-fluid .span2{width:14.529914530000001%;} .row-fluid .span3{width:23.076923077%;} .row-fluid .span4{width:31.623931624%;} .row-fluid .span5{width:40.170940171000005%;} .row-fluid .span6{width:48.717948718%;} .row-fluid .span7{width:57.264957265%;} .row-fluid .span8{width:65.81196581200001%;} .row-fluid .span9{width:74.358974359%;} .row-fluid .span10{width:82.905982906%;} .row-fluid .span11{width:91.45299145300001%;} .row-fluid .span12{width:100%;} input.span1,textarea.span1,.uneditable-input.span1{width:60px;} input.span2,textarea.span2,.uneditable-input.span2{width:160px;} input.span3,textarea.span3,.uneditable-input.span3{width:260px;} input.span4,textarea.span4,.uneditable-input.span4{width:360px;} input.span5,textarea.span5,.uneditable-input.span5{width:460px;} input.span6,textarea.span6,.uneditable-input.span6{width:560px;} input.span7,textarea.span7,.uneditable-input.span7{width:660px;} input.span8,textarea.span8,.uneditable-input.span8{width:760px;} input.span9,textarea.span9,.uneditable-input.span9{width:860px;} input.span10,textarea.span10,.uneditable-input.span10{width:960px;} input.span11,textarea.span11,.uneditable-input.span11{width:1060px;} input.span12,textarea.span12,.uneditable-input.span12{width:1160px;} .thumbnails{margin-left:-30px;} .thumbnails>li{margin-left:30px;}} 4 | -------------------------------------------------------------------------------- /docs/theme/icons/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/docs/theme/icons/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /docs/theme/icons/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/docs/theme/icons/glyphicons-halflings.png -------------------------------------------------------------------------------- /docs/theme/local.min.css: -------------------------------------------------------------------------------- 1 | /* Styling for Nav bar. Bg color, link color etc */ 2 | .navbar-inner{padding-left:20px;padding-right:20px;background-color:#2E435E;background-image:-moz-linear-gradient(top,#2e435e,#205081);background-image:-ms-linear-gradient(top,#2e435e,#205081);background-image:-webkit-gradient(linear,0 0,0 100%,from(#2e435e),to(#205081));background-image:-webkit-linear-gradient(top,#2e435e,#205081);background-image:-o-linear-gradient(top,#2e435e,#205081);background-image:linear-gradient(top,#2e435e,#205081);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333',endColorstr='#222222',GradientType=0);-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.25),inset 0 -1px 0 rgba(0,0,0,0.1)}.navbar .brand:hover{color:#49AFCD}.navbar .brand{padding:12px 20px 12px}.navbar .nav > li > a{color:#FFF;padding:12px 10px 12px}.navbar .nav > li > a:hover{color:#49AFCD}.navbar .divider-vertical{border-right:1px solid #4D4D4D}.navbar{margin-bottom:4em}body{font-size:1em}p{font-size:1em;line-height:1.6em;margin-top:0.2em;margin-bottom:0.2em}.p-footer{font-size:0.7em;line-height:1.6em;padding-bottom:2em}li{line-height:1.6em}h1,h2,h3,h4,h5,h6{padding:2px 0 2px;text-align:justify}.table th,.table td{line-height:1.3em}a:hover{text-decoration:underline}.article{padding-bottom:20px}.content-title{margin-bottom:30px;text-align:justify}.content p{text-align:justify;font-size:1.1rem}.content dd{text-align:justify}.article a:hover{text-decoration:underline}.iconmenu{margin-right:0.4em}.well.small{padding:6px 6px 4px;text-align:justify}.btn-info.xsmall{padding:1px 3px 1px;font-size:11px;float:right;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.btn-info.xsmall.left{padding:1px 3px 1px;font-size:11px;float:left;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px}.img.right{float:right;padding:10px 15px 10px 15px}.summary{margin-bottom:3em}.summary pre{display:none}.well{margin-bottom:5px} 3 | -------------------------------------------------------------------------------- /docs/theme/pygments.min.css: -------------------------------------------------------------------------------- 1 | .highlight pre .hll{background-color:#ffc}.highlight pre{background:#f0f3f3}.highlight pre .c{color:#09f;font-style:italic}.highlight pre .err{color:#a00;background-color:#faa}.highlight pre .k{color:#069;font-weight:700}.highlight pre .o{color:#555}.highlight pre .cm{color:#09f;font-style:italic}.highlight pre .cp{color:#099}.highlight pre .c1{color:#09f;font-style:italic}.highlight pre .cs{color:#09f;font-weight:700;font-style:italic}.highlight pre .gd{background-color:#fcc;border:1px solid #c00}.highlight pre .ge{font-style:italic}.highlight pre .gr{color:red}.highlight pre .gh{color:#030;font-weight:700}.highlight pre .gi{background-color:#cfc;border:1px solid #0c0}.highlight pre .go{color:#aaa}.highlight pre .gp{color:#009;font-weight:700}.highlight pre .gs{font-weight:700}.highlight pre .gu{color:#030;font-weight:700}.highlight pre .gt{color:#9c6}.highlight pre .kc{color:#069;font-weight:700}.highlight pre .kd{color:#069;font-weight:700}.highlight pre .kn{color:#069;font-weight:700}.highlight pre .kp{color:#069}.highlight pre .kr{color:#069;font-weight:700}.highlight pre .kt{color:#078;font-weight:700}.highlight pre .m{color:#f60}.highlight pre .s{color:#c30}.highlight pre .na{color:#309}.highlight pre .nb{color:#366}.highlight pre .nc{color:#0a8;font-weight:700}.highlight pre .no{color:#360}.highlight pre .nd{color:#99f}.highlight pre .ni{color:#999;font-weight:700}.highlight pre .ne{color:#c00;font-weight:700}.highlight pre .nf{color:#c0f}.highlight pre .nl{color:#99f}.highlight pre .nn{color:#0cf;font-weight:700}.highlight pre .nt{color:#309;font-weight:700}.highlight pre .nv{color:#033}.highlight pre .ow{color:#000;font-weight:700}.highlight pre .w{color:#bbb}.highlight pre .mb{color:#f60}.highlight pre .mf{color:#f60}.highlight pre .mh{color:#f60}.highlight pre .mi{color:#f60}.highlight pre .mo{color:#f60}.highlight pre .sb{color:#c30}.highlight pre .sc{color:#c30}.highlight pre .sd{color:#c30;font-style:italic}.highlight pre .s2{color:#c30}.highlight pre .se{color:#c30;font-weight:700}.highlight pre .sh{color:#c30}.highlight pre .si{color:#a00}.highlight pre .sx{color:#c30}.highlight pre .sr{color:#3aa}.highlight pre .s1{color:#c30}.highlight pre .ss{color:#fc3}.highlight pre .bp{color:#366}.highlight pre .vc{color:#033}.highlight pre .vg{color:#033}.highlight pre .vi{color:#033}.highlight pre .il{color:#f60} 2 | -------------------------------------------------------------------------------- /docs/trc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TrivialRC 4 | # The minimalistic RC system and process manager for containers and applications 5 | # Copyright (c) 2016, 2017 by vorakl 6 | 7 | trc_version="v1.1.12" 8 | 9 | set +e # Do not exit on errors by default 10 | if [[ "${RC_DEBUG}" = "true" ]]; then 11 | set -x # Turns on Debug mode 12 | fi 13 | 14 | main() { 15 | check_ver_usage "$1" 16 | check_req_bins ls ps pkill sleep date basename dirname 17 | 18 | # It's possible to exit from the last `halt` command with ${_exit_status} which comes from main() 19 | # and rewrite by this an exit status of the `halt` stage which always has a priority against main's exit status 20 | trap '_exit_status=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_exit_status})"; hook_main_exit' EXIT 21 | trap '_es_mt_s=$?; trap '_ignore_mt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=${_es_mt_s})"; exit ${_es_mt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 22 | trap '_es_mt_er=$?; trap '_ignore_mt_er=1' ERR; if [[ "${RC_WAIT_POLICY}" = "wait_err" ]]; then debug "err-trap (exitcode=${_es_mt_er})"; exit ${_es_mt_er}; fi' ERR 23 | 24 | local _wait_bg_cmd _file _es_m_f=0 _es_m_d=0 25 | 26 | export SELF_NAME=$(run -p basename $0 .sh) # Self name for logging purpose 27 | export MAINPID="${BASHPID}" # PID of the main process 28 | : ${RC_WAIT_POLICY:=wait_any}; export RC_WAIT_POLICY 29 | 30 | work_dir="${PWD}" 31 | boot_stage_left=0 32 | halt_cmds_exist=0 33 | halt_cmds=() 34 | child_pids="" # Collects of child processes 35 | ns="main" # Name Space 36 | 37 | case "${RC_WAIT_POLICY}" in 38 | wait_all) _wait_bg_cmd="run wait";; 39 | wait_any) _wait_bg_cmd="run wait";; 40 | wait_err) _wait_bg_cmd="run wait";; 41 | *) _wait_bg_cmd=":";; 42 | esac 43 | 44 | log "The wait policy: ${RC_WAIT_POLICY}" 45 | 46 | if [[ "$1" = "-w" ]] || [[ "$1" = "--workdir" ]]; then 47 | shift 48 | work_dir="$1" 49 | shift 50 | fi 51 | if [[ -n "${RC_WORK_DIR}" ]]; then 52 | work_dir="${RC_WORK_DIR}" 53 | else 54 | RC_WORK_DIR="${work_dir}" 55 | export RC_WORK_DIR 56 | fi 57 | log "Work dir: ${RC_WORK_DIR}" 58 | 59 | debug "Looking for \`boot\` scripts and commands..." 60 | { 61 | # Reads commands from files to run before everything 62 | for _file in $(run -p ls ${work_dir}/trc.boot.* ${work_dir}/trc.d/boot.* 2>/dev/null || true); do 63 | log "Launching in the $(ns_long ${ns}): ${_file}" 64 | set -e 65 | . ${_file} 66 | set +e 67 | done 68 | 69 | # Checks for boot tasks in the command line (all commands and their parameters should be supplied as one parameter) 70 | while [[ "$1" = "-B" ]]; do 71 | shift 72 | log "Launching in the $(ns_long ${ns}): $1" 73 | set -e 74 | eval "$1" 75 | set +e 76 | shift 77 | done 78 | } 79 | boot_stage_left=1 80 | 81 | debug "Looking for \`halt\` commands..." 82 | # Collects commands for the `halt` stage from a command line to be run on exit from the main exit trap 83 | while [[ "$1" = "-H" ]]; do 84 | shift 85 | halt_cmds_exist=1 86 | halt_cmds[${#halt_cmds[*]}]="$1" 87 | shift 88 | done 89 | 90 | debug "Looking for \`async\` scripts and commands..." 91 | # Reads commands from files to run in the background (in parallel) 92 | for _file in $(run -p ls ${work_dir}/trc.async.* ${work_dir}/trc.d/async.* 2>/dev/null || true); do 93 | ( 94 | # Run this on any exit, catching the exitcode of the "main" command, 95 | # printing additional info and finishing up this sub-program with the right exitcode 96 | trap '_es_dt_ex=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "${_file}"' EXIT 97 | 98 | # In case of exit on errors (set -e) or by a signal, catch exitcode and exit with it, which 99 | # will lead to triggering an EXIT trap 100 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 101 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR 102 | 103 | ns="async" 104 | log "Launching in the $(ns_long ${ns}): ${_file}" 105 | . ${_file} 106 | )& 107 | child_pids="${child_pids} $!" 108 | done 109 | 110 | # Checks for background tasks in the command line (all commands and their parameters should be supplied as one parameter) 111 | while [[ "$1" = "-D" ]]; do 112 | shift 113 | ( 114 | trap '_es_dt_ex=$?; trap '_ignore_dt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "$1"' EXIT 115 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 116 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR 117 | 118 | ns="async" 119 | log "Launching in the $(ns_long ${ns}): $1" 120 | eval "$1" 121 | )& 122 | child_pids="${child_pids} $!" 123 | shift 124 | done 125 | 126 | debug "Looking for \`sync\` scripts and commands..." 127 | # Checks for foreground tasks in files (sequentially) 128 | for _file in $(run -p ls ${work_dir}/trc.sync.* ${work_dir}/trc.d/sync.* 2>/dev/null || true); do 129 | ( 130 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "${_file}"' EXIT 131 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 132 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR 133 | 134 | ns="sync" 135 | log "Launching in the $(ns_long ${ns}): ${_file}" 136 | . ${_file} 137 | ) 138 | # Catch the exitcode of a foreground sub-program 139 | _es_m_f=$? 140 | done 141 | 142 | # Checks for foreground tasks in the command line (all commands and their parameters should be supplied as one parameter) 143 | while [[ "$1" = "-F" ]]; do 144 | shift 145 | ( 146 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$1"' EXIT 147 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 148 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR 149 | 150 | ns="sync" 151 | log "Launching in the $(ns_long ${ns}): $1" 152 | eval "$1" 153 | ) 154 | _es_m_f=$? 155 | shift 156 | done 157 | 158 | # Checks for a foreground task in the command line (without any quotation!) 159 | if [[ -n "$*" ]]; then 160 | ( 161 | set -e 162 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$@"' EXIT 163 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 164 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR 165 | 166 | ns="sync" 167 | log "Launching in the $(ns_long ${ns}): $@" 168 | "$@" 169 | ) 170 | # Catch the exitcode of a foreground sub-program 171 | _es_m_f=$? 172 | fi 173 | 174 | # Wait for all background processes and exit with a status of the last one 175 | # or with 128+SIGNAL in case of getting a signal 176 | # this is a work around for not trigerring ERR trap in main() two times in case of non 0 exist status 177 | ${_wait_bg_cmd} ${child_pids} || _es_m_d=$? 178 | 179 | if [ ${_es_m_d} -ne 0 ]; then 180 | _es_m_f=${_es_m_d} # update exit code only if one of background processes has failed 181 | fi 182 | 183 | if [[ "${RC_WAIT_POLICY}" = "wait_forever" ]]; then 184 | infinite_loop 185 | fi 186 | 187 | return ${_es_m_f} 188 | } 189 | 190 | say() { 191 | if [[ "${RC_VERBOSE}" = "true" ]]; then 192 | run echo "$@" 193 | fi 194 | } 195 | 196 | log() { 197 | say "$(run -p date '+%Y-%m-%d %H:%M:%S') ${SELF_NAME} [${ns}/${BASHPID}]: $@" 198 | } 199 | 200 | warn() { 201 | log "$@" >&2 202 | } 203 | 204 | err() { 205 | warn "$@" 206 | exit 1 207 | } 208 | 209 | debug() { 210 | if [[ "${RC_VERBOSE_EXTRA}" = "true" ]]; then 211 | log " - $@" 212 | fi 213 | } 214 | 215 | hook_main_exit() { 216 | local _pid _cpids _file _cmd _es_mtt_h=0 _term_timeout=5 217 | 218 | # If it fails in the `boot` stage, it appears here with 'set -e'. So, reset to +e again 219 | set +e 220 | trap 'true' ERR 221 | 222 | log "Trying to terminate sub-processes..." 223 | for _pid in ${child_pids}; do 224 | if run -p ps -p ${_pid} &> /dev/null; then 225 | log "terminating the child process " 226 | 227 | # Removes all unexpected sub-processes 228 | _cpids="$(run -p pgrep -P ${_pid} -d,)" 229 | run kill -TERM ${_pid} $(echo "${_cpids}" | tr ',' ' ') &> /dev/null 230 | 231 | while run -p ps -p ${_pid},${_cpids} &> /dev/null && [[ ${_term_timeout} -gt 0 ]] 232 | do 233 | run -p sleep 1 234 | : $((_term_timeout--)) 235 | done 236 | 237 | if run -p ps -p ${_pid},${_cpids} &>/dev/null; then 238 | run kill -KILL ${_pid} $(run echo "${_cpids}" | run -p tr ',' ' ') &> /dev/null 239 | fi 240 | fi 241 | done 242 | debug "removing all unexpected sub-processes" 243 | if ! run -p pkill -KILL -P ${MAINPID} &> /dev/null; then 244 | true 245 | fi 246 | 247 | # --- `halt` 248 | if [[ boot_stage_left -eq 1 ]]; then 249 | debug "Looking for \`halt\` scripts..." 250 | # Checks for shutdown tasks in files (sequentially) 251 | for _file in $(run -p ls ${work_dir}/trc.halt.* ${work_dir}/trc.d/halt.* 2>/dev/null || true); do 252 | halt_cmds_exist=1 253 | ( 254 | set -e # Exit on errors in the sub-shell 255 | ns="halt" 256 | log "Running the shutdown script: ${_file}" 257 | . ${_file} 258 | ) 259 | _es_mtt_h=$? 260 | log "Exiting from the shutdown script (exitcode=${_es_mtt_h}): ${_file}" 261 | done 262 | 263 | # Runs shutdown tasks from the command line if any 264 | for _cmd in "${halt_cmds[@]}"; do 265 | ( 266 | set -e # Exit on errors in the sub-shell 267 | 268 | ns="halt" 269 | log "Running the shutdown command: ${_cmd}" 270 | eval "${_cmd}" 271 | ) 272 | _es_mtt_h=$? 273 | log "Exiting from the shutdown command (exitcode=${_es_mtt_h}): ${_cmd}" 274 | done 275 | fi 276 | 277 | if [[ ${halt_cmds_exist} -eq 1 ]]; then 278 | _exit_status=${_es_mtt_h} 279 | fi 280 | 281 | log "Exited (exitcode=${_exit_status})" 282 | exit ${_exit_status} 283 | } 284 | 285 | hook_sub_exit() { 286 | set +e # do not stop or errors anyway 287 | 288 | local _rc=$1 # Getting the exit code for a logging purpose only 289 | shift 290 | 291 | log "Exiting in the $(ns_long ${ns}) (exitcode=${_rc}): $@" 292 | 293 | if [[ "${RC_WAIT_POLICY}" = "wait_any" ]]; then 294 | # If exiting from a bg process and don't need to wait other processes, let's stop the main 295 | if run -p ps -p ${MAINPID} &> /dev/null; then 296 | debug "terminating the main process " 297 | run kill -TERM ${MAINPID} &> /dev/null 298 | fi 299 | fi 300 | 301 | exit ${_rc} 302 | } 303 | 304 | infinite_loop() { 305 | log "Activated infinite loop! To stop, press or send SIGTERM..." 306 | while true; do 307 | run -p sleep 1 308 | done 309 | } 310 | 311 | ns_long() { 312 | local _ns 313 | 314 | case "$1" in 315 | fg|sync) _ns="foreground";; 316 | bg|async) _ns="background";; 317 | sd|halt) _ns="shutdown";; 318 | main) _ns="boot";; 319 | *) _ns="$1";; 320 | esac 321 | 322 | echo "${_ns}" 323 | } 324 | 325 | check_ver_usage() { 326 | case "$1" in 327 | -v|--version) print_version ;; 328 | -h|--help) print_usage ;; 329 | *) : ;; 330 | esac 331 | } 332 | 333 | print_usage() { 334 | local _self=$0 335 | run echo "Usage: ${_self} [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [cmd [args]]" 336 | run echo "Examples:" 337 | run echo -e " $ ${_self} -B 'name=\$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, \${name}!'" 338 | run echo -e " $ RC_WAIT_POLICY=wait_all ${_self} -D 'echo Hello' -D 'sleep 2; echo World' echo waiting..." 339 | run echo -e " $ RC_VERBOSE=true ${_self} -F 'echo -n \"Hello \"; echo World'" 340 | run echo -e " $ ${_self} --workdir /opt/app" 341 | exit 0 342 | } 343 | 344 | print_version() { 345 | run echo "trc (TrivialRC) $trc_version" 346 | exit 0 347 | } 348 | 349 | run() { 350 | builtin command "$@" 351 | } 352 | 353 | check_req_bins() { 354 | local _cmd 355 | 356 | for _cmd in "$@"; do 357 | run -v ${_cmd} &> /dev/null || { echo "There is no the required command: ${_cmd}" >&2; exit 1; } 358 | done 359 | } 360 | 361 | main "$@" 362 | -------------------------------------------------------------------------------- /docs/trc.sha256: -------------------------------------------------------------------------------- 1 | 35dff4c7a795bffae8e376585aec0cf1923760abfec677d87e58a74878ddedc7 trc 2 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # The TrivialRC's examples 2 | 3 | * [One-liners](https://github.com/vorakl/TrivialRC/blob/master/examples/one-liners/README.md) which show most common use cases and features 4 | * The example of using [configuration files](https://github.com/vorakl/TrivialRC/tree/master/examples/config-files) instead of command line parameters 5 | * A docker container that registers itself in a [Service Discovery](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-service-discovery) in the beginning, then starts some application and automatically removes the registration on exit 6 | * This example launches [two different applications](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-two-apps) inside one docker container and controls the availability both of them. If any of the applications has stopped working fore some reasons, the whole container will be stopped automatically with an appropriate exit status 7 | * The example of building [docker base images](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-base-images) with TrivialRC as an ENTRYPOINT 8 | * Another useful use case is using TrivialRC for [process managing](https://github.com/vorakl/TrivialRC/tree/master/examples/process-manager) of a group of processes which represent one compound application and can be invoked then on the system from the Sysdemd 9 | * This solution shows how to [configure services in a docker container by using templates](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-config-templates) and environment variables 10 | * This example goes even further and shows how easy to [download a configuration from remote resource at run-time](https://github.com/vorakl/docker-images/tree/master/centos-opensmtpd#download-configuration-at-run-time-from-a-remote-resource) in docker container 11 | * This trick shows how to [create configuration on the fly](https://github.com/vorakl/TrivialRC/tree/master/examples/self-configuring) from the `boot` stage 12 | * [Serial launching of a group of parallel processes](https://github.com/vorakl/TrivialRC/tree/master/examples/sync-run-of-async-groups) and failing immediately if some group failed 13 | * [Reliable tests](https://github.com/vorakl/TrivialRC/tree/master/examples/reliable-tests-for-docker-images) for docker images 14 | 15 | -------------------------------------------------------------------------------- /examples/config-files/README.md: -------------------------------------------------------------------------------- 1 | # Keeping the configuration in files 2 | 3 | In these examples it will set a few variables and defining a new function in the `boot` mode. 4 | Then in the background (in the `async` mode) it will create a file using the new function and values of variables. 5 | In the next step, it will run in `sync` mode (in the foreground). Here, trc first sleeps for a defined number of seconds 6 | and then prints out the content of the file. In the last step, it will run in `halt` mode and remove previously created file. 7 | A required RC_WAIT_POLICY for this example is wait_all. 8 | 9 | ### The structure of `trc.d` directory 10 | 11 | ```bash 12 | $ ls -1 trc.d/ 13 | async.info 14 | boot.funcs 15 | boot.setvars 16 | halt.delfile 17 | sync.1 18 | sync.2 19 | ``` 20 | 21 | ### The execution order 22 | 23 | 1. boot.funcs 24 | 2. boot.setvars 25 | 3. async.info 26 | 4. sync.1 27 | 5. sync.2 28 | 6. halt.delfile 29 | 30 | ### Results 31 | 32 | * without logs 33 | 34 | ```bash 35 | $ RC_WAIT_POLICY=wait_all ./trc 36 | host: marche 37 | ip: 127.0.0.1 38 | login: vorakl 39 | ``` 40 | 41 | * with logs 42 | 43 | ```bash 44 | $ RC_VERBOSE=true RC_WAIT_POLICY=wait_all ./trc 45 | 2017-02-23 01:04:16 trc [main/7207]: The wait policy: wait_all 46 | 2017-02-23 01:04:16 trc [main/7207]: Launching in the boot: ./trc.d/boot.funcs 47 | 2017-02-23 01:04:16 trc [main/7207]: Launching in the boot: ./trc.d/boot.setvars 48 | 2017-02-23 01:04:16 trc [async/7225]: Launching in the background: ./trc.d/async.info 49 | 2017-02-23 01:04:16 trc [sync/7231]: Launching in the foreground: ./trc.d/sync.1 50 | 2017-02-23 01:04:16 trc [async/7225]: Exiting in the background (exitcode=0): ./trc.d/async.info 51 | 2017-02-23 01:04:18 trc [sync/7231]: Exiting in the foreground (exitcode=0): ./trc.d/sync.1 52 | 2017-02-23 01:04:18 trc [sync/7244]: Launching in the foreground: ./trc.d/sync.2 53 | host: marche 54 | ip: 127.0.0.1 55 | login: vorakl 56 | 2017-02-23 01:04:18 trc [sync/7244]: Exiting in the foreground (exitcode=0): ./trc.d/sync.2 57 | 2017-02-23 01:04:18 trc [main/7207]: Going down. Running shutdown scripts... 58 | 2017-02-23 01:04:18 trc [halt/7256]: Running the shutdown script: ./trc.d/halt.delfile 59 | 2017-02-23 01:04:18 trc [main/7207]: Handling of termination... 60 | 2017-02-23 01:04:18 trc [main/7207]: Exited. 61 | ``` 62 | -------------------------------------------------------------------------------- /examples/config-files/trc.d/async.info: -------------------------------------------------------------------------------- 1 | if ! touch ${INFO_FILE}; then 2 | print "Cannot create file ${INFO_FILE}" 3 | exit 1 4 | fi 5 | print "host:\t${MYHOSTNAME}\nip:\t${MYIP}\nlogin:\t${USERNAME}\n" > ${INFO_FILE} 6 | -------------------------------------------------------------------------------- /examples/config-files/trc.d/boot.funcs: -------------------------------------------------------------------------------- 1 | print() { 2 | echo -en "$@" 3 | } 4 | declare -fx print 5 | -------------------------------------------------------------------------------- /examples/config-files/trc.d/boot.setvars: -------------------------------------------------------------------------------- 1 | export MYIP=$(hostname -i) 2 | export MYHOSTNAME=$(hostname) 3 | export USERNAME=$(id -un) 4 | export WAIT_TIME=1 5 | export INFO_FILE="localinfo.log" 6 | -------------------------------------------------------------------------------- /examples/config-files/trc.d/halt.delfile: -------------------------------------------------------------------------------- 1 | [[ -f "${INFO_FILE}" ]] && rm -f ${INFO_FILE} || true 2 | -------------------------------------------------------------------------------- /examples/config-files/trc.d/sync.1: -------------------------------------------------------------------------------- 1 | sleep ${WAIT_TIME} 2 | -------------------------------------------------------------------------------- /examples/config-files/trc.d/sync.2: -------------------------------------------------------------------------------- 1 | [[ -f "${INFO_FILE}" ]] && cat ${INFO_FILE} || true 2 | -------------------------------------------------------------------------------- /examples/docker-base-images/Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:latest 2 | 3 | RUN apk add --no-cache bash procps 4 | 5 | RUN wget -qP /etc/ http://trivialrc.cf/trc && \ 6 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \ 7 | chmod +x /etc/trc && \ 8 | /etc/trc --version 9 | 10 | ENTRYPOINT ["/etc/trc"] 11 | -------------------------------------------------------------------------------- /examples/docker-base-images/Dockerfile.centos: -------------------------------------------------------------------------------- 1 | FROM centos:latest 2 | 3 | RUN curl -sSLfo /etc/trc /etc/trc http://trivialrc.cf/trc && \ 4 | ( cd /etc && curl -sSLf /etc/trc http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \ 5 | chmod +x /etc/trc && \ 6 | /etc/trc --version 7 | 8 | ENTRYPOINT ["/etc/trc"] 9 | -------------------------------------------------------------------------------- /examples/docker-base-images/README.md: -------------------------------------------------------------------------------- 1 | # The example of building docker base images with TrivialRC as an ENTRYPOINT 2 | 3 | In this example, as base images were taken CentOS and Alpine. 4 | Basically, it shows two different approaches which are specific for each distribution. 5 | 6 | ## Build 7 | 8 | Run these commands from the directory with the example 9 | 10 | ```bash 11 | $ docker build -t centos-base -f Dockerfile.centos . 12 | $ docker build -t alpine-base -f Dockerfile.alpine . 13 | ``` 14 | 15 | ## Test 16 | 17 | Let's test that TrivialRC works as expected 18 | 19 | ```bash 20 | $ docker run --rm -e RC_VERBOSE=true centos-base 21 | 2017-03-05 22:31:15 trc [main/1]: The wait policy: wait_any 22 | 2017-03-05 22:31:15 trc [main/1]: Trying to terminate sub-processes... 23 | 2017-03-05 22:31:15 trc [main/1]: Exited (exitcode=0) 24 | 25 | $ docker run --rm -e RC_VERBOSE=true alpine-base 26 | 2017-03-05 22:31:29 trc [main/1]: The wait policy: wait_any 27 | 2017-03-05 22:31:29 trc [main/1]: Trying to terminate sub-processes... 28 | 2017-03-05 22:31:29 trc [main/1]: Exited (exitcode=0) 29 | ``` 30 | -------------------------------------------------------------------------------- /examples/docker-config-templates/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM haproxy:1.7-alpine 2 | 3 | # Install TrivialRC 4 | RUN apk add --no-cache bash procps 5 | RUN wget -qP /etc/ http://trivialrc.cf/trc && \ 6 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \ 7 | chmod +x /etc/trc && \ 8 | /etc/trc --version 9 | COPY trc.d/ /etc/trc.d/ 10 | 11 | # Install FakeTpl 12 | RUN wget -qP /usr/bin/ http://faketpl.vorakl.name/faketpl && \ 13 | ( cd /usr/bin && wget -qO - http://faketpl.vorakl.name/faketpl.sha256 | sha256sum -c ) 14 | 15 | # Add a template. The config file will be made at run-time 16 | COPY haproxy.cfg.ftpl /usr/local/etc/haproxy/ 17 | 18 | ENTRYPOINT ["/etc/trc"] 19 | -------------------------------------------------------------------------------- /examples/docker-config-templates/README.md: -------------------------------------------------------------------------------- 1 | # Changing configuration of HAProxy in a docker container at run-time 2 | 3 | In the example, for transforming templates to configuration files is used [FakeTpl](https://github.com/vorakl/FakeTpl). 4 | To understand how it works it's worth following the link and read its documentation. 5 | 6 | ## Build 7 | 8 | Run this command from the directory with the example 9 | 10 | ```bash 11 | docker build -t trc-test-conftpl . 12 | ``` 13 | 14 | ## Environment 15 | 16 | To test HAProxy by balancing some traffic, there will be used three instances with Nginx + uWSGI + Python Flask application which was explained in the another example - [Two applications in one docker image](https://github.com/vorakl/TrivialRC/tree/master/examples/docker-two-apps). They all will be started from the same docker image on three different IP addresses and port 80. 17 | 18 | Let's create three additional IP addresses on the localhost for future instances: 19 | 20 | ```bash 21 | $ sudo ip addr add 127.0.0.2/32 dev lo 22 | $ sudo ip addr add 127.0.0.3/32 dev lo 23 | $ sudo ip addr add 127.0.0.4/32 dev lo 24 | ``` 25 | 26 | And run three containers: 27 | 28 | ```bash 29 | $ docker run -d -p 127.0.0.2:80:80 --name trc-test-2apps-1 -e RC_VERBOSE=true trc-test-2apps 30 | $ docker run -d -p 127.0.0.3:80:80 --name trc-test-2apps-2 -e RC_VERBOSE=true trc-test-2apps 31 | $ docker run -d -p 127.0.0.4:80:80 --name trc-test-2apps-3 -e RC_VERBOSE=true trc-test-2apps 32 | ``` 33 | 34 | So, now there are 3 instances with the same web application and they all are answering on the port 80 but on different IPs. 35 | Take a look at the `host` field, it will be needed later for finding out that balancing works: 36 | 37 | ```bash 38 | $ curl http://127.0.0.2/ 39 | { 40 | "base_url": "http://127.0.0.2/", 41 | "cookies": {}, 42 | "full_path": "/?", 43 | "headers": { 44 | "Accept": "*/*", 45 | "Content-Length": "", 46 | "Content-Type": "", 47 | "Host": "127.0.0.2", 48 | "User-Agent": "curl/7.51.0" 49 | }, 50 | "host": "2254f0879ac2", 51 | "host_url": "http://127.0.0.2/", 52 | "method": "GET", 53 | "path": "/", 54 | "query_string": "", 55 | "scheme": "http" 56 | } 57 | 58 | $ curl http://127.0.0.3/ 59 | { 60 | "base_url": "http://127.0.0.3/", 61 | "cookies": {}, 62 | "full_path": "/?", 63 | "headers": { 64 | "Accept": "*/*", 65 | "Content-Length": "", 66 | "Content-Type": "", 67 | "Host": "127.0.0.3", 68 | "User-Agent": "curl/7.51.0" 69 | }, 70 | "host": "e758ded171f6", 71 | "host_url": "http://127.0.0.3/", 72 | "method": "GET", 73 | "path": "/", 74 | "query_string": "", 75 | "scheme": "http" 76 | } 77 | 78 | $ curl http://127.0.0.4 79 | { 80 | "base_url": "http://127.0.0.4/", 81 | "cookies": {}, 82 | "full_path": "/?", 83 | "headers": { 84 | "Accept": "*/*", 85 | "Content-Length": "", 86 | "Content-Type": "", 87 | "Host": "127.0.0.4", 88 | "User-Agent": "curl/7.51.0" 89 | }, 90 | "host": "50afa4eba886", 91 | "host_url": "http://127.0.0.4/", 92 | "method": "GET", 93 | "path": "/", 94 | "query_string": "", 95 | "scheme": "http" 96 | } 97 | ``` 98 | 99 | ## Preparation 100 | 101 | The last piece of a puzzle - load balancer. 102 | A docker image with HAProxy has only a template of its configuration. 103 | You can modify a final configuration of HAProxy by changing these environment variables. 104 | They all, except BACKEND, have have default values. 105 | 106 | * *BACKEND* (array) [] 107 | * *BIND_IP* (scalar) [127.0.0.1] 108 | * *MAXCONN* (scalar) [2000] 109 | * *TIMEOUT_CONNECT* (scalar) [5000] 110 | * *TIMEOUT_CLIENT* (scalar) [10000] 111 | * *TIMEOUT_SERVER* (scalar) [10000] 112 | 113 | This HAProxy is gonna use host's network to simplify things. The list of backend's names and addresses should be put in the BACKEND array variable. All logs could be found in the host's systemd journal. 114 | 115 | ## Test 116 | 117 | It's time to run a container with HAProxy 118 | 119 | ```bash 120 | $ docker run -d --name trc-test-conftpl -e RC_VERBOSE=true -e BACKEND="([web1]=127.0.0.2 [web2]=127.0.0.3 [web3]=127.0.0.4)" --net=host -v /run/systemd/journal/:/host-journal trc-test-conftpl 121 | 122 | $ docker logs -f trc-test-conftpl 123 | 2017-03-08 18:56:48 trc [main/1]: The wait policy: wait_any 124 | 2017-03-08 18:56:48 trc [main/1]: Launching in the boot: /etc/trc.d/boot.make-conf 125 | 2017-03-08 18:56:48 trc [main/1]: Building a new config file from the template: 126 | global 127 | log /host-journal/dev-log local0 128 | maxconn 2000 129 | stats socket /tmp/haproxy.sock 130 | defaults 131 | log global 132 | mode http 133 | option httplog 134 | option dontlognull 135 | retries 3 136 | option redispatch 137 | option forwardfor 138 | timeout connect 5000 139 | timeout client 10000 140 | timeout server 10000 141 | frontend web 142 | bind 127.0.0.1:80 143 | default_backend web_dyn 144 | backend web_dyn 145 | balance roundrobin 146 | server web1 127.0.0.2:80 check 147 | server web2 127.0.0.3:80 check 148 | server web3 127.0.0.4:80 check 149 | 2017-03-08 18:56:48 trc [async/30]: Launching in the background: /etc/trc.d/async.haproxy 150 | <7>haproxy-systemd-wrapper: executing /usr/local/sbin/haproxy -p /run/haproxy.pid -f /usr/local/etc/haproxy/haproxy.cfg -Ds 151 | ``` 152 | 153 | Everything is Up and Running! 154 | We can access localhost port 80 three times in row and get the answer from three different containers: 155 | 156 | ```bash 157 | $ curl -s http://127.0.0.1/ | grep '"host"' 158 | "host": "2254f0879ac2", 159 | 160 | $ curl -s http://127.0.0.1/ | grep '"host"' 161 | "host": "e758ded171f6", 162 | 163 | $ curl -s http://127.0.0.1/ | grep '"host"' 164 | "host": "50afa4eba886", 165 | ``` 166 | 167 | All logs from HAProxy is being sent to the host's Journal and can be easily accessed by filtering a sarvice name: 168 | 169 | ```bash 170 | $ sudo journalctl -b -f _COMM=haproxy 171 | Mar 08 19:59:02 marche haproxy[11492]: 127.0.0.1:58086 [08/Mar/2017:18:59:02.514] web web_dyn/web1 0/0/0/2/2 200 566 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1" 172 | Mar 08 19:59:06 marche haproxy[11492]: 127.0.0.1:58102 [08/Mar/2017:18:59:06.123] web web_dyn/web2 0/0/0/2/2 200 566 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1" 173 | Mar 08 19:59:09 marche haproxy[11492]: 127.0.0.1:58118 [08/Mar/2017:18:59:09.474] web web_dyn/web3 0/0/0/2/2 200 566 - - ---- 1/1/0/1/0 0/0 "GET / HTTP/1.1" 174 | ``` 175 | 176 | Let's remove all these containers and IP addresses: 177 | 178 | ```bash 179 | $ docker rm -f trc-test-conftpl trc-test-2apps-1 trc-test-2apps-2 trc-test-2apps-3 180 | trc-test-conftpl 181 | trc-test-2apps-1 182 | trc-test-2apps-2 183 | trc-test-2apps-3 184 | 185 | $ sudo ip addr del 127.0.0.2/32 dev lo 186 | $ sudo ip addr del 127.0.0.3/32 dev lo 187 | $ sudo ip addr del 127.0.0.4/32 dev lo 188 | ``` 189 | 190 | -------------------------------------------------------------------------------- /examples/docker-config-templates/haproxy.cfg.ftpl: -------------------------------------------------------------------------------- 1 | global 2 | log /host-journal/dev-log local0 3 | maxconn ${MAXCONN:-2000} 4 | stats socket /tmp/haproxy.sock 5 | 6 | defaults 7 | log global 8 | mode http 9 | option httplog 10 | option dontlognull 11 | retries 3 12 | option redispatch 13 | option forwardfor 14 | timeout connect ${TIMEOUT_CONNECT:-5000} 15 | timeout client ${TIMEOUT_CLIENT:-10000} 16 | timeout server ${TIMEOUT_SERVER:-10000} 17 | 18 | frontend web 19 | bind ${BIND_IP:-127.0.0.1}:80 20 | default_backend web_dyn 21 | 22 | backend web_dyn 23 | balance ${LB_ALG:-roundrobin} 24 | $(IFS=' '; for host in $(tr ' ' '\n' <<< ${!BACKENDS[@]} | sort -n | tr '\n' ' '); do echo " server ${host} ${BACKENDS[${host}]}:80 check"; done) 25 | 26 | -------------------------------------------------------------------------------- /examples/docker-config-templates/trc.d/async.haproxy: -------------------------------------------------------------------------------- 1 | /usr/local/sbin/haproxy-systemd-wrapper -p /run/haproxy.pid -f /usr/local/etc/haproxy/haproxy.cfg 2 | -------------------------------------------------------------------------------- /examples/docker-config-templates/trc.d/boot.make-conf: -------------------------------------------------------------------------------- 1 | # import faketpl function 2 | . faketpl 3 | 4 | # It takes scalar from environment and makes array from it 5 | if [[ -n "${BACKEND}" ]]; then 6 | declare -A BACKENDS 7 | eval BACKENDS=${BACKEND}; 8 | fi 9 | 10 | log "Building a new config file from the template:" 11 | (faketpl < /usr/local/etc/haproxy/haproxy.cfg.ftpl | tee /usr/local/etc/haproxy/haproxy.cfg) 12 | -------------------------------------------------------------------------------- /examples/docker-service-discovery/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos 2 | MAINTAINER "Oleksii Tsvietnov" 3 | 4 | ENV RC_VERBOSE true 5 | ENV ZKURL 127.0.0.1 6 | 7 | RUN curl -sSLfo /etc/trc http://trivialrc.cf/trc && \ 8 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \ 9 | chmod +x /etc/trc && \ 10 | /etc/trc --version 11 | 12 | RUN curl -sSLfo /usr/bin/zookeepercli https://github.com/outbrain/zookeepercli/releases/download/v1.0.10/zookeepercli && \ 13 | chmod +x /usr/bin/zookeepercli 14 | 15 | COPY trc.d/ /etc/trc.d/ 16 | 17 | ENTRYPOINT ["/etc/trc"] 18 | -------------------------------------------------------------------------------- /examples/docker-service-discovery/README.md: -------------------------------------------------------------------------------- 1 | # The example of a registration a newly created docker container in the Service Discovery 2 | 3 | In this example, after a docker container has started up, 4 | it's going to register in the Service Discovery as a first step. 5 | After that, it will start the main application and, in the end, on exit by any reasons 6 | the container will remove itself from the Service Discovery as the last step. 7 | 8 | As a Service Discovery is used ZooKeeper and you can specify the URL during the docker run. 9 | 10 | ## Build 11 | 12 | Run this command from the directory with the example 13 | 14 | ```bash 15 | docker build -t trc-test-sd . 16 | ``` 17 | 18 | ## Test 19 | 20 | To test this example you need an IP of a working ZooKeeper cluster. 21 | The 'virtual app' can be interrupted by pressing ``. 22 | 23 | The command line will look like 24 | 25 | ```bash 26 | $ docker run --rm -it -e ZKURL="srv1[:port1][,srv2[:port2]...]" trc-test-sd 27 | ``` 28 | For example: 29 | 30 | ```bash 31 | $ docker run --rm -it -e ZKURL=192.168.1.173 trc-test-sd 32 | 2017-03-03 16:49:06 trc [main/1]: The wait policy: wait_any 33 | 2017-03-03 16:49:06 trc [main/1]: Launching in the boot: /etc/trc.d/boot.sd-reg 34 | 2017-03-03 16:49:06 trc [main/1]: 2a3556e6f646 has been registered at 192.168.1.173 35 | 2017-03-03 16:49:06 trc [sync/29]: Launching in the foreground: /etc/trc.d/sync.app 36 | I am alive! Press to exit... 37 | I am alive! Press to exit... 38 | I am alive! Press to exit... 39 | ^C2017-03-03 16:49:08 trc [sync/29]: Exiting in the foreground (exitcode=130): /etc/trc.d/sync.app 40 | 2017-03-03 16:49:09 trc [main/1]: Trying to terminate sub-processes... 41 | 2017-03-03 16:49:09 trc [halt/44]: Running the shutdown script: /etc/trc.d/halt.sd-unreg 42 | 2017-03-03 16:49:09 trc [halt/44]: 2a3556e6f646 has been unregistered at 192.168.1.173 43 | 2017-03-03 16:49:09 trc [main/1]: Exiting from the shutdown script (exitcode=0): /etc/trc.d/halt.sd-unreg 44 | 2017-03-03 16:49:09 trc [main/1]: Exited. 45 | 46 | $ echo $? 47 | 0 48 | ``` 49 | -------------------------------------------------------------------------------- /examples/docker-service-discovery/trc.d/boot.sd-reg: -------------------------------------------------------------------------------- 1 | if ! zookeepercli -servers "${ZKURL}" -c get /nodes &>/dev/null 2 | then 3 | zookeepercli -servers "${ZKURL}" -c create /nodes "" &>/dev/null || err "Cannot connect to the ZooKeeper at ${ZKURL}" 4 | fi 5 | 6 | zookeepercli -servers "${ZKURL}" -c create /nodes/$(hostname) "" 7 | log "$(hostname) has been registered at ${ZKURL}" 8 | -------------------------------------------------------------------------------- /examples/docker-service-discovery/trc.d/halt.sd-unreg: -------------------------------------------------------------------------------- 1 | # Ignore any errors on exit 2 | set +e 3 | 4 | if zookeepercli -servers "${ZKURL}" -c get /nodes/$(hostname) &>/dev/null 5 | then 6 | zookeepercli -servers "${ZKURL}" -c rm /nodes/$(hostname) 7 | log "$(hostname) has been unregistered at ${ZKURL}" 8 | fi 9 | 10 | # Ignoring exit status 130 (after pressing Ctrl+C) by finishing with status 0 11 | exit 0 12 | -------------------------------------------------------------------------------- /examples/docker-service-discovery/trc.d/sync.app: -------------------------------------------------------------------------------- 1 | while true; do 2 | echo "I am alive! Press to exit..." 3 | sleep 1 4 | done 5 | -------------------------------------------------------------------------------- /examples/docker-two-apps/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:edge 2 | MAINTAINER "Oleksii Tsvietnov" 3 | 4 | RUN apk add --no-cache nginx uwsgi uwsgi-python3 && \ 5 | pip3 install --upgrade pip && \ 6 | pip3 install flask 7 | 8 | RUN mkdir /run/uwsgi && \ 9 | chown nginx:nginx /run/uwsgi 10 | COPY app/ /app/ 11 | 12 | # The required packages for TrivialRC to be run on Alpine Linux 13 | RUN apk add --no-cache bash procps 14 | RUN wget -qP /etc/ http://trivialrc.cf/trc && \ 15 | ( cd /etc && wget -qO - http://trivialrc.cf/trc.sha256 | sha256sum -c ) && \ 16 | chmod +x /etc/trc && \ 17 | /etc/trc --version 18 | COPY etc/ /etc/ 19 | 20 | EXPOSE 80 21 | 22 | ENTRYPOINT ["/etc/trc"] 23 | -------------------------------------------------------------------------------- /examples/docker-two-apps/README.md: -------------------------------------------------------------------------------- 1 | # The example of launching two applications within one docker container 2 | 3 | In this example we will launch Python Flask application plus uWSGI behind Nginx in one docker container. 4 | They will work simultaneously and communicate to each other inside the same container. 5 | If any of these serviceses, either uWSGI or Nginx, at any point of time, has stopped working by some reasons, 6 | the whole container will be stopped as well by TrivialRC. 7 | 8 | ## Build 9 | 10 | Run this command from the directory with the example: 11 | 12 | ```bash 13 | docker build -t trc-test-2apps . 14 | ``` 15 | 16 | ## Test 17 | 18 | Let's run a new container with mapping its port 80 to the host's port 8080: 19 | 20 | ```bash 21 | docker run -d -p 8080:80 --name trc-test-2apps -e RC_VERBOSE=true trc-test-2apps 22 | ``` 23 | 24 | To check if both services were started and a container is up and running, run next commands: 25 | 26 | ```bash 27 | docker ps -al 28 | docker logs trc-test-2apps 29 | ``` 30 | 31 | Now we can check if the Flask application is answering by accessing port 8080 on the localhost. 32 | It should show a JSON document with HTTP request headers. 33 | 34 | ```bash 35 | $ curl http://localhost:8080/ 36 | { 37 | "base_url": "http://localhost:8080/", 38 | "cookies": {}, 39 | "full_path": "/?", 40 | "headers": { 41 | "Accept": "*/*", 42 | "Content-Length": "", 43 | "Content-Type": "", 44 | "Host": "localhost:8080", 45 | "User-Agent": "curl/7.51.0" 46 | }, 47 | "host": "52d0ba70fef2", 48 | "host_url": "http://localhost:8080/", 49 | "method": "GET", 50 | "path": "/", 51 | "query_string": "", 52 | "scheme": "http" 53 | } 54 | ``` 55 | 56 | If you wanna check that a container is really being stopped when uWSGI or Nginx doesn't work in the container, 57 | you can stop any of them while a container is still running and check its status: 58 | 59 | ```bash 60 | $ docker exec trc-test-2apps pkill nginx 61 | 62 | $ docker ps -al 63 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 64 | 52d0ba70fef2 trc-test-2apps "/etc/trc" 12 minutes ago Exited (143) 8 seconds ago trc-test-2apps 65 | 66 | $ docker logs trc-test-2apps |& tail -10 67 | WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x559785bca080 pid: 26 (default app) 68 | *** uWSGI is running in multiple interpreter mode *** 69 | spawned uWSGI worker 1 (and the only) (pid: 26, cores: 16) 70 | [pid: 26|app: 0|req: 1/1] 172.17.0.1 () {32 vars in 324 bytes} [Sat Mar 4 21:26:15 2017] GET / => generated 389 bytes in 4 msecs (HTTP/1.1 200) 2 headers in 72 bytes (2 switches on core 1) 71 | [04/Mar/2017:21:26:15 +0000] localhost 172.17.0.1 "GET / HTTP/1.1" 200 389 "-" "curl/7.51.0" "-" 72 | 2017-03-04 21:33:31 trc [async/16]: Exiting in the background (exitcode=0): /etc/trc.d/async.nginx 73 | 2017-03-04 21:33:31 trc [main/1]: Trying to terminate sub-processes... 74 | 2017-03-04 21:33:31 trc [main/1]: terminating the child process 75 | 2017-03-04 21:33:31 trc [async/17]: Exiting in the background (exitcode=30): /etc/trc.d/async.uwsgi 76 | 2017-03-04 21:33:32 trc [main/1]: Exited (exitcode=143) 77 | ``` 78 | 79 | And don't forget to remove a container manually:) 80 | 81 | ```bash 82 | $ docker rm trc-test-2apps 83 | ``` 84 | -------------------------------------------------------------------------------- /examples/docker-two-apps/app/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask import request 3 | from flask import jsonify 4 | import socket 5 | 6 | app = Flask(__name__) 7 | 8 | @app.route("/") 9 | def headers(): 10 | return jsonify(host=socket.gethostname(), 11 | host_url=request.host_url, 12 | base_url=request.base_url, 13 | headers={ k:v for k, v in request.headers }, 14 | cookies=request.cookies, 15 | query_string=request.query_string.decode(), 16 | path=request.path, 17 | full_path=request.full_path, 18 | method=request.method, 19 | scheme=request.scheme) 20 | 21 | if __name__ == "__main__": 22 | app.run() 23 | -------------------------------------------------------------------------------- /examples/docker-two-apps/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/examples/docker-two-apps/app/favicon.ico -------------------------------------------------------------------------------- /examples/docker-two-apps/etc/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes auto; 2 | daemon off; 3 | pid /run/nginx.pid; 4 | error_log /dev/stderr warn; 5 | user nginx; 6 | 7 | 8 | events { 9 | worker_connections 512; 10 | } 11 | 12 | http { 13 | include mime.types; 14 | sendfile on; 15 | keepalive_timeout 70; 16 | 17 | log_format custom '[$time_local] $host $remote_addr "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$gzip_ratio"'; 18 | access_log /dev/stdout custom; 19 | 20 | server { 21 | listen 80 default_server; 22 | server_name _; 23 | root /app; 24 | 25 | location / { 26 | try_files $uri @flaskApp; 27 | } 28 | 29 | location @flaskApp { 30 | include uwsgi_params; 31 | uwsgi_pass unix:/run/uwsgi/uwsgiApp.sock; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/docker-two-apps/etc/trc.d/async.nginx: -------------------------------------------------------------------------------- 1 | nginx 2 | -------------------------------------------------------------------------------- /examples/docker-two-apps/etc/trc.d/async.uwsgi: -------------------------------------------------------------------------------- 1 | uwsgi --ini /etc/uwsgi.ini 2 | -------------------------------------------------------------------------------- /examples/docker-two-apps/etc/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | plugins = /usr/lib/uwsgi/python3 3 | chdir = /app 4 | module = app:app 5 | uid = nginx 6 | gid = nginx 7 | socket = /run/uwsgi/uwsgiApp.sock 8 | pidfile = /run/uwsgi/uwsgi.pid 9 | processes = 1 10 | threads = 16 11 | -------------------------------------------------------------------------------- /examples/one-liners/README.md: -------------------------------------------------------------------------------- 1 | # One Liners 2 | 3 | ```bash 4 | # The simplest start is doing nothing 5 | $ ./trc 6 | ``` 7 | ```bash 8 | # The most famous one 9 | $ ./trc echo Hello World 10 | Hello World 11 | ``` 12 | ```bash 13 | # Turns on logs 14 | $ RC_VERBOSE=true ./trc echo Hello World 15 | 2017-02-22 23:10:11 trc [main/23630]: The wait policy: wait_any 16 | 2017-02-22 23:10:11 trc [sync/23640]: Launching in the foreground: echo Hello World 17 | Hello World 18 | 2017-02-22 23:10:11 trc [sync/23640]: Exiting in the foreground (exitcode=0): echo Hello World 19 | 2017-02-22 23:10:11 trc [main/23630]: Going down. Running shutdown scripts... 20 | 2017-02-22 23:10:11 trc [main/23630]: Handling of termination... 21 | 2017-02-22 23:10:11 trc [main/23630]: Exited. 22 | 23 | $ echo $? 24 | 0 25 | ``` 26 | ```bash 27 | # Exits with a proper exit code 28 | $ ./trc exit 111 29 | 30 | $ echo $? 31 | 111 32 | ``` 33 | ```bash 34 | # Both commands are running in the foreground but it exits after the first one by default 35 | $ RC_VERBOSE=true \ 36 | RC_VERBOSE_EXTRA=true \ 37 | ./trc -F 'echo Hello' 38 | echo World 39 | 2017-02-22 23:14:35 trc [main/24314]: The wait policy: wait_any 40 | 2017-02-22 23:14:35 trc [sync/24324]: Launching in the foreground: echo Hello 41 | Hello 42 | 2017-02-22 23:14:35 trc [sync/24324]: - exit-trap (exitcode=0) 43 | 2017-02-22 23:14:35 trc [sync/24324]: Exiting in the foreground (exitcode=0): echo Hello 44 | 2017-02-22 23:14:35 trc [sync/24324]: - terminating the main process 45 | 2017-02-22 23:14:35 trc [main/24314]: - sig-trap (exitcode=0) 46 | 2017-02-22 23:14:35 trc [main/24314]: - exit-trap (exitcode=0) 47 | 2017-02-22 23:14:35 trc [main/24314]: Going down. Running shutdown scripts... 48 | 2017-02-22 23:14:35 trc [main/24314]: Handling of termination... 49 | 2017-02-22 23:14:35 trc [main/24314]: Exited. 50 | ``` 51 | ```bash 52 | # To achive the same goal it needs to wait for all commands and then we'll see both outputs 53 | $ RC_WAIT_POLICY=wait_all ./trc -F 'echo Hello' echo World 54 | Hello 55 | World 56 | ``` 57 | ```bash 58 | # A few ways to run many commands in the foreground 59 | $ RC_WAIT_POLICY=wait_all \ 60 | ./trc -F 'echo Hello' \ 61 | -F 'sleep 1' \ 62 | -F 'echo World' 63 | Hello 64 | World 65 | 66 | $ ./trc -F 'echo Hello; sleep 1; echo World' 67 | Hello 68 | World 69 | ``` 70 | ```bash 71 | # It's going to create file in the background, waiting for 3 sec and then reading this file 72 | $ RC_WAIT_POLICY=wait_all \ 73 | ./trc -D 'date > date1.log' \ 74 | -F 'sleep 3' \ 75 | -F 'echo -e "Old time: $(cat date1.log)\nNew time: $(date)"' 76 | -F 'rm -f date1.log' 77 | Old time: Wed Feb 22 14:15:20 CET 2017 78 | New time: Wed Feb 22 14:15:23 CET 2017 79 | 80 | $ ls -l date1.log 81 | ls: cannot access 'date1.log': No such file or directory 82 | ``` 83 | ```bash 84 | # It assigns environment variables in the boot-block an then uses them in the foreground-block 85 | ./trc -B 'export myhost=$(hostname) user=$(id -un)' \ 86 | -F 'echo -e "Username: $user\nHostname: $myhost"' 87 | Username: vorakl 88 | Hostname: marche 89 | ``` 90 | ```bash 91 | # It catches all exit codes of all background processes and prints them out in a readable way 92 | $ RC_VERBOSE=true \ 93 | RC_WAIT_POLICY=wait_all \ 94 | ./trc -D 'exit 2' \ 95 | -D 'false' \ 96 | -D 'exit 4' \ 97 | -D 'true' | \ 98 | sed -n 's|^.*(exitcode=\([[:digit:]]*\)): \(.*\)$|\2\t : \1|p' 99 | exit 2 : 2 100 | true : 0 101 | false : 1 102 | exit 4 : 4 103 | ``` 104 | ```bash 105 | # It catches an exit code of the first failed command. 106 | # It's `exit 4` because `exit 0` didn't fail and all other commands stil run 107 | # For this example it needs a `wait_err` wait policy 108 | $ RC_WAIT_POLICY=wait_err ./trc -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5 109 | 110 | $ echo $? 111 | 4 112 | 113 | # And now, let's run the same example but with a `wait_all` wait policy. 114 | # In this case, it will wait for all commands and exit with a status of the last command which is `sleep 2; exit 3` 115 | $ RC_WAIT_POLICY=wait_all ./trc -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5 116 | 117 | $ echo $? 118 | 3 119 | ``` 120 | 121 | -------------------------------------------------------------------------------- /examples/process-manager/README.md: -------------------------------------------------------------------------------- 1 | # Managing a group of processes 2 | 3 | When your application is quite complex and it relies on a specific group of processes (other applications) there is a need to having a controller of this group as a whole. Usually, all processes have to be in "Up and Running" state all the time for a proper functioning. For this purpose, a process manager restarts some of them in case of failures, writes some messages to the log, etc. If it's not possible to bring back to life a failed app, it should puts down the whole group, notify about this situation and finishes with a proper exit status. 4 | 5 | So, that's the solution! It consists of systemd unit that runs TrivialRC that runs, let's say, some three apps. Each of them will be run asynchronously. There is a special routine in each async task that restarts the app if it fails and writes a message about that to the main log using one of [internal functions](https://github.com/vorakl/TrivialRC#integrated-functions). There are available a few function for logging which are printing out messages in the same format as TrivialRC does and depending on values of environment variables (RC_VERBOSE, RC_VERBOSE_EXTRA). Please, follow the link for more information. 6 | 7 | To prevent printing out stdout/stderr from all applications to the same log at the same time by making a real mess, each async task will redirect stdout/stderr to the uniq named pipe per task and then another async task will be printing them out, line by line, and prefixing lines by a task id. All uniq pipes for logging will be created at `boot` stage and removed at `halt` stage. By doing this, we will have in the main log only TrivialRC's logs regarding launching tasks and logs from tasks if they were sent by `log` of `warn` functions. 8 | All these messages will be seen via `journalctl`. 9 | 10 | ## Installation 11 | 12 | To try this example you need to acomplish 3 steps: 13 | 14 | * Install TrivialRC script in a standart path as `/usr/bin/trc` and give it an execute permission 15 | * Copy configuration into /opt/app, so you've got `/opt/app/trc.d/` with all config files 16 | * Copy Systemd unit `trc-example-procmgr.service` to /etc/systemd/system/ and run `sudo systemctl enable trc-example-procmgr` 17 | 18 | ## Test 19 | 20 | If these steps are done, you can simply start Systemd unit and check via journalctl how it reacts on killing different processes from the group: 21 | 22 | ```bash 23 | $ sudo systemctl start trc-example-procmgr 24 | $ sudo kill `pidof ss` 25 | $ sudo kill `pidof nc` 26 | $ sudo systemctl stop trc-example-procmgr 27 | 28 | $ sudo journalctl -o cat -u trc-example-procmgr 29 | Started The example of running TrivialRC as a process manager. 30 | 2017-03-06 23:46:29 trc [main/29871]: The wait policy: wait_any 31 | 2017-03-06 23:46:29 trc [main/29871]: Launching in the boot: /opt/app/trc.d/boot.create-logs 32 | 2017-03-06 23:46:29 trc [async/29929]: Launching in the background: /opt/app/trc.d/async.netlistener-logger 33 | 2017-03-06 23:46:29 trc [async/29933]: Launching in the background: /opt/app/trc.d/async.netmon-logger 34 | 2017-03-06 23:46:29 trc [async/29928]: Launching in the background: /opt/app/trc.d/async.netlistener 35 | 2017-03-06 23:46:29 trc [async/29931]: Launching in the background: /opt/app/trc.d/async.netmon 36 | 2017-03-06 23:46:29 trc [async/29934]: Launching in the background: /opt/app/trc.d/async.netsender 37 | 2017-03-06 23:46:29 trc [async/29931]: Start monitoring TCP activity... 38 | netmon: State Recv-Q Send-Q Local Address:Port Peer Address:Port 39 | 2017-03-06 23:46:29 trc [async/29928]: Start a network listener on the port 1234... 40 | netlistener: Mon Mar 6 23:46:29 CET 2017 41 | netlistener: Mon Mar 6 23:46:30 CET 2017 42 | netlistener: Mon Mar 6 23:46:31 CET 2017 43 | netlistener: Mon Mar 6 23:46:32 CET 2017 44 | netlistener: Mon Mar 6 23:46:33 CET 2017 45 | netlistener: Mon Mar 6 23:46:34 CET 2017 46 | Terminated 47 | 017-03-06 23:46:34 trc [async/29931]: Monitoring tool has stopped working! 48 | netlistener: Mon Mar 6 23:46:35 CET 2017 49 | 2017-03-06 23:46:35 trc [async/29931]: Start monitoring TCP activity... 50 | netmon: State Recv-Q Send-Q Local Address:Port Peer Address:Port 51 | netlistener: Mon Mar 6 23:46:36 CET 2017 52 | netlistener: Mon Mar 6 23:46:37 CET 2017 53 | Terminated 54 | 2017-03-06 23:46:37 trc [async/29928]: The network listener has stopped working! 55 | 2017-03-06 23:46:38 trc [async/29928]: Start a network listener on the port 1234... 56 | netlistener: Mon Mar 6 23:46:39 CET 2017 57 | netlistener: Mon Mar 6 23:46:40 CET 2017 58 | netlistener: Mon Mar 6 23:46:41 CET 2017 59 | netlistener: Mon Mar 6 23:46:42 CET 2017 60 | netlistener: Mon Mar 6 23:46:43 CET 2017 61 | Stopping The example of running TrivialRC as a process manager... 62 | Terminated 63 | Terminated 64 | Terminated 65 | 2017-03-06 23:46:43 trc [main/29871]: Trying to terminate sub-processes... 66 | 2017-03-06 23:46:43 trc [async/29931]: Exiting in the background (exitcode=143): /opt/app/trc.d/async.netmon 67 | 2017-03-06 23:46:43 trc [async/29929]: Exiting in the background (exitcode=0): /opt/app/trc.d/async.netlistener-logger 68 | 2017-03-06 23:46:43 trc [async/29933]: Exiting in the background (exitcode=0): /opt/app/trc.d/async.netmon-logger 69 | 2017-03-06 23:46:43 trc [async/29934]: Exiting in the background (exitcode=143): /opt/app/trc.d/async.netsender 70 | 2017-03-06 23:46:43 trc [async/29928]: Exiting in the background (exitcode=143): /opt/app/trc.d/async.netlistener 71 | 2017-03-06 23:46:43 trc [main/29871]: terminating the child process 72 | 2017-03-06 23:46:43 trc [halt/30183]: Running the shutdown script: /opt/app/trc.d/halt.remove-logs 73 | 2017-03-06 23:46:43 trc [main/29871]: Exiting from the shutdown script (exitcode=143): /opt/app/trc.d/halt.remove-logs 74 | 2017-03-06 23:46:43 trc [main/29871]: Exited (exitcode=143) 75 | Stopped The example of running TrivialRC as a process manager. 76 | ``` 77 | 78 | ## Conclusion 79 | 80 | You can notice that two processes were restarted automatically: 81 | 82 | ```bash 83 | 017-03-06 23:46:34 trc [async/29931]: Monitoring tool has stopped working! 84 | 2017-03-06 23:46:35 trc [async/29931]: Start monitoring TCP activity... 85 | 86 | 2017-03-06 23:46:37 trc [async/29928]: The network listener has stopped working! 87 | 2017-03-06 23:46:38 trc [async/29928]: Start a network listener on the port 1234... 88 | ``` 89 | More over, every single line is prefixed by the task name as `netlistener` or `netmon`. This makes possible to filter the stream by the task 90 | -------------------------------------------------------------------------------- /examples/process-manager/trc-example-procmgr.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=The example of running TrivialRC as a process manager 3 | After=network.target 4 | 5 | [Service] 6 | Type=simple 7 | Environment=RC_VERBOSE=true 8 | ExecStart=/usr/bin/trc --workdir /opt/app 9 | ExecStop=/bin/kill -TERM $MAINPID 10 | Restart=on-failure 11 | RestartSec=5s 12 | TimeoutStopSec=5s 13 | SuccessExitStatus=0 143 SIGTERM 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/async.netlistener: -------------------------------------------------------------------------------- 1 | # Do not react on errors, keep working 2 | trap 'true' ERR 3 | 4 | while true; do 5 | log "Start a network listener on the port 1234..." 6 | nc -kl 1234 &> ${NETLISTENER} 7 | warn "The network listener has stopped working!" 8 | sleep 1 9 | done 10 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/async.netlistener-logger: -------------------------------------------------------------------------------- 1 | # Do not react on errors, keep working 2 | trap 'true' ERR 3 | local _str 4 | exec 6<>${NETLISTENER} 5 | 6 | while read -u 6 _str; do 7 | echo "netlistener: ${_str}" 8 | done 9 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/async.netmon: -------------------------------------------------------------------------------- 1 | # Do not react on errors, keep working 2 | trap 'true' ERR 3 | 4 | while true; do 5 | log "Start monitoring TCP activity..." 6 | ss -Etn &> ${NETMON} 7 | warn "Monitoring tool has stopped working!" 8 | sleep 1 9 | done 10 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/async.netmon-logger: -------------------------------------------------------------------------------- 1 | # Do not react on errors, keep working 2 | trap 'true' ERR 3 | local _str 4 | exec 5<>${NETMON} 5 | 6 | while read -u 5 _str; do 7 | echo "netmon: ${_str}" 8 | done 9 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/async.netsender: -------------------------------------------------------------------------------- 1 | # Do not react on errors, keep working 2 | trap 'true' ERR 3 | 4 | while true; do 5 | nc localhost 1234 <<< "$(date)" &>/dev/null 6 | sleep 1 7 | done 8 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/boot.create-logs: -------------------------------------------------------------------------------- 1 | export NETMON="/tmp/netmon.pipe" 2 | export NETLISTENER="/tmp/netlistener.pipe" 3 | mkfifo ${NETMON} 4 | mkfifo ${NETLISTENER} 5 | -------------------------------------------------------------------------------- /examples/process-manager/trc.d/halt.remove-logs: -------------------------------------------------------------------------------- 1 | # Do not fail on errors in halt stage 2 | set +e 3 | 4 | rm -f ${NETMON} ${NETLISTENER} 5 | 6 | # Finishing with the original exit status 7 | exit ${_exit_status} 8 | -------------------------------------------------------------------------------- /examples/reliable-tests-for-docker-images/README.md: -------------------------------------------------------------------------------- 1 | # Reliable tests for docker images 2 | 3 | If you have automated builds of docker images, you need to be sure that these images are not only built successfuly but also run services. Sometimes, services fail immediately after they were started up in a container because of different reasons like segmentation fault, lack of some libraries because they were moved by an upstream package, wrong system permissions or something else that is hard to be predicted and cannot be noticed without actually running a service and testing that it is up and fully functional. 4 | 5 | This is an example of how services in containers can be reliably tested without any extra efforts by adding a test process in the background which runs all necessary tests and then, checking the exit status on exit. 6 | 7 | As an example docker image I'm going to use [OpenSMTPD](https://github.com/vorakl/docker-images/tree/master/centos-opensmtpd) service image. Then, inject a background test command. In case of a success test, it will send a specific signal to the main process and cause (because a default wait policy is `wait_any`) a stop the whole container. On exit, I'll inject a `halt` command to check if the main process has cought my signal (128 + 10 = 138). If yes, I'll rewrite an exit status to 0 (success). Otherwise, the script will finish with some other error. 8 | 9 | So many words but in fact it looks much more simple. 10 | The container with a service resides in [vorakl/centos-opensmtpd](https://hub.docker.com/r/vorakl/centos-opensmtpd/) and can be simply started as 11 | 12 | ```bash 13 | $ docker run -d --name smtpd --net host vorakl/centos-opensmtpd 14 | ``` 15 | 16 | Let's add two additional commands to test the service and check the result: 17 | 18 | * async: `-D 'sleep 3; smtpctl show status && kill -10 ${MAINPID}'` 19 | * halt: `-H 'if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi'` 20 | 21 | That's how it looks like: 22 | 23 | ```bash 24 | $ docker run --rm vorakl/centos-opensmtpd -H 'if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi' -D 'sleep 3; smtpctl show status && kill -10 ${MAINPID}' 25 | 26 | 2017-03-16 21:40:50 trc [main/1]: The wait policy: wait_any 27 | 2017-03-16 21:40:50 trc [async/15]: Launching in the background: /etc/trc.d/async.opensmtpd 28 | 2017-03-16 21:40:50 trc [async/16]: Launching in the background: sleep 3; smtpctl show status && kill -10 ${MAINPID} 29 | info: OpenSMTPD 6.0.2p1 starting 30 | setup_peer: klondike -> control[28] fd=4 31 | setup_peer: klondike -> pony express[30] fd=5 32 | setup_done: ca[27] done 33 | setup_proc: klondike done 34 | setup_peer: control -> klondike[27] fd=5 35 | setup_peer: control -> lookup[29] fd=6 36 | setup_peer: control -> pony express[30] fd=7 37 | setup_peer: control -> queue[31] fd=8 38 | setup_peer: control -> scheduler[32] fd=9 39 | setup_done: control[28] done 40 | setup_proc: control done 41 | setup_peer: scheduler -> control[28] fd=9 42 | setup_peer: scheduler -> queue[31] fd=10 43 | setup_peer: lookup -> control[28] fd=6 44 | setup_peer: lookup -> pony express[30] fd=7 45 | setup_peer: lookup -> queue[31] fd=8 46 | setup_done: lka[29] done 47 | setup_proc: lookup done 48 | setup_peer: pony express -> control[28] fd=7 49 | setup_peer: queue -> control[28] fd=8 50 | setup_peer: pony express -> klondike[27] fd=8 51 | setup_peer: queue -> pony express[30] fd=9 52 | setup_peer: queue -> lookup[29] fd=10 53 | setup_peer: queue -> scheduler[32] fd=11 54 | setup_peer: pony express -> lookup[29] fd=9 55 | setup_peer: pony express -> queue[31] fd=10 56 | setup_done: pony[30] done 57 | setup_proc: pony express done 58 | setup_done: queue[31] done 59 | setup_proc: scheduler done 60 | setup_done: scheduler[32] done 61 | smtpd: setup done 62 | warn: purge_task: opendir: No such file or directory 63 | setup_proc: queue done 64 | MDA running 65 | MTA running 66 | SMTP running 67 | 2017-03-16 21:40:53 trc [async/16]: Exiting in the background (exitcode=0): sleep 3; smtpctl show status && kill -10 ${MAINPID} 68 | 2017-03-16 21:40:53 trc [main/1]: Trying to terminate sub-processes... 69 | 2017-03-16 21:40:53 trc [main/1]: terminating the child process 70 | info: Terminated, shutting down 71 | info: control process exiting 72 | info: ca agent exiting 73 | info: pony agent exiting 74 | info: queue handler exiting 75 | info: lookup agent exiting 76 | info: scheduler handler exiting 77 | warn: parent terminating 78 | 2017-03-16 21:40:53 trc [async/15]: Exiting in the background (exitcode=0): /etc/trc.d/async.opensmtpd 79 | 2017-03-16 21:40:54 trc [halt/60]: Running the shutdown command: if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi 80 | 2017-03-16 21:40:54 trc [main/1]: Exiting from the shutdown command (exitcode=0): if [[ ${_exit_status} -eq 138 ]]; then exit 0; else exit ${_exit_status}; fi 81 | 2017-03-16 21:40:54 trc [main/1]: Exited (exitcode=0) 82 | ``` 83 | -------------------------------------------------------------------------------- /examples/self-configuring/README.md: -------------------------------------------------------------------------------- 1 | # In this tricky example will be shown how to create configuration on the fly 2 | 3 | In the begging, it has only the `boot` stage. 4 | As long as scripts in this stages run in the same environment with the main process, it can bootstrap itself by creating missing configuration. There to ways: 5 | * create configuration files 6 | * modify command line 7 | 8 | In this example, will be shown how to modify a command line, to print out the famous "Hello, World!" 9 | Because the whole bootstrapping logic will reside in one boot file, there are no reasons to create a configuration directory. 10 | That's why there is only one file `trc.boot.make-self-conf`. 11 | And don't forget to change a wait policy, otherwise you won't see the result of executing of all commands! 12 | Let's run it: 13 | 14 | ```bash 15 | $ RC_WAIT_POLICY=wait_all trc -w . 16 | Hello World 17 | ``` 18 | -------------------------------------------------------------------------------- /examples/self-configuring/trc.boot.make-self-conf: -------------------------------------------------------------------------------- 1 | set -- "$@" -F 'echo -n "Hello "' -F 'echo "World"' 2 | -------------------------------------------------------------------------------- /examples/sync-run-of-async-groups/README.md: -------------------------------------------------------------------------------- 1 | # Serial launching of group of parallel processes and fails immediately if some group failed 2 | 3 | This example uses the idea of ["create configuration on the fly"](https://github.com/vorakl/TrivialRC/tree/master/examples/self-configuring) example. The trick is to run one TrivialRC with one wait policy a group of other TrivialRCs with another wait policy. The goal of a first copy of TrivialRC is: 4 | 5 | * to construct a proper list of sync tasks 6 | * itereate over the list checking exit status after each task (because this trc is run with 'wait_err' wait policy) 7 | 8 | Each sync task runs another copy of TrivialRC with another wait policy ('wait_all') to iterate over a smaller group of IP address (or other parameters from a command line) by running commands in parallel. All final commands will be forms at the `boot` stage in the same way. 9 | 10 | So, it would look like: 11 | 12 | ```bash 13 | RC_WAIT_POLICY=wait_err trc --workdir . 14 | \- RC_WAIT_POLICY=wait_all trc --workdir sub-trc IP1 IP2 IP3 ... IP10 15 | a \- cmd IP1 16 | s \- cmd IP2 17 | y \- cmd IP3 18 | n \- ....... 19 | c \- cmd IP10 20 | s \- ..... 21 | y \- ..... 22 | n \- ..... 23 | c \- RC_WAIT_POLICY=wait_all trc --workdir sub-trc IP1 IP2 IP3 ... IP10 24 | a \- cmd IP1 25 | s \- cmd IP2 26 | y \- cmd IP3 27 | n \- ....... 28 | c \- cmd IP10 29 | ``` 30 | The main idea is to have only boot scripts for bootstrapping subsequent behavior of the first TrivialRC copy by 31 | running one by one another copy of TrivialRC which eventually runs commands in parallel (one command per one IP). 32 | 33 | A number of parallel tasks and a command can be changed by environment variables ASYNC_TASKS and TASK_CMD accordingly. 34 | 35 | ## Test 36 | 37 | ```bash 38 | $ RC_WAIT_POLICY=wait_err trc -w . 39 | 40 | $ TASK_CMD="uname -a" RC_WAIT_POLICY=wait_err trc -w . 41 | 42 | $ ASYNC_TASKS=20 TASK_CMD="id -un" RC_WAIT_POLICY=wait_err trc -w . 43 | ``` 44 | -------------------------------------------------------------------------------- /examples/sync-run-of-async-groups/sub-trc/trc.boot.async-run-cmds: -------------------------------------------------------------------------------- 1 | local _ip _params 2 | 3 | for _ip in "$@"; do 4 | _params="${_params}-D 'ssh -i admin.key -o StrictHostKeyChecking=no admin@${_ip} ${TASK_CMD:-uptime}' " 5 | done 6 | 7 | # replaces all command line parameters 8 | eval set -- ${_params} 9 | -------------------------------------------------------------------------------- /examples/sync-run-of-async-groups/trc.boot.serial-run-groups: -------------------------------------------------------------------------------- 1 | # The script gets IP address, 2 | # converts them to lines by ${ASYNC_TASKS} elements (or 10 by default), 3 | # iterates over the list and for each new line 4 | # forms a part of a future command line for itself and then 5 | # updates its own command line to have a number of sync process when 6 | # it has moved to the next stage. 7 | 8 | local _ips _params 9 | 10 | while read _ips; do 11 | _params="${_params}-F 'RC_WAIT_POLICY=wait_all trc -w sub-trc ${_ips}' " 12 | done < <( curl -sSLf 'http://service-discovery.domain.com/api/nodes' | \ 13 | jq -r '.[] | select(.ipaddr != null) | .ipaddr' | \ 14 | xargs -n ${ASYNC_TASKS:-10} echo ) 15 | 16 | eval set -- "$@" ${_params} 17 | -------------------------------------------------------------------------------- /src.docs/.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__/ 2 | py3/ 3 | *.pyc 4 | -------------------------------------------------------------------------------- /src.docs/Makefile: -------------------------------------------------------------------------------- 1 | PY?=python 2 | PELICAN?=pelican 3 | PELICANOPTS= 4 | 5 | BASEDIR=$(CURDIR) 6 | INPUTDIR=$(BASEDIR)/content 7 | OUTPUTDIR=$(shell readlink -f ../docs) 8 | STATICDIR=$(BASEDIR)/theme/static 9 | THEMEDIR=/home/vorakl/repos/my/github/aves/theme 10 | PLUGINSDIR=/home/vorakl/repos/others/pelican-plugins 11 | CONFFILE=pelicanconf.py 12 | PUBLISHCONF=publishconf.py 13 | 14 | UID=$(shell id -u) 15 | GID=$(shell id -g) 16 | 17 | GITHUB_PAGES_BRANCH=master 18 | 19 | PORT ?= 8000 20 | DEBUG ?= 0 21 | ifeq ($(DEBUG), 1) 22 | PELICANOPTS += -D 23 | endif 24 | 25 | RELATIVE ?= 0 26 | ifeq ($(RELATIVE), 1) 27 | PELICANOPTS += --relative-urls 28 | endif 29 | 30 | .PHONY: html help serve publish github 31 | 32 | help: 33 | @echo 'Makefile for a pelican Web site ' 34 | @echo ' ' 35 | @echo 'Usage: ' 36 | @echo ' make html (re)generate the web site ' 37 | @echo ' make publish generate using production settings ' 38 | @echo ' make bundle generate bundled version of CSS and JS ' 39 | @echo ' make serve [PORT=8000] serve site at http://localhost:8000/ ' 40 | @echo ' make github upload the web site via gh-pages ' 41 | @echo ' ' 42 | @echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html ' 43 | @echo 'Set the RELATIVE variable to 1 to enable relative urls ' 44 | @echo ' ' 45 | 46 | html: 47 | @if docker version &>/dev/null; then \ 48 | echo 'Building a local web-site using docker image'; \ 49 | docker run \ 50 | --rm \ 51 | -u $(UID):$(GID) \ 52 | -v $(BASEDIR):/site \ 53 | -v $(INPUTDIR):/input \ 54 | -v $(OUTPUTDIR):/output \ 55 | -v $(THEMEDIR):/theme \ 56 | -v $(PLUGINSDIR):/plugins \ 57 | vorakl/alpine-pelican \ 58 | -F 'cd /site && pelican /input -o /output -t /theme -s $(CONFFILE) $(PELICANOPTS)'; \ 59 | else \ 60 | echo 'Building a local web-site using a local Pelican'; \ 61 | @$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(BASEDIR)/$(CONFFILE) $(PELICANOPTS); \ 62 | fi 63 | 64 | bundle: 65 | @cd $(STATICDIR) && \ 66 | cat bootstrap.min.css bootstrap.min.responsive.css local.min.css pygments.min.css > bootstrap-pygments.bundle.min.css && \ 67 | cat jquery.min.js bootstrap-collapse.min.js > jquery-bootstrap-collapse.bundle.min.js 68 | 69 | serve: 70 | @if docker version &>/dev/null; then \ 71 | echo 'Launching a docker container with Nginx on the port $(PORT)'; \ 72 | docker run \ 73 | --rm \ 74 | -it \ 75 | -p $(PORT):80 \ 76 | -v $(OUTPUTDIR):/usr/share/nginx/html \ 77 | nginx:mainline-alpine; \ 78 | else \ 79 | echo 'Launching Pelican Server on port $(PORT)'; \ 80 | cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT); \ 81 | fi 82 | 83 | publish: 84 | @if docker version &>/dev/null; then \ 85 | echo 'Building a public web-site using docker image'; \ 86 | docker run \ 87 | --rm \ 88 | -u $(UID):$(GID) \ 89 | -v $(BASEDIR):/site \ 90 | -v $(INPUTDIR):/input \ 91 | -v $(OUTPUTDIR):/output \ 92 | -v $(THEMEDIR):/theme \ 93 | -v $(PLUGINSDIR):/plugins \ 94 | vorakl/alpine-pelican \ 95 | -F 'cd /site && pelican /input -o /output -t /theme -s $(PUBLISHCONF) $(PELICANOPTS)'; \ 96 | else \ 97 | echo 'Building a public web-site using a local Pelican'; \ 98 | @$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(BASEDIR)/$(PUBLISHCONF) $(PELICANOPTS); \ 99 | fi 100 | 101 | github: publish 102 | @cd $(OUTPUTDIR) && \ 103 | git add . && \ 104 | git commit -m "New content" && \ 105 | git push origin $(GITHUB_PAGES_BRANCH) 106 | 107 | -------------------------------------------------------------------------------- /src.docs/content/images/trivialrc.xml: -------------------------------------------------------------------------------- 1 | 3V1bc6M2FP41nmlfPAhxfdxNt22mu9tMtzO7ecoQm7XpYssDuE766wtGwnCOiAnIwiEPiS0EiI/vXHTOkTKjN5un35Jgt/7ElmE8M43l04z+MjNNQnwz/1O0PJctjmmVDaskWvJOp4Yv0X8hbzR46z5ahmmjY8ZYnEW7ZuOCbbfhImu0BUnCDs1u31ncvOsuWIWo4csiiHHr12iZrcVzGcbpwO9htFrzW3s2P/AYLH6sErbf8vvNTPr9+FMe3gTiWrx/ug6W7FBroh9m9CZhLCs/bZ5uwrjAVsBWnvdry9Fq3Em4zbqcYPEXlWbP4tnDZQ4F/8qSbM1WbBvEH06t74/PFxZXMPJv62wT5x9J/jG/afL8rWif2+LrPe/2T5hlz/xNB/uM5U2nq39kbMevkWYJ+1GhTvOWcoTFsFofkjelbJ8sRC+H8yZIViHvZtIK4py6IduE+QDzPkkYB1n0b/P6AefQqup3wjH/wKFseQ/85v8G8Z5fNc0HkmGw4zjndAHqYR1l4ZddcBz/IRerJrT8emGShU8vw4Afj59g2eUZXCYtTsHDieAub1rXqC2YOgQO078MyxocO1JON8tKRgGWWbpYRpVKbz/siFLsCG3hsHLwxN1rIppLVxqx7cx04nw47x+T/NMqO8KA29ZhvEPoJ2u2edynesTZaIizh8XZk4izp0CcLe/ajEZPAlqYgJariX/2FcjuAL0ngc60dYmuhUT3wJIfhb8ZJaPKJPFHE0pK0JO/JT7ZEj45mvjkXJ0TrA5ER5s9tZFQPhbTmDHFkY5nIz37Mpy6ApdXMrHyDPl7UM4y/wq03AABHarR+Kl3LMrvUtHcNps0Nx1A4PJV8bNOYL9LkuC51m1XdEjR66gG2k0P4KnvOojH1QOWN55ZppPVA67EZHua9AC5VEhhbhEXWu3jl7swifJhhskrTbky3SGB29flIYmb14T6LmGLME3xxPhTsA1W4bh+uDee4SdE6exYO82GSrDcRBGDABvldrNRfSyQh8j6mD/0qIT03fEsEJ4tf2YIjPzRsuZTl4y6YTErVN6WbQuefo/iGDQFcbTa5l8XORSFdnxfABUtgvgdP7CJlssjyWUwK8CWAGpVWZoauJYEXBWRbYpnPdMCt/IlxwAXu5LTAtcak7nYpE8LXMcdEVxsgqYFrj8ic8V1G8lVhpMxY+ZWiY3guFRy1cJa8j5Mp0Q24MybOrmGtWQaFn4tnvdULacGq0hLPByTEuWx/Ha1w9AfbcwB9CcrKlN/xim1FeBqYwU5MdIidDWy1vYRulg7bpfvikqpAqM4SNNoAUIiT1H2rfb5FGia9ZpYtiVWa3DYL5Bt4PwTurEUwlwOFU0/0YWoAQyfgLZ3rLXbfURC4cSA8op9J8mEKA2g6Q5TOEMTDh3DFBbkibowhYPdGGRCwqdwIbcdEgMUzIqCyM0m2C5Hty0w3mG6tJttsYdrP4ecBzbZb1tw5QhiW6QbQpi81GmeHVwcNboBactk6zEgFiS0B5ja1YBYNjQgQ5N13e6j2oA4k592IAHU6ME5eN4xMXSRhdCIrjv1wCIxAHd1xmdcBbMPnKRuNydV8rbK5A41NSKD1CgMbfE49Zgf/5zV6Gp+4IUsmGJSZH6I4clH3DYwdIJlqjVYnoNZOI1KDAFcoyJLV92fuHlN2oP0ebtAYOtMtzpg9qyz8M+dLM3ESrx6wY+ucnkPT+pGZ5lrjscybGOn5cHY0OBpdGB8rNJQAGHB4rhYeXrlQQRY+agziCAWAU93DoPQ1UlSBSEaHI3t4GUbc7exrqF/ENeX+S2+HHE9brbtNvO4lPR0sx2zeaHhJdktA4YMVBzl8fFMGanCQxC16UFJkHqxYMky2q7i4jFzpyX/tQ7rXWvp079uHr6+u/374e7Pj7c391eZRPUosFM+VgHVZK3OWEiIXjoAR4neYqTbBKlSm+gzUsTAjuXErBScDOm0UsTABZITgxfWmhOJi3UxeAlm7+irbSAgtKM4K6m+F6hexi0yFOWy+Hsbz80BoTajp5tDQFlF13T5q6OJsHzDbgQTh1dDmFiOpjWf9kBhA6UatZRkwb4CoewqemNJGXJrRFXwYCmjeqRMDFidlGFnQKFy1pEZqgxuYxneqKVtDjC3Tt/UkAP52nGJ1mtp5r+szM/2F/5sa4WFMay/TRWX2kmWRE/MB4bLAajIoGqxLrJZsDqXT6wI5pqFzA0QDkO7fBRd4XLhfurGl+y1MLLv6PnwVffNRMPymsutCCXCYNbXL9/mv8Xkv8aU9BBt4uAoVk1SmGpEBS2cQYJyyoTXX59lqZAViv3cXZID/MD3Pvvp5267n5Un7dNgFRanwNmm+gASbSm/EL4WCLdaeP2NLwFVRRCOiD0037R6P4OvC2krCXNeSsF7E1hMdw5eoFQ1oksk68eqvR2Mts0ckJYUmM6KlQvFj1R/LoN0fdQLygTfsmnTHGHkqFjEVYeOqFGnF9rdRdX2uf1zc5Wl6FIDd4GdTSWkPBrsb/P5vXabDc1LVQxeY5kplnw2WAbzh/3AkOy+cgTjdj7/QzsYljsmGL4slHSVKS/aEhUQs+2zuzFfLOUlWZ1e8unzfP5JO59cMiqfZCGp6+RTy848V8An5QXfzQm4XZ9+z14uiuxn7WQVtH7LNt6aUjQgKOX2nWbDQjrnQlE9GIYUCYbWcZnD+iuP0km2lVDIYlWr4MZlpQtZ2bc+Cl7oUqvgHJTSsF8cF+x/jpXwOUB/BayULWZ9W7pVljFp2SpcE4tB0ZwLrWBnFiN2XYbF+D6qdZ/mYtKeum9U1sAchwuDh11Z45l6dJ/nvU73wf5id7KuzwH6v5aV+dfT/z0qu5/+uRT98D8= -------------------------------------------------------------------------------- /src.docs/content/pages/404.rst: -------------------------------------------------------------------------------- 1 | Not Found 2 | ######### 3 | 4 | :status: hidden 5 | :save_as: 404.html 6 | :slug: 404 7 | 8 | The requested item could not be located. 9 | -------------------------------------------------------------------------------- /src.docs/content/pages/info.rst: -------------------------------------------------------------------------------- 1 | 2 | The minimalistic Run-time Configuration (RC) system and process manager 3 | ####################################################################### 4 | 5 | :slug: info 6 | :summary: The minimalistic Run-time Configuration (RC) system and process manager 7 | 8 | | 9 | 10 | * Community_ 11 | * Introduction_ 12 | * Installation_ 13 | * `The installation on top of CentOS Linux base image`_ 14 | * `The installation on top of Alpine Linux base image`_ 15 | * `How to get started?`_ 16 | * `Command line options`_ 17 | * `Run stages`_ 18 | * `Wait policies`_ 19 | * `Verbose levels`_ 20 | * `Integrated functions`_ 21 | * `Useful global variables`_ 22 | 23 | | 24 | 25 | Community 26 | ========= 27 | 28 | There are a few options for the communication: 29 | 30 | * ``IRC``: #trivialrc channel on the OFTC network (irc.oftc.net) 31 | * ``Mailing list``: trivialrc-dev@freelists.org, subscribe_ 32 | 33 | | 34 | 35 | Introduction 36 | ============ 37 | 38 | The minimalistic Run-time Configuration (RC) system and process manager is written in pure BASH and uses just a few external utilities like ``ls``, ``ps``, ``date`` and ``sleep``. Minimally, installation of TrivialRC consists of only one file which can be downloaded directly from the Github. Originaly, it was designed for use in containers but it also can be well used for running a group of processes asynchronously and synchronously, as well as managing their running order and exit codes. 39 | 40 | TrivialRC is not a replacement for an init process that usually resides in ``/sbin/init`` and has a PID 1. In containers for this purpose projects like dumb-init_ or tini_ can be used, although in most cases, having only TrivialRC as a first/main process (PID 1) in containers is quite enough. In terms of Docker, the best place for it is ENTRYPOINT. 41 | 42 | TrivialRC is an equivalent to well known ``/etc/rc`` for xBSD users. The RC system that is used for managing startup and shutdown processes. It can start and stop one or more processes, in parallel or sequentially, on back- or foreground, react differently in case of process failures, etc. All commands can be specified in the command line if they are relatively simple, or in separate files if a more comprehensive scenario is needed. That's why it can be used as a simplest tool for managing a group of process and be a lightweight replacement for solutions like Supervisor_. 43 | 44 | For instance, in docker images when TrivialRC is used as an Entrypoint, it doesn't reveal itself by default, does not affect any configuration and behaves absolutely transparently. So, you can add it into any Dockerfiles which do not have an entrypoint yet and get by this the full control under processes with fairly detailed logs of what's is going on inside a container. Please, take a look at examples__ for more information. 45 | 46 | __ https://github.com/vorakl/TrivialRC/tree/master/examples 47 | 48 | | 49 | 50 | Installation 51 | ============ 52 | 53 | Basically, all you need to install TrivialRC is download the latest release of the script from ``http://trivialrc.vorakl.com/trc`` 54 | and give it an execute permission. By default, it looks for configuration files in the same directory from which it was invoked but this behavior can be changed by setting a work directory (``-w|--workdir`` parametr). So, if you are going to use configuration files and keep them in ``/etc/``, then you would probably want to install the script to /etc/ as well and simply run it without specifying any parametrs. 55 | 56 | Another option in this case could be to install the script in a more appropriate path but don't forget to specify ``--workdir /etc`` parametr every time when you invoke this rc system. Both options are possible and depend more on a particular use case. 57 | For instance, in case of using in a docker container, I personaly prefer to have all configuration in separate files in ``trc.d/`` sub-directory and copy it together with the script in ``/etc/``. 58 | 59 | | 60 | 61 | The installation on top of CentOS Linux base image 62 | -------------------------------------------------- 63 | 64 | This is an example of how it would look in a Dockerfile with `centos:latest`_ as base image: 65 | 66 | .. code-block:: bash 67 | 68 | FROM centos:latest 69 | 70 | RUN curl -sSLfo /etc/trc http://trivialrc.vorakl.com/trc && \ 71 | ( cd /etc && curl -sSLf http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \ 72 | chmod +x /etc/trc && \ 73 | /etc/trc --version 74 | 75 | # Uncomment this if you have configuration files in trc.d/ 76 | # COPY trc.d/ /etc/trc.d/ 77 | 78 | ENTRYPOINT ["/etc/trc"] 79 | 80 | | 81 | 82 | The installation on top of Alpine Linux base image 83 | -------------------------------------------------- 84 | 85 | **Attention**! The Alpine Linux comes with Busybox but its functionality as a shell and as a few emulated tools ``is not enough`` for TrivialRC. To work in this distribution it requires two extra packages: ``bash`` and ``procps``. 86 | As a result, Dockerfile for the `alpine:latest`_ base image would look like: 87 | 88 | .. code-block:: bash 89 | 90 | FROM alpine:latest 91 | 92 | RUN apk add --no-cache bash procps 93 | 94 | RUN wget -qP /etc/ http://trivialrc.vorakl.com/trc && \ 95 | ( cd /etc && wget -qO - http://trivialrc.vorakl.com/trc.sha256 | sha256sum -c ) && \ 96 | chmod +x /etc/trc && \ 97 | /etc/trc --version 98 | 99 | # Uncomment this if you have configuration files in trc.d/ 100 | # COPY trc.d/ /etc/trc.d/ 101 | 102 | ENTRYPOINT ["/etc/trc"] 103 | 104 | | 105 | 106 | How to get started? 107 | =================== 108 | 109 | To get started and find out some features, basically, I suggest to go through `all available examples`_ and read their readmes plus comments along the code but to start from `one-liners`_ which show most common use cases and features. 110 | 111 | | 112 | 113 | Command line options 114 | ==================== 115 | 116 | It is important to notice that the order of command line options **is not** equal to their run order. 117 | In general it looks like: 118 | 119 | .. code-block:: bash 120 | 121 | $ trc [-h|--help] [-v|--version] [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [command [args]] 122 | 123 | 124 | Where 125 | 126 | * ``-h`` or ``--help``, prints a short help message 127 | * ``-v`` or ``--version``, prints a current version 128 | * ``-w 'directory'`` or ``--workdir 'directory'``, sets a location with configuration files 129 | * ``-B 'command1; command2; ...'``, boot commands 130 | * ``-H 'command1; command2; ...'``, halt commands 131 | * ``-D 'command1; command2; ...'``, async commands 132 | * ``-F 'command1; command2; ...'``, sync commands 133 | * ``command [args]``, a sync command 134 | 135 | So, command line options have to be supplied in the next order 136 | 137 | 1. ``-B``, zero or more 138 | 2. ``-H``, zero or more 139 | 3. ``-D``, zero or more 140 | 4. ``-F``, zero or more 141 | 5. ``command with arguments`` (without an option), zero or only one 142 | 143 | Examples: 144 | 145 | .. code-block:: bash 146 | 147 | $ trc -B 'name=$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, ${name}!' 148 | 149 | $ RC_WAIT_POLICY=wait_all trc -D 'echo Hello' -D 'sleep 2; echo World' echo waiting... 150 | 151 | $ RC_VERBOSE=true trc -F 'echo -n "Hello "; echo World' 152 | 153 | $ trc --workdir /opt/app 154 | 155 | | 156 | 157 | Run stages 158 | ========== 159 | 160 | The life cycle of TrivialRC consists of different stages, with different isolation. 161 | By default, all configuration files (or trc.d/ directory with them) are searched in the directory from which was executed ``trc`` itself. For instance, if you've installed trc in /usr/bin/ and run it by using only its name, like ``trc``, then configuration will also be searched in /usr/bin/. Though, you can place configuration files anywhere you like and specify their location in the ``-w|--workdir`` option, like ``trc -w /etc/``. 162 | 163 | Let's check: 164 | 165 | .. code-block:: bash 166 | 167 | $ which trc 168 | /usr/bin/trc 169 | 170 | $ trc -B 'echo $work_dir' 171 | /usr/bin 172 | 173 | $ trc -w /etc -B 'echo $work_dir' 174 | /etc 175 | 176 | 177 | All stages are executed through in the next order: 178 | 179 | 1. ``boot`` 180 | **Execution order**: trc.boot.* -> trc.d/boot.* -> [-B 'cmds' [...]] 181 | 182 | Commands run in a same environment as the main process and that's why it has to be used with caution. 183 | It's useful for setting up global variables which are seen in all other isolated environments. 184 | 2. ``async`` 185 | **Execution order**: trc.async.* -> trc.d/async.* -> [-D 'cmds' [...]] 186 | 187 | Commands run in the separate environment, asynchronously (all run in parallel), in the background and do not affect the main process. 188 | If you are going to run more than one async commands, don't forget that default RC_WAIT_POLICY is set to 'wait_any' and the executing process will be stopped after the first finished command and only if there wasn't any running foreground (sync) command that could block the reaction on the TERM signal. So, there are two options: 189 | 190 | * to wait until all async commands have finished, you need to set RC_WAIT_POLICY to 'wait_all'. 191 | * to wait for the first finished command, do not change the default value of RC_WAIT_POLICY but run only async commands. 192 | 3. ``sync`` 193 | **Execution order**: trc.sync.* -> trc.d/sync.* -> [-F 'cmds' [...]] -> [cmd] 194 | 195 | Commands run in the separate environment, synchronously (one by one), in the foreground and do not affect the main process. 196 | if you are going to run more than one sync commands, don't forget to change RC_WAIT_POLICY to 'wait_all' or 'wait_err', otherwise, the executing process will be stopped after the first command. 197 | 4. ``halt`` 198 | **Execution order**: trc.halt.* -> trc.d/halt.* -> [-H 'cmds' [...]] 199 | 200 | Commands run in the separate environment, synchronously (one by one) when the main process is finishing (on exit). 201 | An exit status from the last halt command has precedence under an exit status from the main process which was supplied as ${_exit_status} variable. So you are able to keep a main exit status (by finishing as **exit ${_exit_status}**) or rewrite it to something else but anyway, if you have at least one halt command, TrivialRC will finish with an exit status of this halt command. 202 | 203 | | 204 | 205 | Wait policies 206 | ============= 207 | 208 | The rc system reacts differently when one of controlled processes finishes. 209 | Depending on the value of **RC_WAIT_POLICY** environment variable it makes a decision when exactly it should stop itself. 210 | The possible values are: 211 | 212 | * ``wait_all`` 213 | stops after exiting all commands and it doesn't matter whether they are synchronous or asynchronous. Just keep in mind, if you need to catch a signal in the main process, it doesn't have to be blocked by some foreground (sync) process. For example, this mode can be helpful if you need to troubleshoot a container (with `wait_any` policy) where some async task fails and the whole container gets stopped by this immediately. In this case, you can change a policy to `wait_all` and run BASH in the foreground like ``docker -e RC_WAIT_POLICY=wait_all some-container bash`` 214 | * ``wait_any`` [default] 215 | stops after exiting any of background commands and if there are no foreground commands working at that moment. It makes sense to use this mode if all commands are **asynchronous** (background). For example, if you need to start more than one process in the docker container, they all have to be asynchronous. Then, the main processed will be able to catch signals (for instance, from a docker daemon) and wait for finishing all other async processes. 216 | * ``wait_err`` 217 | stops after the first failed command. It make sense to use this mode with **synchronous** (foreground) commands only. For example, if you need to iterate sequentially over the list of commands and to stop only if one of them has failed. 218 | * ``wait_forever`` 219 | there is a special occasion when a process has doubled forked to become a daemon, it's still running but for the parent shell such process is considered as finished. So, in this mode, TrivialRC will keep working even if all processes have finished and it has to be stopped by the signal from its parent process (such as docker daemon for example). 220 | 221 | | 222 | 223 | Verbose levels 224 | ============== 225 | 226 | By default, TrivailRC doesn't print any service messages at all. 227 | It only sends ``stdout`` and ``stderr`` of all isolated sub-shells to the same terminal. 228 | If another behavior is needed, you can redirect any of them inside each sub-shell separately. 229 | To increase the verbosity of rc system there are provided a few environment variables: 230 | 231 | * ``RC_DEBUG`` (true|false) [false] 232 | Prints out all commands which are being executed 233 | * ``RC_VERBOSE`` (true|false) [false] 234 | Prints out service information 235 | * ``RC_VERBOSE_EXTRA`` (true|false) [false] 236 | Prints out additional service information 237 | 238 | | 239 | 240 | Integrated functions 241 | ==================== 242 | 243 | You can also use some of internal functions in async/sync tasks: 244 | 245 | * ``say`` 246 | prints only if RC_VERBOSE is set 247 | * ``log`` 248 | does the same as ``say`` but add additional info about time, PID, namespace, etc 249 | * ``warn`` 250 | does the say as ``log`` but sends a mesage to stderr 251 | * ``err`` 252 | does the same as ``warn`` but exits with an error (exit status = 1) 253 | * ``debug`` 254 | does the same as ``log`` but only if RC_VERBOSE_EXTRA is set 255 | * ``run`` 256 | launches builtin or external commands without checking functions with the same name 257 | For instance, if you wanna run only external command from the standart PATH list, use ``run -p 'command'`` 258 | Or, if you need to check existence of the command, try ``run -v 'command'`` 259 | 260 | | 261 | 262 | Useful global variables 263 | ======================= 264 | 265 | * ``MAINPID``, for sending signals to the main process (see `Testing of Docker images`_) 266 | * ``_exit_status``, for checking or rewriting an exit status of the whole script (see `Process Manager`_, `Service Discovery`_) 267 | 268 | .. Links 269 | 270 | .. |run-stages| image:: trivialrc-stages.svg 271 | :scale: 50 272 | :alt: Run Stages 273 | :align: middle 274 | .. _dumb-init: https://github.com/Yelp/dumb-init 275 | .. _tini: https://github.com/krallin/tini 276 | .. _Supervisor: https://github.com/Supervisor/supervisor 277 | .. _`centos:latest`: https://hub.docker.com/_/centos/ 278 | .. _`alpine:latest`: https://hub.docker.com/_/alpine/ 279 | .. _`all available examples`: https://github.com/vorakl/TrivialRC/tree/master/examples 280 | .. _`one-liners`: https://github.com/vorakl/TrivialRC/blob/master/examples/one-liners 281 | .. _`Testing of Docker images`: https://github.com/vorakl/TrivialRC/tree/master/examples/reliable-tests-for-docker-images 282 | .. _`Process Manager`: https://github.com/vorakl/TrivialRC/blob/master/examples/process-manager/trc.d/halt.remove-logs 283 | .. _`Service Discovery`: https://github.com/vorakl/TrivialRC/blob/master/examples/docker-service-discovery/trc.d/halt.sd-unreg 284 | .. _subscribe: https://www.freelists.org/list/trivialrc-dev 285 | -------------------------------------------------------------------------------- /src.docs/content/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vorakl/TrivialRC/58144bec2f26eb9bcb00df6c0fbeb3b8f5aca02f/src.docs/content/static/favicon.ico -------------------------------------------------------------------------------- /src.docs/content/static/google747986e3861ca881.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google747986e3861ca881.html -------------------------------------------------------------------------------- /src.docs/content/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /unknown/ 3 | 4 | Sitemap: https://trivialrc.cf/sitemap.xml 5 | -------------------------------------------------------------------------------- /src.docs/pelicanconf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- # 3 | from __future__ import unicode_literals 4 | 5 | ### Basic configuration 6 | ######################## 7 | 8 | AUTHOR = u'vorakl' 9 | SITENAME = u"TrivialRC" 10 | SITEURL = 'https://trivialrc.vorakl.com' 11 | SITEDESC = u'The minimal Run-time Configuration (RC) system and process manager' 12 | SITE_VERSION = '1703025594' 13 | SITE_KEYWORDS = 'trivialrc,trc,rc system,process manager,rc' 14 | ARTICLE_PATHS = ['articles'] # a place for articles under the content location 15 | PAGE_PATHS = ['pages'] 16 | CONTACT_URL = 'https://vorakl.com/pages/about/' 17 | START_URL = 'pages/info/' # What's a start point of a site (like 'news/' or 'pages/about/')? 18 | TIMEZONE = 'America/Los_Angeles' 19 | DEFAULT_LANG = u'en' 20 | RELATIVE_URLS = True # disable in public version 21 | DEFAULT_DATE = 'fs' 22 | DEFAULT_DATE_FORMAT = '%Y-%m-%d' 23 | PLUGIN_PATHS = ['/plugins'] 24 | PLUGINS = ['post_stats', 'pelican_youtube', 'minify'] # keep 'minify' plugin as the last element in the list to minify all output HTMLs 25 | 26 | DEFAULT_PAGINATION = 10 # Turns on the pagination 27 | PAGINATION_PATTERNS = ( 28 | (1, '{base_name}/', '{base_name}/index.html'), 29 | (2, '{base_name}/p{number}/', '{base_name}/p{number}/index.html'), 30 | ) 31 | 32 | DELETE_OUTPUT_DIRECTORY = True # build an output dir from scratch every time 33 | OUTPUT_RETENTION = ["CNAME", "trc", "trc.sha256"] # but these dirs and files should be kept 34 | 35 | 36 | ### Interface configuration 37 | ############################ 38 | 39 | DISPLAY_MENU = True 40 | DISPLAY_PAGES_IN_MENU = False 41 | DISPLAY_CATEGORIES_ON_MENU = False 42 | DISPLAY_ITEMS_ON_MENU = True # Items are set in the MENUITEMS variable below 43 | 44 | DISPLAY_SIDEBAR = False 45 | DISPLAY_ARCHIVES_ON_SIDEBAR = False # It also turns on/off an appropriate section in a sitemap.xml 46 | DISPLAY_CATEGORIES_ON_SIDEBAR = False # It also turns on/off an appropriate section in a sitemap.xml 47 | DISPLAY_TAGS_ON_SIDEBAR = False # It also turns on/off an appropriate section in a sitemap.xml 48 | DISPLAY_PAGES_ON_SIDEBAR = True # It also turns on/off an appropriate section in a sitemap.xml 49 | DISPLAY_AUTHORS_ON_SIDEBAR = False # It's turned off because I'm the only one author on this site 50 | DISPLAY_SUBSCRIBES_ON_SIDEBAR = False 51 | DISPLAY_SITE_ON_SIDEBAR = False 52 | DISPLAY_LINKS_ON_SIDEBAR = False # Links are set in the LINKS variable below 53 | 54 | MENUITEMS = [ 55 | ("repo", "https://github.com/vorakl/TrivialRC"), 56 | ("blog", "https://vorakl.com/"), 57 | ("author", "https://vorakl.com/pages/about/"), 58 | ] 59 | #LINKS = [("Github", "https://github.com/vorakl"), ("LinkedIn", "https://linkedin.com/in/vorakl/")] 60 | DISPLAY_AUTHOR = False # Add an author in a article's metadata 61 | 62 | CATEGORIES_DESCRIPTION = {} 63 | TAGS_DESCRIPTION = {} 64 | 65 | ### Feed's specification 66 | ######################### 67 | 68 | FEED_EMAIL = None # disable in development version 69 | FEED_DOMAIN = '' # and create all feed under the local domain for testing purpose 70 | FEED_MAX_ITEMS = 15 71 | FEED_ALL_ATOM = '' 72 | FEED_ALL_RSS = None # Here is used the only one feed on Google's feedburner. All other feeds are disabled 73 | CATEGORY_FEED_ATOM = None 74 | CATEGORY_FEED_RSS = None 75 | TRANSLATION_FEED_ATOM = None 76 | AUTHOR_FEED_ATOM = None 77 | AUTHOR_FEED_RSS = None 78 | TAG_FEED_ATOM = None 79 | TAG_FEED_RSS = None 80 | 81 | 82 | ### Static files (non templates) 83 | ################################# 84 | 85 | STATIC_PATHS = [ 86 | 'static/robots.txt', 87 | 'static/google747986e3861ca881.html', 88 | 'static/favicon.ico', 89 | 'images', 90 | ] 91 | # and sprecial output paths for them 92 | EXTRA_PATH_METADATA = { 93 | 'static/robots.txt': {'path': 'robots.txt'}, 94 | 'static/google747986e3861ca881.html': {'path': 'google747986e3861ca881.html'}, 95 | 'static/favicon.ico': {'path': 'favicon.ico'}, 96 | } 97 | 98 | 99 | ### Templates for html pages 100 | ############################# 101 | 102 | # blog posts related pages 103 | 104 | # If there is a 'Save_as' metadata (like in 404.html), then a page will be rendered anyway 105 | ARTICLE_SAVE_AS = '' # activates rendering each article 106 | ARTICLE_URL = '{category}/{slug}/' 107 | ARTICLE_LANG_SAVE_AS = '' 108 | ARTICLE_LANG_URL = '{category}/{slug}-{lang}/' 109 | DRAFT_SAVE_AS = '' # activates rendering each article's draft 110 | DRAFT_URL = 'drafts/{category}/{slug}/' 111 | DRAFT_LANG_SAVE_AS = 'drafts/{category}/{slug}-{lang}/index.html' 112 | DRAFT_LANG_URL = 'drafts/{category}/{slug}-{lang}/' 113 | PAGE_SAVE_AS = 'pages/{slug}/index.html' # activates rendering each page. 114 | PAGE_URL = 'pages/{slug}/' 115 | PAGE_LANG_SAVE_AS = 'pages/{slug}-{lang}/index.html' 116 | PAGE_LANG_URL = 'pages/{slug}-{lang}/' 117 | CATEGORY_SAVE_AS = '' # activates rendering each category 118 | CATEGORY_URL = 'category/{slug}/' 119 | TAG_SAVE_AS = '' # activates rendering each tag 120 | TAG_URL = 'tag/{slug}/' 121 | AUTHOR_SAVE_AS = '' # activates rendering each author 122 | AUTHOR_URL = 'author/{slug}/' 123 | 124 | # site related pages 125 | 126 | # a list of templates for rendering blog posts. Not all of them, just an index and groups of entities (tags, categories, ...) 127 | # other templates for blog posts rendering (for a tag, a category, ...) are activated by *_SAVE_AS variables below 128 | DIRECT_TEMPLATES = [] 129 | PAGINATED_TEMPLATES = {'tag': None, 'category': None, 'author': None} 130 | 131 | INDEX_SAVE_AS = 'news/index.html' 132 | AUTHORS_SAVE_AS = 'author/index.html' # defines where to save an authors page, it's activated by DIRECT_TEMPLATES 133 | AUTHORS_URL = 'author/' 134 | ARCHIVES_SAVE_AS = 'archives/index.html' # defines where to save an archives page, it's activated by DIRECT_TEMPLATES 135 | ARCHIVES_URL = 'archives/' 136 | TAGS_SAVE_AS = 'tag/index.html' # defines where to save a tags page, it's activated by DIRECT_TEMPLATES 137 | TAGS_URL = 'tag/' 138 | CATEGORIES_URL = 'category/' # defines where to save a categories page, it's activated by DIRECT_TEMPLATES 139 | CATEGORIES_SAVE_AS = 'category/index.html' 140 | PAGES_SAVE_AS = 'pages/index.html' # defines where to save a list of all pages, it's activated by TEMPLATE_PAGES 141 | PAGES_URL = 'pages/' 142 | 143 | YEAR_ARCHIVE_SAVE_AS = 'archives/{date:%Y}/index.html' # activates rendering an archive page per year/month/day 144 | MONTH_ARCHIVE_SAVE_AS = 'archives/{date:%Y}/{date:%m}/index.html' 145 | DAY_ARCHIVE_SAVE_AS = '' 146 | 147 | # additional pages 148 | 149 | # a hash array with an extra list of 'templates+output_filename' for rendering besides of blog posts 150 | # The output filename is needed because they don't have *_SAVE_AS variables 151 | TEMPLATE_PAGES = {'sitemap.html': 'sitemap.xml', 152 | 'start.html': 'index.html', 153 | 'pages.html': PAGES_SAVE_AS} 154 | 155 | -------------------------------------------------------------------------------- /src.docs/publishconf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- # 3 | from __future__ import unicode_literals 4 | 5 | # This file is only used if you use `make publish` or 6 | # explicitly specify it as your config file. 7 | 8 | import os 9 | import sys 10 | sys.path.append(os.curdir) 11 | 12 | # inherits pelicanconf configuration 13 | from pelicanconf import * 14 | 15 | RELATIVE_URLS = False 16 | -------------------------------------------------------------------------------- /tests/config-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env roundup 2 | 3 | describe "Checks if it works with config files" 4 | 5 | before() { 6 | cd .. 7 | } 8 | 9 | after() { 10 | cd - 11 | } 12 | 13 | it_prints_something() { 14 | output="$(RC_WAIT_POLICY=wait_all ./trc -w tests)" 15 | test "${output}" = "host: test-host; ip: 127.0.0.1. Good bye" 16 | } 17 | -------------------------------------------------------------------------------- /tests/exitcodes-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env roundup 2 | 3 | describe "Checks exit codes" 4 | 5 | before() { 6 | cd .. 7 | } 8 | 9 | after() { 10 | cd - 11 | } 12 | 13 | it_catches_all_exitcodes() { 14 | set +e 15 | output="$(RC_VERBOSE=true RC_WAIT_POLICY=wait_all ./trc -H 'exit 0' -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5 | sed -n 's|^.*(exitcode=\([[:digit:]]*\)):.*$|\1|p' | tr '\n' ' ')" 16 | test "${output}" = "0 4 5 2 3 0 " 17 | } 18 | 19 | it_catches_exitcodes_till_errs_in_sync() { 20 | set +e 21 | output="$(RC_VERBOSE=true RC_WAIT_POLICY=wait_err ./trc -D 'sleep 1; exit 2' -D 'sleep 2; exit 3' -F 'exit 0' -F 'exit 4' exit 5 | sed -n 's|^.*(exitcode=\([[:digit:]]*\)):.*$|\1|p' | tr '\n' ' ')" 22 | test "${output}" = "0 4 143 143 " 23 | } 24 | 25 | it_rewrites_exitcode_on_halting_to_err() { 26 | set +e 27 | ./trc -H 'exit 11' exit 5 28 | test $? -eq 11 29 | } 30 | 31 | it_rewrites_exitcode_on_halting_to_ok() { 32 | set +e 33 | ./trc -H 'exit 0' exit 5 34 | test $? -eq 0 35 | } 36 | 37 | it_fails_on_booting_imm() { 38 | set +e 39 | ./trc -B 'exit 22' -H 'exit 0' exit 5 40 | test $? -eq 22 41 | } 42 | 43 | it_exits_143_on_async_any() { 44 | set +e 45 | output=$(RC_WAIT_POLICY=wait_any ./trc -D 'echo 11; exit 11' -D 'sleep 1; echo 22; exit 22') 46 | test $? -eq 143 47 | test "${output}" = "11" 48 | } 49 | 50 | it_exits_last_status_on_sync_any() { 51 | set +e 52 | output=$(RC_WAIT_POLICY=wait_any ./trc -F 'echo 11; exit 11' -F 'echo 22; exit 22') 53 | test $? -eq 11 54 | test "${output}" = "11" 55 | } 56 | 57 | it_exits_last_syncstatus_on_mix_any() { 58 | set +e 59 | output=$(RC_WAIT_POLICY=wait_any ./trc -D 'exit 33' -F 'echo 11; exit 11' -F 'echo 22; exit 22') 60 | test $? -eq 11 61 | test "${output}" = "11" 62 | } 63 | 64 | -------------------------------------------------------------------------------- /tests/modes-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env roundup 2 | 3 | describe "Checks all available modes" 4 | 5 | before() { 6 | cd .. 7 | } 8 | 9 | after() { 10 | cd - 11 | } 12 | 13 | it_boot() { 14 | set +e 15 | ./trc -B 'e=11' -B 'exit $e' 16 | test $? -eq 11 17 | } 18 | 19 | it_async_any() { 20 | set +e 21 | output="$(RC_WAIT_POLICY=wait_any ./trc -D 'echo Hello' -D 'sleep 1; echo World')" 22 | test "${output}" = "Hello" 23 | } 24 | 25 | it_async_all() { 26 | output="$(RC_WAIT_POLICY=wait_all ./trc -D 'echo -n "Hello "' -D 'sleep 1; echo World')" 27 | test "${output}" = "Hello World" 28 | } 29 | 30 | it_sync_any() { 31 | set +e 32 | output="$(RC_WAIT_POLICY=wait_any ./trc -F 'echo Hello' echo World)" 33 | test "${output}" = "Hello" 34 | } 35 | 36 | it_sync_all() { 37 | set +e 38 | output="$(RC_WAIT_POLICY=wait_all ./trc -F 'echo -n "Hello "' -F 'echo World')" 39 | test "${output}" = "Hello World" 40 | } 41 | 42 | it_mix_all() { 43 | output="$(RC_WAIT_POLICY=wait_all ./trc -B 'export wait_sec=1' -D 'sleep ${wait_sec}; echo Hello' -F 'echo -n "World "')" 44 | test "${output}" = "World Hello" 45 | } 46 | 47 | it_isolated() { 48 | output="$(RC_WAIT_POLICY=wait_all ./trc -B 'export v1=A' -H 'echo "${v1}${v2}${v3}"' -D 'export v2=B' -F 'export v3=C' -F 'echo -n "${v1}${v2}${v3}"')" 49 | test "${output}" = "AA" 50 | } 51 | 52 | -------------------------------------------------------------------------------- /tests/trc.d/async.info: -------------------------------------------------------------------------------- 1 | print "host: ${MYHOSTNAME}; ip: ${MYIP}. " 2 | -------------------------------------------------------------------------------- /tests/trc.d/boot.funcs: -------------------------------------------------------------------------------- 1 | print() { 2 | echo -en "$@" 3 | } 4 | declare -fx print 5 | -------------------------------------------------------------------------------- /tests/trc.d/boot.setvars: -------------------------------------------------------------------------------- 1 | export MYIP="127.0.0.1" 2 | export MYHOSTNAME="test-host" 3 | export WAIT_TIME=1 4 | -------------------------------------------------------------------------------- /tests/trc.d/halt.goodbye: -------------------------------------------------------------------------------- 1 | print "Good bye\n" 2 | -------------------------------------------------------------------------------- /tests/trc.d/sync.sleep: -------------------------------------------------------------------------------- 1 | sleep ${WAIT_TIME} 2 | -------------------------------------------------------------------------------- /tests/verbose-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env roundup 2 | 3 | describe "Checks if verbose options work" 4 | 5 | before() { 6 | cd .. 7 | } 8 | 9 | after() { 10 | cd - 11 | } 12 | 13 | it_is_silent() { 14 | output="$(./trc)" 15 | test -z "${output}" 16 | } 17 | 18 | it_is_verbose() { 19 | verbose=$(RC_VERBOSE=true ./trc) 20 | verbose_extra=$(RC_VERBOSE=true RC_VERBOSE_EXTRA=true ./trc) 21 | test -n "${verbose}" 22 | test -n "${verbose_extra}" 23 | test $(echo "${verbose}" | wc -l) -ne $(echo "${verbose_extra}" | wc -l) 24 | } 25 | -------------------------------------------------------------------------------- /trc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # TrivialRC 4 | # The minimalistic RC system and process manager for containers and applications 5 | # Copyright (c) 2016, 2017 by vorakl 6 | 7 | trc_version="v1.1.12" 8 | 9 | set +e # Do not exit on errors by default 10 | if [[ "${RC_DEBUG}" = "true" ]]; then 11 | set -x # Turns on Debug mode 12 | fi 13 | 14 | main() { 15 | check_ver_usage "$1" 16 | check_req_bins ls ps pkill sleep date basename dirname 17 | 18 | # It's possible to exit from the last `halt` command with ${_exit_status} which comes from main() 19 | # and rewrite by this an exit status of the `halt` stage which always has a priority against main's exit status 20 | trap '_exit_status=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_exit_status})"; hook_main_exit' EXIT 21 | trap '_es_mt_s=$?; trap '_ignore_mt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=${_es_mt_s})"; exit ${_es_mt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 22 | trap '_es_mt_er=$?; trap '_ignore_mt_er=1' ERR; if [[ "${RC_WAIT_POLICY}" = "wait_err" ]]; then debug "err-trap (exitcode=${_es_mt_er})"; exit ${_es_mt_er}; fi' ERR 23 | 24 | local _wait_bg_cmd _file _es_m_f=0 _es_m_d=0 25 | 26 | export SELF_NAME=$(run -p basename $0 .sh) # Self name for logging purpose 27 | export MAINPID="${BASHPID}" # PID of the main process 28 | : ${RC_WAIT_POLICY:=wait_any}; export RC_WAIT_POLICY 29 | 30 | work_dir="${PWD}" 31 | boot_stage_left=0 32 | halt_cmds_exist=0 33 | halt_cmds=() 34 | child_pids="" # Collects of child processes 35 | ns="main" # Name Space 36 | 37 | case "${RC_WAIT_POLICY}" in 38 | wait_all) _wait_bg_cmd="run wait";; 39 | wait_any) _wait_bg_cmd="run wait";; 40 | wait_err) _wait_bg_cmd="run wait";; 41 | *) _wait_bg_cmd=":";; 42 | esac 43 | 44 | log "The wait policy: ${RC_WAIT_POLICY}" 45 | 46 | if [[ "$1" = "-w" ]] || [[ "$1" = "--workdir" ]]; then 47 | shift 48 | work_dir="$1" 49 | shift 50 | fi 51 | if [[ -n "${RC_WORK_DIR}" ]]; then 52 | work_dir="${RC_WORK_DIR}" 53 | else 54 | RC_WORK_DIR="${work_dir}" 55 | export RC_WORK_DIR 56 | fi 57 | log "Work dir: ${RC_WORK_DIR}" 58 | 59 | debug "Looking for \`boot\` scripts and commands..." 60 | { 61 | # Reads commands from files to run before everything 62 | for _file in $(run -p ls ${work_dir}/trc.boot.* ${work_dir}/trc.d/boot.* 2>/dev/null || true); do 63 | log "Launching in the $(ns_long ${ns}): ${_file}" 64 | set -e 65 | . ${_file} 66 | set +e 67 | done 68 | 69 | # Checks for boot tasks in the command line (all commands and their parameters should be supplied as one parameter) 70 | while [[ "$1" = "-B" ]]; do 71 | shift 72 | log "Launching in the $(ns_long ${ns}): $1" 73 | set -e 74 | eval "$1" 75 | set +e 76 | shift 77 | done 78 | } 79 | boot_stage_left=1 80 | 81 | debug "Looking for \`halt\` commands..." 82 | # Collects commands for the `halt` stage from a command line to be run on exit from the main exit trap 83 | while [[ "$1" = "-H" ]]; do 84 | shift 85 | halt_cmds_exist=1 86 | halt_cmds[${#halt_cmds[*]}]="$1" 87 | shift 88 | done 89 | 90 | debug "Looking for \`async\` scripts and commands..." 91 | # Reads commands from files to run in the background (in parallel) 92 | for _file in $(run -p ls ${work_dir}/trc.async.* ${work_dir}/trc.d/async.* 2>/dev/null || true); do 93 | ( 94 | # Run this on any exit, catching the exitcode of the "main" command, 95 | # printing additional info and finishing up this sub-program with the right exitcode 96 | trap '_es_dt_ex=$?; trap '_ignore_mt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "${_file}"' EXIT 97 | 98 | # In case of exit on errors (set -e) or by a signal, catch exitcode and exit with it, which 99 | # will lead to triggering an EXIT trap 100 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 101 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR 102 | 103 | ns="async" 104 | log "Launching in the $(ns_long ${ns}): ${_file}" 105 | . ${_file} 106 | )& 107 | child_pids="${child_pids} $!" 108 | done 109 | 110 | # Checks for background tasks in the command line (all commands and their parameters should be supplied as one parameter) 111 | while [[ "$1" = "-D" ]]; do 112 | shift 113 | ( 114 | trap '_es_dt_ex=$?; trap '_ignore_dt_ex=1' EXIT; debug "exit-trap (exitcode=${_es_dt_ex})"; hook_sub_exit ${_es_dt_ex} "$1"' EXIT 115 | trap '_es_dt_s=$?; trap '_ignore_dt_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_dt_s)"; exit ${_es_dt_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 116 | trap '_es_dt_er=$?; trap '_ignore_dt_er=1' ERR; debug "err-trap (exitcode=$_es_dt_er)"; exit ${_es_dt_er}' ERR 117 | 118 | ns="async" 119 | log "Launching in the $(ns_long ${ns}): $1" 120 | eval "$1" 121 | )& 122 | child_pids="${child_pids} $!" 123 | shift 124 | done 125 | 126 | debug "Looking for \`sync\` scripts and commands..." 127 | # Checks for foreground tasks in files (sequentially) 128 | for _file in $(run -p ls ${work_dir}/trc.sync.* ${work_dir}/trc.d/sync.* 2>/dev/null || true); do 129 | ( 130 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "${_file}"' EXIT 131 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 132 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR 133 | 134 | ns="sync" 135 | log "Launching in the $(ns_long ${ns}): ${_file}" 136 | . ${_file} 137 | ) 138 | # Catch the exitcode of a foreground sub-program 139 | _es_m_f=$? 140 | done 141 | 142 | # Checks for foreground tasks in the command line (all commands and their parameters should be supplied as one parameter) 143 | while [[ "$1" = "-F" ]]; do 144 | shift 145 | ( 146 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$1"' EXIT 147 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 148 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR 149 | 150 | ns="sync" 151 | log "Launching in the $(ns_long ${ns}): $1" 152 | eval "$1" 153 | ) 154 | _es_m_f=$? 155 | shift 156 | done 157 | 158 | # Checks for a foreground task in the command line (without any quotation!) 159 | if [[ -n "$*" ]]; then 160 | ( 161 | set -e 162 | trap '_es_ft_ex=$?; trap '_ignore_ft_ex=1' EXIT; debug "exit-trap (exitcode=${_es_ft_ex})"; hook_sub_exit ${_es_ft_ex} "$@"' EXIT 163 | trap '_es_ft_s=$?; trap '_ignore_ft_s=1' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS; debug "sig-trap (exitcode=$_es_ft_s)"; exit ${_es_ft_s}' HUP INT ILL TRAP ABRT BUS FPE USR1 SEGV USR2 PIPE ALRM TERM XCPU XFSZ VTALRM SYS 164 | trap '_es_ft_er=$?; trap '_ignore_ft_er=1' ERR; debug "err-trap (exitcode=$_es_ft_er)"; exit ${_es_ft_er}' ERR 165 | 166 | ns="sync" 167 | log "Launching in the $(ns_long ${ns}): $@" 168 | "$@" 169 | ) 170 | # Catch the exitcode of a foreground sub-program 171 | _es_m_f=$? 172 | fi 173 | 174 | # Wait for all background processes and exit with a status of the last one 175 | # or with 128+SIGNAL in case of getting a signal 176 | # this is a work around for not trigerring ERR trap in main() two times in case of non 0 exist status 177 | ${_wait_bg_cmd} ${child_pids} || _es_m_d=$? 178 | 179 | if [ ${_es_m_d} -ne 0 ]; then 180 | _es_m_f=${_es_m_d} # update exit code only if one of background processes has failed 181 | fi 182 | 183 | if [[ "${RC_WAIT_POLICY}" = "wait_forever" ]]; then 184 | infinite_loop 185 | fi 186 | 187 | return ${_es_m_f} 188 | } 189 | 190 | say() { 191 | if [[ "${RC_VERBOSE}" = "true" ]]; then 192 | run echo "$@" 193 | fi 194 | } 195 | 196 | log() { 197 | say "$(run -p date '+%Y-%m-%d %H:%M:%S') ${SELF_NAME} [${ns}/${BASHPID}]: $@" 198 | } 199 | 200 | warn() { 201 | log "$@" >&2 202 | } 203 | 204 | err() { 205 | warn "$@" 206 | exit 1 207 | } 208 | 209 | debug() { 210 | if [[ "${RC_VERBOSE_EXTRA}" = "true" ]]; then 211 | log " - $@" 212 | fi 213 | } 214 | 215 | hook_main_exit() { 216 | local _pid _cpids _file _cmd _es_mtt_h=0 _term_timeout=5 217 | 218 | # If it fails in the `boot` stage, it appears here with 'set -e'. So, reset to +e again 219 | set +e 220 | trap 'true' ERR 221 | 222 | log "Trying to terminate sub-processes..." 223 | for _pid in ${child_pids}; do 224 | if run -p ps -p ${_pid} &> /dev/null; then 225 | log "terminating the child process " 226 | 227 | # Removes all unexpected sub-processes 228 | _cpids="$(run -p pgrep -P ${_pid} -d,)" 229 | run kill -TERM ${_pid} $(echo "${_cpids}" | tr ',' ' ') &> /dev/null 230 | 231 | while run -p ps -p ${_pid},${_cpids} &> /dev/null && [[ ${_term_timeout} -gt 0 ]] 232 | do 233 | run -p sleep 1 234 | : $((_term_timeout--)) 235 | done 236 | 237 | if run -p ps -p ${_pid},${_cpids} &>/dev/null; then 238 | run kill -KILL ${_pid} $(run echo "${_cpids}" | run -p tr ',' ' ') &> /dev/null 239 | fi 240 | fi 241 | done 242 | debug "removing all unexpected sub-processes" 243 | if ! run -p pkill -KILL -P ${MAINPID} &> /dev/null; then 244 | true 245 | fi 246 | 247 | # --- `halt` 248 | if [[ boot_stage_left -eq 1 ]]; then 249 | debug "Looking for \`halt\` scripts..." 250 | # Checks for shutdown tasks in files (sequentially) 251 | for _file in $(run -p ls ${work_dir}/trc.halt.* ${work_dir}/trc.d/halt.* 2>/dev/null || true); do 252 | halt_cmds_exist=1 253 | ( 254 | set -e # Exit on errors in the sub-shell 255 | ns="halt" 256 | log "Running the shutdown script: ${_file}" 257 | . ${_file} 258 | ) 259 | _es_mtt_h=$? 260 | log "Exiting from the shutdown script (exitcode=${_es_mtt_h}): ${_file}" 261 | done 262 | 263 | # Runs shutdown tasks from the command line if any 264 | for _cmd in "${halt_cmds[@]}"; do 265 | ( 266 | set -e # Exit on errors in the sub-shell 267 | 268 | ns="halt" 269 | log "Running the shutdown command: ${_cmd}" 270 | eval "${_cmd}" 271 | ) 272 | _es_mtt_h=$? 273 | log "Exiting from the shutdown command (exitcode=${_es_mtt_h}): ${_cmd}" 274 | done 275 | fi 276 | 277 | if [[ ${halt_cmds_exist} -eq 1 ]]; then 278 | _exit_status=${_es_mtt_h} 279 | fi 280 | 281 | log "Exited (exitcode=${_exit_status})" 282 | exit ${_exit_status} 283 | } 284 | 285 | hook_sub_exit() { 286 | set +e # do not stop or errors anyway 287 | 288 | local _rc=$1 # Getting the exit code for a logging purpose only 289 | shift 290 | 291 | log "Exiting in the $(ns_long ${ns}) (exitcode=${_rc}): $@" 292 | 293 | if [[ "${RC_WAIT_POLICY}" = "wait_any" ]]; then 294 | # If exiting from a bg process and don't need to wait other processes, let's stop the main 295 | if run -p ps -p ${MAINPID} &> /dev/null; then 296 | debug "terminating the main process " 297 | run kill -TERM ${MAINPID} &> /dev/null 298 | fi 299 | fi 300 | 301 | exit ${_rc} 302 | } 303 | 304 | infinite_loop() { 305 | log "Activated infinite loop! To stop, press or send SIGTERM..." 306 | while true; do 307 | run -p sleep 1 308 | done 309 | } 310 | 311 | ns_long() { 312 | local _ns 313 | 314 | case "$1" in 315 | fg|sync) _ns="foreground";; 316 | bg|async) _ns="background";; 317 | sd|halt) _ns="shutdown";; 318 | main) _ns="boot";; 319 | *) _ns="$1";; 320 | esac 321 | 322 | echo "${_ns}" 323 | } 324 | 325 | check_ver_usage() { 326 | case "$1" in 327 | -v|--version) print_version ;; 328 | -h|--help) print_usage ;; 329 | *) : ;; 330 | esac 331 | } 332 | 333 | print_usage() { 334 | local _self=$0 335 | run echo "Usage: ${_self} [-w|--workdir 'dir'] [-B 'cmds' [...]] [-H 'cmds' [...]] [-D 'cmds' [...]] [-F 'cmds' [...]] [cmd [args]]" 336 | run echo "Examples:" 337 | run echo -e " $ ${_self} -B 'name=\$(id -un); echo booting...' -H 'echo halting...' -F 'echo Hello, \${name}!'" 338 | run echo -e " $ RC_WAIT_POLICY=wait_all ${_self} -D 'echo Hello' -D 'sleep 2; echo World' echo waiting..." 339 | run echo -e " $ RC_VERBOSE=true ${_self} -F 'echo -n \"Hello \"; echo World'" 340 | run echo -e " $ ${_self} --workdir /opt/app" 341 | exit 0 342 | } 343 | 344 | print_version() { 345 | run echo "trc (TrivialRC) $trc_version" 346 | exit 0 347 | } 348 | 349 | run() { 350 | builtin command "$@" 351 | } 352 | 353 | check_req_bins() { 354 | local _cmd 355 | 356 | for _cmd in "$@"; do 357 | run -v ${_cmd} &> /dev/null || { echo "There is no the required command: ${_cmd}" >&2; exit 1; } 358 | done 359 | } 360 | 361 | main "$@" 362 | -------------------------------------------------------------------------------- /version: -------------------------------------------------------------------------------- 1 | v1.1.12 2 | --------------------------------------------------------------------------------