diff --git a/README.md b/README.md index 6d8998b..1554a4a 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,18 @@ dmicmp_v4_send_echo_request(&dst, my_identifier, 1, NULL, 0, DMARP_DEFAULT_TIMEO It's its own Application-type DMOD module (`ping`), released alongside `dmicmp` itself in every tagged release. +## Running as a service + +dmicmp answers pings as soon as it's loaded, but being a Library-type +module it has no `main()` to spawn on its own - something still needs to +load it. **[configs/](configs/)** has a +[dmsystem](https://github.com/choco-technologies/dmsystem)/`libsystemd` +unit (`icmp.ini`) plus the two `dmell` scripts it runs +(`icmp-start.dme`/`icmp-stop.dme`, each a one-line `module load`/`module +unload dmicmp`) so ping response can be enabled at boot and toggled off +again. See [docs/service.md](docs/service.md) for the full setup and why +there's no `service stop icmp`. + ## API | Function | Description | @@ -105,12 +117,17 @@ See the `docs/` directory: - **[dmicmp.md](docs/dmicmp.md)** - Design overview and rationale - **[api-reference.md](docs/api-reference.md)** - Complete API documentation +- **[service.md](docs/service.md)** - Running ICMP echo response as a `dmsystem` service View documentation using `dmf-man dmicmp`. ## Project Structure ``` dmicmp/ +├── configs/ # dmsystem unit + dmell scripts to load/unload dmicmp as a service +│ ├── icmp.ini +│ ├── icmp-start.dme +│ └── icmp-stop.dme ├── docs/ # Documentation (markdown format) ├── include/ # Public headers │ └── dmicmp.h diff --git a/configs/icmp-start.dme b/configs/icmp-start.dme new file mode 100644 index 0000000..c4d1edd --- /dev/null +++ b/configs/icmp-start.dme @@ -0,0 +1,11 @@ +#!/bin/dmell +# +# icmp-start.dme - loads the dmicmp module, which registers itself with dmip +# as the ICMPv4/ICMPv6 protocol handler from its own dmod_init() (see +# ../src/dmicmp.c) - once loaded, this device starts answering ICMP Echo +# Requests (ping) automatically, with no further setup needed. Run standalone +# (e.g. `dmell icmp-start.dme`) or as icmp.ini's exec, to load it at boot - +# see ../docs/service.md. + +module load dmicmp +exit $? diff --git a/configs/icmp-stop.dme b/configs/icmp-stop.dme new file mode 100644 index 0000000..0231c37 --- /dev/null +++ b/configs/icmp-stop.dme @@ -0,0 +1,10 @@ +#!/bin/dmell +# +# icmp-stop.dme - unloads the dmicmp module (dmod_deinit() unregisters it +# from dmip, see ../src/dmicmp.c), so this device stops answering ICMP Echo +# Requests (ping) until icmp-start.dme is run again. Not wired into icmp.ini +# as an "ExecStop" - dmsystem's libsystemd has no such key yet (see +# ../docs/service.md) - so run this one directly, e.g. `dmell icmp-stop.dme`. + +module unload dmicmp +exit $? diff --git a/configs/icmp.ini b/configs/icmp.ini new file mode 100644 index 0000000..b8052cf --- /dev/null +++ b/configs/icmp.ini @@ -0,0 +1,19 @@ +# icmp.ini - dmsystem unit that loads the dmicmp module at boot, so this +# device starts answering ICMP Echo Requests (ping) with no manual step. +# Drop it into libsystemd's units directory - see dmsystem's configuration +# docs (https://github.com/choco-technologies/dmsystem/blob/main/app/libsystemd/docs/configuration.md) +# - and adjust the path in "args" below to wherever this dmicmp release was +# installed (${destination}/dmicmp/configs/icmp-start.dme, if installed via +# dmicmp.dmr's own "configs" entry). See ../docs/service.md for the full +# rationale. +description=Answer ICMP Echo Requests (ping) by loading the dmicmp module +exec=dmell +args=/opt/dmicmp/configs/icmp-start.dme +type=oneshot + +# type=oneshot: icmp-start.dme only needs to run once - dmicmp stays loaded +# and answering pings on its own once dmod_init() has run (see +# ../src/dmicmp.c), so there is no long-running process for this unit to +# keep alive. "service stop icmp" has nothing left to kill once this has run +# to completion - libsystemd has no ExecStop-style key yet - so to stop +# answering pings, run icmp-stop.dme directly instead (see ../docs/service.md). diff --git a/dmicmp.dmr b/dmicmp.dmr index d6f6c77..a074e97 100644 --- a/dmicmp.dmr +++ b/dmicmp.dmr @@ -21,6 +21,11 @@ docs=./docs => ${destination}/${module}/docs [origin=${repo_dir}/docs] # README file readme=./README.md => ${destination}/${module}/README.md [origin=${repo_dir}/README.md] +# === Configuration Files === +# icmp.ini dmsystem unit + icmp-start.dme/icmp-stop.dme dmell scripts that +# load/unload dmicmp to turn ICMP echo (ping) response on/off - see docs/service.md +configs=./configs => ${destination}/${module}/configs [origin=${repo_dir}/configs] + # === Header Files === # Include directory with all headers (public header + generated _defs.h) inc=./include => ${destination}/${module}/include [origin=${repo_dir}/include] [origin=${build_dir}/${module}_defs.h] diff --git a/docs/README.md b/docs/README.md index c5fce0e..62f7e12 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,7 @@ Welcome to the dmicmp module documentation. - **[dmicmp.md](dmicmp.md)** - Design overview and rationale - **[api-reference.md](api-reference.md)** - Complete API documentation +- **[service.md](service.md)** - Running ICMP echo response as a `dmsystem` service ## Quick Reference diff --git a/docs/service.md b/docs/service.md new file mode 100644 index 0000000..002146e --- /dev/null +++ b/docs/service.md @@ -0,0 +1,78 @@ +# ICMP echo response as a dmsystem service + +## Why this exists + +dmicmp needs no setup from other modules to do its core job - it registers +itself with [dmip](https://github.com/choco-technologies/dmip) automatically +in `dmod_init()` (see `src/dmicmp.c`), and from that point on answers ICMP +Echo Requests (ping) with no further calls needed. The catch is exactly that: +`dmod_init()` only runs once something *loads* dmicmp - nothing does that on +its own at boot. `configs/` gives +[dmsystem](https://github.com/choco-technologies/dmsystem)'s `libsystemd` a +way to do it automatically. + +## Why a `.dme` script instead of `exec=dmicmp` directly + +dmicmp is a **Library**-type DMOD module (see `CMakeLists.txt`), not an +Application - it has no `main()`, so nothing can spawn it as a process the +way `libsystemd` starts a unit (`Dmod_RunModuleDetached`, see dmsystem's +[`app/libsystemd/docs/configuration.md`](https://github.com/choco-technologies/dmsystem/blob/main/app/libsystemd/docs/configuration.md)). +What actually needs to happen is "load this module into the running DMOD +system", which is a different operation - the one +[`dmell`](https://github.com/choco-technologies/dmell) exposes as its own +`module load`/`module unload` built-in. `configs/icmp-start.dme` and +`configs/icmp-stop.dme` are two-line wrappers over exactly that, so a +`libsystemd` unit (whose `exec` must be something it can spawn) can drive +them the same way it drives any other unit. + +## Files + +| File | Does | +|------|------| +| [`configs/icmp-start.dme`](../configs/icmp-start.dme) | `module load dmicmp` - starts answering pings. | +| [`configs/icmp-stop.dme`](../configs/icmp-stop.dme) | `module unload dmicmp` - stops answering pings. | +| [`configs/icmp.ini`](../configs/icmp.ini) | The dmsystem unit itself: `exec=dmell`, `args=`, `type=oneshot`. | + +## Why `type=oneshot`, and why there is no "stop" + +`icmp-start.dme` only needs to run once: unlike a `type=simple` unit (a +server expected to keep running until explicitly stopped), dmicmp stays +loaded - and answering pings - entirely on its own once `dmod_init()` has +run, independent of the script or process that triggered it. `type=oneshot` +tells `libsystemd` this exit is expected, not a crash (see dmsystem's +configuration docs, "Service type"). + +This does mean `service stop icmp` has nothing to act on: `libsystemd` stops +a unit by killing its tracked process +(`libsystemd_stop_service_internal()`), and a oneshot unit's process is +already gone by the time it would run. `libsystemd` also has no `ExecStop`- +style key yet - a unit file has no way to name a separate command to run on +stop. So turning ping response back off is a deliberate separate step, not +something `service stop icmp` does: run `icmp-stop.dme` directly. + +```bash +dmell /opt/dmicmp/configs/icmp-stop.dme +``` + +Whether dmicmp is currently answering pings or not is therefore controlled +entirely by which of the two scripts was last run - `module load`/`module +unload` are idempotent-safe either way (loading an already-loaded module or +unloading an already-unloaded one just fails harmlessly and is logged, it +does not crash the shell). + +## Enabling at boot + +Install dmicmp with its `.dmr` (see the root [README.md](../README.md)), then +drop `icmp.ini` into `libsystemd`'s units directory and point its `args` at +wherever `icmp-start.dme` was installed: + +```bash +cp /opt/dmicmp/configs/icmp.ini /etc/dmsystem/units/icmp.ini +# args= in icmp.ini must match the actual install path, e.g.: +# args=/opt/dmicmp/configs/icmp-start.dme +dmod_loader systemd.dmf --args "/etc/dmsystem/units" +``` + +See also dmsystem's [configuration.md](https://github.com/choco-technologies/dmsystem/blob/main/app/libsystemd/docs/configuration.md#dependency-ordering) +if you want dmicmp brought up only `after`/once a particular network +interface unit (`after=`/`requires=` in `icmp.ini`) is already running.