A cross-platform service manager
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

652 lines
15 KiB

# [go-serviceman](https://git.rootprojects.org/root/serviceman)
5 years ago
Cross-platform service management made easy.
> sudo serviceman add --name foo ./serve.js --port 3000
5 years ago
> Success: "foo" started as a "launchd" SYSTEM service, running as "root"
## Why?
Because it sucks to debug launchctl, systemd, etc.
Also, I wanted a reasonable way to install [Telebit](https://telebit.io) on Windows.
(see more in the **More Why** section below)
5 years ago
5 years ago
## Features
5 years ago
- Unprivileged (User Mode) Services with `--user` (_Default_)
5 years ago
- [x] Linux (`sytemctl --user`)
- [x] MacOS (`launchctl`)
- [x] Windows (`HKEY_CURRENT_USER/.../Run`)
- Privileged (System) Services with `--system` (_Default_ for `root`)
5 years ago
- [x] Linux (`sudo sytemctl`)
- [x] MacOS (`sudo launchctl`)
- [ ] Windows (_not yet implemented_)
5 years ago
# Table of Contents
5 years ago
- Usage
- Install
- Examples
- compiled programs
- scripts
- bash
- node
- python
- ruby
- PATH
5 years ago
- Logging
- Debugging
- Windows
- Building
- More Why
5 years ago
- Legal
5 years ago
# Usage
5 years ago
The basic pattern of usage:
5 years ago
```bash
sudo serviceman add --name "foobar" [options] [interpreter] <service> [--] [service options]
sudo serviceman start <service>
sudo serviceman stop <service>
sudo serviceman list --all
5 years ago
serviceman version
5 years ago
```
5 years ago
And what that might look like:
```bash
sudo serviceman add --name "foo" foo.exe -c ./config.json
5 years ago
```
5 years ago
You can also view the help:
5 years ago
5 years ago
```
5 years ago
serviceman add --help
```
# System Services VS User Mode Services
User services start **on login**.
System services start **on boot**.
The **default** is to register a _user_ services. To register a _system_ service, use `sudo` or run as `root`.
5 years ago
# Install
You can install `serviceman` directly from the official git releases with [`webi`](https://webinstall.dev/serviceman):
**Mac**, **Linux**:
```bash
curl -sL https://webinstall.dev/serviceman | bash
```
**Windows 10**:
```pwsh
curl.exe -sLA "MS" https://webinstall.dev/serviceman | powershell
```
You can run this from cmd.exe or PowerShell (curl.exe is a native part of Windows 10).
## Manual Install
5 years ago
There are a number of pre-built binaries.
If none of them work for you, or you prefer to build from source,
see the instructions for building far down below.
## Downloads
```
curl -fsSL "https://rootprojects.org/serviceman/dist/$(uname -s)/$(uname -m)/serviceman" -o serviceman
chmod +x ./serviceman
```
5 years ago
### MacOS
<details>
<summary>See download options</summary>
5 years ago
MacOS (darwin): [64-bit Download ](https://rootprojects.org/serviceman/dist/darwin/amd64/serviceman)
```
curl https://rootprojects.org/serviceman/dist/darwin/amd64/serviceman -o serviceman
chmod +x ./serviceman
5 years ago
```
</details>
5 years ago
### Windows
<details>
<summary>See download options</summary>
Windows 10: [64-bit Download](https://rootprojects.org/serviceman/dist/windows/amd64/serviceman.exe)
```
powershell.exe $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://rootprojects.org/serviceman/dist/windows/amd64/serviceman.exe -OutFile serviceman.exe
5 years ago
```
**Debug version**:
```
powershell.exe $ProgressPreference = 'SilentlyContinue'; Invoke-WebRequest https://rootprojects.org/serviceman/dist/windows/amd64/serviceman.debug.exe -OutFile serviceman.debug.exe
```
5 years ago
Windows 7: [32-bit Download](https://rootprojects.org/serviceman/dist/windows/386/serviceman.exe)
```
powershell.exe "(New-Object Net.WebClient).DownloadFile('https://rootprojects.org/serviceman/dist/windows/386/serviceman.exe', 'serviceman.exe')"
```
**Debug version**:
```
powershell.exe "(New-Object Net.WebClient).DownloadFile('https://rootprojects.org/serviceman/dist/windows/386/serviceman.debug.exe', 'serviceman.debug.exe')"
```
5 years ago
</details>
### Linux
5 years ago
<details>
<summary>See download options</summary>
Linux (64-bit): [Download](https://rootprojects.org/serviceman/dist/linux/amd64/serviceman)
```
curl https://rootprojects.org/serviceman/dist/linux/amd64/serviceman -o serviceman
chmod +x ./serviceman
5 years ago
```
Linux (32-bit): [Download](https://rootprojects.org/serviceman/dist/linux/386/serviceman)
```
curl https://rootprojects.org/serviceman/dist/linux/386/serviceman -o serviceman
chmod +x ./serviceman
5 years ago
```
</details>
### Raspberry Pi (Linux ARM)
<details>
<summary>See download options</summary>
RPi 4 (64-bit armv8): [Download](https://rootprojects.org/serviceman/dist/linux/armv8/serviceman)
```
curl https://rootprojects.org/serviceman/dist/linux/armv8/serviceman -o serviceman`
chmod +x ./serviceman
5 years ago
```
RPi 3 (armv7): [Download](https://rootprojects.org/serviceman/dist/linux/armv7/serviceman)
```
curl https://rootprojects.org/serviceman/dist/linux/armv7/serviceman -o serviceman
chmod +x ./serviceman
5 years ago
```
ARMv6: [Download](https://rootprojects.org/serviceman/dist/linux/armv6/serviceman)
```
curl https://rootprojects.org/serviceman/dist/linux/armv6/serviceman -o serviceman
chmod +x ./serviceman
5 years ago
```
RPi Zero (armv5): [Download](https://rootprojects.org/serviceman/dist/linux/armv5/serviceman)
```
curl https://rootprojects.org/serviceman/dist/linux/armv5/serviceman -o serviceman
chmod +x ./serviceman
5 years ago
```
</details>
### Add to PATH
**Windows**
```
mkdir %userprofile%\bin
move serviceman.exe %userprofile%\bin\serviceman.exe
reg add HKEY_CURRENT_USER\Environment /v PATH /d "%PATH%;%userprofile%\bin"
5 years ago
```
**All Others**
```
sudo mv ./serviceman /usr/local/bin/
```
5 years ago
# Examples
```bash
sudo serviceman add --name <name> <program> [options] [--] [raw options]
# Example
sudo serviceman add --name "gizmo" gizmo --foo bar/baz
```
Anything that looks like file or directory will be **resolved to its absolute path**:
```bash
# Example of path resolution
gizmo --foo /User/me/gizmo/bar/baz
```
Use `--` to prevent this behavior:
```bash
# Complex Example
sudo serviceman add --name "gizmo" gizmo -c ./config.ini -- --separator .
```
For native **Windows** programs that use `/` for flags, you'll need to resolve some paths yourself:
```bash
# Windows Example
serviceman add --name "gizmo" gizmo.exe .\input.txt -- /c \User\me\gizmo\config.ini /q /s .
```
In this case `./config.ini` would still be resolved (before `--`), but `.` would not (after `--`)
5 years ago
5 years ago
<details>
<summary>Compiled Programs</summary>
5 years ago
5 years ago
Normally you might your program somewhat like this:
```bash
gizmo run --port 8421 --config envs/prod.ini
5 years ago
```
Adding a service for that program with `serviceman` would look like this:
```bash
sudo serviceman add --name "gizmo" gizmo run --port 8421 --config envs/prod.ini
```
5 years ago
serviceman will find `gizmo` in your PATH and resolve `envs/prod.ini` to its absolute path.
5 years ago
5 years ago
</details>
5 years ago
<details>
<summary>Using with scripts</summary>
```bash
./snarfblat.sh --port 8421
```
5 years ago
Although your text script may be executable, you'll need to specify the interpreter
in order for `serviceman` to configure the service correctly.
This can be done in two ways:
1. Put a **hashbang** in your script, such as `#!/bin/bash`.
2. Prepend the **interpreter** explicitly to your command, such as `bash ./dinglehopper.sh`.
5 years ago
For example, suppose you had a script like this:
`iamok.sh`:
```bash
while true; do
sleep 1; echo "Still Alive, Still Alive!"
done
5 years ago
```
Normally you would run the script like this:
```bash
./imok.sh
5 years ago
```
So you'd either need to modify the script to include a hashbang:
5 years ago
```bash
#!/usr/bin/env bash
while true; do
sleep 1; echo "I'm Ok!"
done
```
5 years ago
Or you'd need to prepend it with `bash` when creating a service for it:
```bash
sudo serviceman add --name "imok" bash ./imok.sh
```
5 years ago
**Background Information**
An operating system can't "run" text files (even if the executable bit is set).
Scripts require an _interpreter_. Often this is denoted at the top of
"executable" scripts with something like one of these:
5 years ago
```
5 years ago
#!/usr/bin/env ruby
```
```bash
5 years ago
#!/usr/bin/python
```
5 years ago
However, sometimes people get fancy and pass arguments to the interpreter,
like this:
```bash
5 years ago
#!/usr/local/bin/node --harmony --inspect
```
Serviceman understands all 3 of those approaches.
5 years ago
</details>
<details>
<summary>Using with node.js</summary>
If normally you run your node script something like this:
```bash
pushd ~/my-node-project/
npm start
```
Then you would add it as a system service like this:
```bash
sudo serviceman add npm start
```
If normally you run your node script something like this:
```bash
pushd ~/my-node-project/
node ./serve.js --foo bar --baz
```
5 years ago
Then you would add it as a system service like this:
```bash
sudo serviceman add node ./serve.js --foo bar --baz
```
It's important that any paths start with `./` and have the `.js`
so that serviceman knows to resolve the full path.
5 years ago
```bash
# Bad Examples
sudo serviceman add node ./demo # Wouldn't work for 'demo.js' - not a real filename
sudo serviceman add node demo # Wouldn't work for './demo/' - doesn't look like a directory
```
5 years ago
See **Using with scripts** for more detailed information.
</details>
<details>
<summary>Using with python</summary>
If normally you run your python script something like this:
```bash
pushd ~/my-python-project/
python ./serve.py --config ./config.ini
5 years ago
```
Then you would add it as a system service like this:
```bash
sudo serviceman add python ./serve.py --config ./config.ini
```
5 years ago
See **Using with scripts** for more detailed information.
</details>
<details>
<summary>Using with ruby</summary>
If normally you run your ruby script something like this:
```bash
pushd ~/my-ruby-project/
ruby ./serve.rb --config ./config.yaml
5 years ago
```
Then you would add it as a system service like this:
```bash
sudo serviceman add ruby ./serve.rb --config ./config.yaml
```
5 years ago
See **Using with scripts** for more detailed information.
</details>
5 years ago
<details>
<summary>Setting PATH</summary>
5 years ago
You can set the `$PATH` (`%PATH%` on Windows) for your service like this:
5 years ago
```bash
sudo serviceman add ./myservice --path "/home/myuser/bin"
```
Snapshot your actual path like this:
```bash
sudo serviceman add ./myservice --path "$PATH"
```
Remember that this takes a snapshot and sets it in the configuration, it's not
a live reference to your path.
5 years ago
</details>
## Hints
5 years ago
- If something goes wrong, read the output **completely** - it'll probably be helpful
- Run `serviceman` from your **project directory**, just as you would run it normally
- Otherwise specify `--name <service-name>` and `--workdir <project directory>`
- Use `--` in front of arguments that should not be resolved as paths
- This also holds true if you need `--` as an argument, such as `-- --foo -- --bar`
5 years ago
```
# Example of a / that isn't a path
# (it needs to be escaped with --)
sudo serviceman add dinglehopper config/prod -- --category color/blue
5 years ago
```
5 years ago
# Logging
### Linux
```bash
sudo journalctl -xef --unit <NAME>
sudo journalctl -xef --user-unit <NAME>
```
### Mac, Windows
5 years ago
When you run `serviceman add` it will either give you an error or
will print out the location where logs will be found.
By default it's one of these:
```txt
~/.local/share/<NAME>/var/log/<NAME>.log
```
```txt
/opt/<NAME>/var/log/<NAME>.log
5 years ago
```
You set it with one of these:
5 years ago
- `--logdir <path>` (cli)
- `"logdir": "<path>"` (json)
- `Logdir: "<path>"` (go)
5 years ago
If anything about the logging sucks, tell me... unless they're your logs
(which they probably are), in which case _you_ should fix them.
That said, my goal is that it shouldn't take an IT genius to interpret
why your app failed to start.
# Debugging
- `serviceman add --dryrun <normal options>`
- `serviceman run --config <special config>`
5 years ago
One of the most irritating problems with all of these launchers is that they're
terrible to debug - it's often difficult to find the logs, and nearly impossible
to interpret them, if they exist at all.
The config files generate by `serviceman` are simple, template-generated and
tested, and therefore gauranteed to work - **_if_** your
application runs with the parameters given, which is big 'if'.
`serviceman` tries to make sure that all necessary files and folders
exist and give clear error messages if they don't (be sure to check the logs,
mentioned above).
There's also a `run` utility that can be used to test that the parameters
you've given are being interpreted correctly (absolute paths and such).
```bash
serviceman run --config ./conf.json
```
Where `conf.json` looks something like
**For Binaries**:
```json
{
5 years ago
"title": "Demo",
"exec": "/Users/me/go-demo/demo",
"argv": ["--foo", "bar", "--baz", "qux"]
5 years ago
}
```
**For Scripts**:
Scripts can't be run directly. They require a binary `interpreter` - bash, node, ruby, python, etc.
If you're running from the folder containing `./demo.js`,
and `node.exe` is in your PATH, then you can use executable
names and relative paths.
```json
{
5 years ago
"title": "Demo",
"interpreter": "node.exe",
"exec": "./bin/demo.js",
"argv": ["--foo", "bar", "--baz", "qux"]
}
```
5 years ago
That's equivalent to this:
```json
{
5 years ago
"title": "Demo",
5 years ago
5 years ago
"name": "demo",
5 years ago
5 years ago
"exec": "node.exe",
"argv": ["./bin/demo.js", "--foo", "bar", "--baz", "qux"]
5 years ago
}
```
Making `add` and `run` take the exact same arguments is on the TODO list.
The fact that they don't is an artifact of `run` being created specifically
for Windows.
If you have gripes about it, tell me. It shouldn't suck. That's the goal anyway.
5 years ago
## Peculiarities of Windows
# Console vs No Console
Windows binaries can be built either for the console or the GUI.
When they're built for the console they can hide themselves when they start.
They must open up a terminal window.
When they're built for the GUI they can't print any output - even if they're started in the terminal.
This is why there's a **Debug version** for the windows binaries -
so that you can get your arguments correct with the one and then
switch to the other.
There's probably a clever way to work around this, but I don't know what it is yet.
# No userspace launcher
5 years ago
Windows doesn't have a userspace daemon launcher.
This means that if your application crashes, it won't automatically restart.
However, `serviceman` handles this by not directly adding your application
to `HKEY_CURRENT_USER/.../Run`, but rather installing a copy of _itself_
instead, which runs your application and automatically restarts it whenever it
exits.
If the application fails to start `serviceman` will retry continually,
but it does have an exponential backoff of up to 1 minute between failed
restart attempts.
See the bit on `serviceman run` in the **Debugging** section up above for more information.
5 years ago
5 years ago
# Building
```bash
git clone https://git.coolaj86.com/coolaj86/go-serviceman.git
```
```bash
pushd ./go-serviceman
```
```bash
go generate -mod=vendor ./...
5 years ago
```
**Windows**:
```bash
go build -mod=vendor -ldflags "-H=windowsgui" -o serviceman.exe
```
**Linux, MacOS**:
```bash
go build -mod=vendor -o /usr/local/bin/serviceman
```
# More Why
5 years ago
I created this for two reasons:
1. Too often I just run services in `screen -xRS foo` because systemd `.service` files are way too hard to get right and even harder to debug. I make stupid typos or config mistakes and get it wrong. Then I get a notice 18 months later from digital ocean that NYC region 3 is being rebooted and to expect 5 seconds of downtime... and I don't remember if I remembered to go back and set up that service with systemd or not.
2. To make it easier for people to install [Telebit](https://telebit.io) on Windows.
<!-- {{ if .Legal }} -->
# Legal
[serviceman](https://git.coolaj86.com/coolaj86/go-serviceman) |
MPL-2.0 |
[Terms of Use](https://therootcompany.com/legal/#terms) |
[Privacy Policy](https://therootcompany.com/legal/#privacy)
Copyright 2019 AJ ONeal.
<!-- {{ end }} -->