diff --git a/README.md b/README.md
index 0ea6748..cbb70ea 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,12 @@
[
](https://github.com/FedericoPonzi/Horust/raw/master/res/horust-logo.png)
-[](https://github.com/FedericoPonzi/Horust/actions?query=workflow%3ACI) [](./LICENSE) [](https://gitter.im/horust-init/community)
-
-[Horust](https://github.com/FedericoPonzi/Horust) is a supervisor / init system written in rust and designed to be run inside containers.
+[](https://github.com/FedericoPonzi/Horust/actions?query=workflow%3ACI) [](./LICENSE)
+
+[Horust](https://github.com/FedericoPonzi/Horust) is a supervisor / init system written in rust and designed to be run
+inside containers.
# Table of contents
+
* [Goals](#goals)
* [Status](#status)
* [Usage](#usage)
@@ -12,99 +14,149 @@
* [License](#license)
## Goals
-* **Supervision**: Be a fully-featured supervision system, designed to be run in containers (but not only).
-* **Easy to Grasp**: Have code that is easy to understand, modify _and remove_ when the situation calls for it.
-* **Completeness**: Be a drop-in replacement for your own `init` system.
-* **Rock Solid**: Be your favorite Egyptian God, to trust across all use cases.
+
+* **Supervision**: A fully-featured supervision system, designed to be run in containers (but not exclusively).
+* **Simplicity**: Clear, modifiable, and removable code as needed.
+* **Completeness**: A seamless drop-in for any `init` system.
+* **Reliability**: Stability and trustworthiness across all use cases.
## Status
-At this point, this should be considered Alpha software. As in, you can (and should) use it, but under your own discretion.
-Horust can be used on macOS in development situations. Due to limitations in the macOS API, subprocesses of supervised processes may not correctly be reaped when their parent process exits.
+
+This should be considered Beta software. You can (and should) use it, but under your own
+discretion. Please report any issue you encounter, or also sharing your use cases would be very helpful.
+Horust can be used on macOS in development situations. Due to limitations in the macOS API, subprocesses of supervised
+processes may not correctly be reaped when their parent process exits.
## Usage
-Assume you'd like to create a website health monitoring system. You can create one using Horust and a small python script.
-
-#### 1. Create a new directory:
-That will contain our services:
-```shell
-mkdir -p /etc/horust/services
+Being a supervision and init system, it can be used to start and manage a bunch of processes. You can use it to
+supervise a program and, for example, restart it in case it exists with an error. Or startup dependencies like start a
+webserver after starting a database.
+
+## Quick tutorial
+
+As a simple example, assume you'd like to host your rest api. This is the code:
+
+```
+from http.server import BaseHTTPRequestHandler, HTTPServer
+
+class Handler(BaseHTTPRequestHandler):
+ def do_GET(self):
+ if self.path == "/user":
+ raise Exception("Unsupported path: /user") # Exception will kill the server
+ self.send_response(200)
+ self.send_header("Content-type", "text/plain")
+ self.end_headers()
+ self.wfile.write(b"Hello, World!")
+
+HTTPServer(('', 8000), Handler).serve_forever()
```
-#### 2. Create your first Horust service:
+you can run it using `python3 myapp.py`. If you go to localhost:8000/user, unfortunately, the server will fail. Now you
+need to manually restart it!
-> **Pro Tip:** You can also bootstrap the creation of a new service, by using `horust --sample-service > new_service.toml`.
+Let's see how we can use horust to supervise it and restart it in case of failure.
-Create a new configuration file for Horust under `/etc/horust/services/healthchecker.toml`:
+#### 1. Create your first Horust service:
+
+> [!TIP]
+> You can also bootstrap the creation of a new service, by using `horust --sample-service > new_service.toml`.
+
+We are now going to create a new config file for our service. They are defined
+in [TOML](https://github.com/toml-lang/toml) and the default path where horust will look for service is in
+`/etc/horust/services/`.
+
+> [!NOTE]
+> It's possible to run a one-shot instance just by doing `horust myprogram` without defining a service config file.
+
+Let's create a new service under `/etc/horust/services/healthchecker.toml`:
```toml
-command = "/tmp/healthcheck.py"
-start-delay = "10s"
+command = "/tmp/myapp.py"
[restart]
-strategy = "never"
+strategy = "always"
```
-A couple of notes are due here:
-* This library uses [TOML](https://github.com/toml-lang/toml) for configuration, to go along nicely with Rust's chosen configuration language.
-* There are many [_supported_](https://github.com/FedericoPonzi/Horust/blob/master/DOCUMENTATION.md) properties for your service file, but only `command` is _required_.
+There are many [_supported_](https://github.com/FedericoPonzi/Horust/blob/master/DOCUMENTATION.md) properties for your
+service file, but only `command` is _required_.
-On startup, Horust will read this service file, and run the `command`. According to the restart strategy "`never`", as soon as the service has carried out its task it _will not restart_, and Horust will exit.
+On startup, Horust will read this service file, and run the `command` after waiting for 10 seconds. According to the
+restart strategy "`never`", as
+soon as the service has carried out its task it will restart.
-As you can see, it will run the `/tmp/healthcheck.py` Python script, which doesn't exist yet. Let's create it!
+As you can see, it will run the `/tmp/myapp.py` Python script, which doesn't exist yet. Let's create it!
-#### 3. Create your Python script:
+#### 2. Create your app:
-Create a new Python script under `/tmp/healthcheck.py`:
+Create a new file script under `/tmp/myapp.py`:
```python
#!/usr/bin/env python3
-import urllib.request
-import sys
-req = urllib.request.Request("https://www.google.com", method="HEAD")
-resp = urllib.request.urlopen(req)
-if resp.status == 200:
- sys.exit(0)
-else:
- sys.exit(1)
+from http.server import BaseHTTPRequestHandler, HTTPServer
+
+class Handler(BaseHTTPRequestHandler):
+ def do_GET(self):
+ if self.path == "/user":
+ raise Exception("Unsupported path: /user") # Exception will kill the server
+ self.send_response(200)
+ self.send_header("Content-type", "text/plain")
+ self.end_headers()
+ self.wfile.write(b"Hello, World!")
+
+HTTPServer(('', 8000), Handler).serve_forever()
```
-Don't forget to make it executable:
+And remember to make it executable:
```shell
-chmod +x /tmp/healthcheck.py
+chmod +x /tmp/api.py
```
-#### 4. Build Horust:
-
-This step is only required because we don't have a release yet, or if you like to live on the edge.
+#### 3. Get the latest release or build from source:
-For building Horust, you will need [Rust](https://www.rust-lang.org/learn/get-started). As soon as it's installed, you can build Horust with Rust's `cargo`:
-
-```shell
-cargo build --release
-```
+You can grab the latest release from the [releases](https://github.com/FedericoPonzi/Horust/releases/) page. Or if you
+like to live on the edge, scroll down to the building section.
#### 4. Run Horust:
Now you can just:
```shell
-./horust
+./horust --uds-folder-path /tmp
```
-By default Horust searches for services inside the `/etc/horust/services` folder (which we have created in step 1).
+> [!NOTE]
+> Horustctl is a program that allows you to interact with horust. They communicate using Unix Domain Socket (UDS),
+> and by default horust stores the sockets in `/var/run/horust`.
+> In this example, we have overridden the path by using the argument `--uds-folder-path`.
+
+Try navigating to `http://localhost:8000/`. A page with Hello world
+should be greeting you.
+
+Now try navigating to `http://localhost:8000/user` - you should get a "the connection was reset" error page.
+Checking on your terminal, you will see that the program has raised the exception, as we expected. Now, try navigating
+again to `http://localhost:8000/` and the website is still up and running.
-Every 10 seconds from now on, Horust will send an HTTP `HEAD` request to https://google.it. If the response is different than `200`, then there is an issue!
+Pretty nice uh? One last thing!
+
+If you downloaded a copy of horustctl, you can also do:
+
+```
+horustctl --uds-folder-path /tmp status myapp.toml
+```
-In this case, we're just exiting with a different exit code (i.e. a `1` instead of a `0`). But in real life, you could trigger other actions - maybe storing this information in a database for long-term analysis, or sending an e-mail to the website's owner.
+To check the status of your service. Currently, horustctl only support querying for the service status.
-#### 5. Finish up:
+### 5. Wrapping up
-Use Ctrl+C to stop Horust. Horust will send a `SIGTERM` signal to all the running services, and if it doesn't hear back for a while - it will terminate them by sending an additional `SIGKILL` signal.
+Use Ctrl+C to stop Horust. Horust will send a `SIGTERM` signal to all the running services, and if
+it doesn't hear back for a while - it will terminate them by sending an additional `SIGKILL` signal. Wait time and
+signals are configurable.
---
-Check out the [documentation](https://github.com/FedericoPonzi/Horust/blob/master/DOCUMENTATION.md) for a complete reference of the options available on the service config file. A general overview is available below as well:
+Check out the [documentation](https://github.com/FedericoPonzi/Horust/blob/master/DOCUMENTATION.md) for a complete
+reference of the options available on the service config file. A general overview is available below as well:
```toml
command = "/bin/bash -c 'echo hello world'"
@@ -125,7 +177,7 @@ http-endpoint = "http://localhost:8080/healthcheck"
file-path = "/var/myservice/up"
[failure]
-successful-exit-code = [ 0, 1, 255]
+successful-exit-code = [0, 1, 255]
strategy = "ignore"
[termination]
@@ -135,12 +187,26 @@ die-if-failed = ["db.toml"]
[environment]
keep-env = false
-re-export = [ "PATH", "DB_PASS"]
-additional = { key = "value"}
+re-export = ["PATH", "DB_PASS"]
+additional = { key = "value" }
+```
+
+## Building
+
+For building Horust, you will need [Rust](https://www.rust-lang.org/learn/get-started) and `protoc` compiler. Protoc is
+used for interacting with horust through horustctl.
+As soon as both are installed, you can build Horust with:
+
+```shell
+cargo build --release
```
## Contributing
-Thanks for considering contributing to horust! To get started have a look on [CONTRIBUTING.md](https://github.com/FedericoPonzi/Horust/blob/master/CONTRIBUTING.md).
+
+Thanks for considering contributing to horust! To get started, have a look
+at [CONTRIBUTING.md](https://github.com/FedericoPonzi/Horust/blob/master/CONTRIBUTING.md).
## License
-Horust is provided under the MIT license. Please read the attached [license](https://github.com/FedericoPonzi/horust/blob/master/LICENSE) file.
+
+Horust is provided under the MIT license. Please read the
+attached [license](https://github.com/FedericoPonzi/horust/blob/master/LICENSE) file.
diff --git a/horust/src/main.rs b/horust/src/main.rs
index c8441f1..817ce57 100644
--- a/horust/src/main.rs
+++ b/horust/src/main.rs
@@ -57,7 +57,12 @@ fn main() -> Result<()> {
)
})?;
if !opts.uds_folder_path.exists() {
- std::fs::create_dir_all(&opts.uds_folder_path)?;
+ std::fs::create_dir_all(&opts.uds_folder_path).with_context(|| {
+ format!(
+ "Failed to create uds folder path: {:?}.",
+ opts.uds_folder_path
+ )
+ })?;
}
if !opts.uds_folder_path.is_dir() {