diff --git a/README.md b/README.md index 1554a4a..9e1977c 100644 --- a/README.md +++ b/README.md @@ -86,15 +86,14 @@ dmicmp_v4_send_echo_request(&dst, my_identifier, 1, NULL, 0, DMARP_DEFAULT_TIMEO ## 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`. +dmicmp answers pings as soon as it's loaded and enabled, 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`, `exec=dmicmp` `type=library`) that registers dmicmp directly as a +service - no separate loader script needed. `service start icmp`/`service +stop icmp` load+enable / disable+unload it. See +[docs/service.md](docs/service.md) for the full setup. ## API @@ -124,10 +123,8 @@ View documentation using `dmf-man dmicmp`. ``` dmicmp/ -├── configs/ # dmsystem unit + dmell scripts to load/unload dmicmp as a service -│ ├── icmp.ini -│ ├── icmp-start.dme -│ └── icmp-stop.dme +├── configs/ # dmsystem unit (type=library) to register dmicmp as a service +│ └── icmp.ini ├── docs/ # Documentation (markdown format) ├── include/ # Public headers │ └── dmicmp.h diff --git a/configs/icmp-start.dme b/configs/icmp-start.dme deleted file mode 100644 index 165eb6f..0000000 --- a/configs/icmp-start.dme +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/dmell -# -# icmp-start.dme - loads *and enables* the dmicmp module, which registers -# itself with dmip as the ICMPv4/ICMPv6 protocol handler from its own -# dmod_init() (see ../src/dmicmp.c). Once enabled, this device answers 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 bring it -# up at boot - see ../docs/service.md. -# -# Both steps are needed and they are not the same thing. Loading only creates -# the module's context; dmod_init() runs on enable, and so does the pulling in -# of required modules (dmip here). A module left merely loaded therefore never -# registers anything and never answers a packet - silently, since nothing it -# imports has been bound yet, so nothing crashes either. - -module load dmicmp -module enable dmicmp -exit $? diff --git a/configs/icmp-stop.dme b/configs/icmp-stop.dme deleted file mode 100644 index 0231c37..0000000 --- a/configs/icmp-stop.dme +++ /dev/null @@ -1,10 +0,0 @@ -#!/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 index 1cfbb85..7af6acf 100644 --- a/configs/icmp.ini +++ b/configs/icmp.ini @@ -1,22 +1,14 @@ -# 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 make sure the path in "args" below is where this release's configs -# actually landed. The default matches the reference deployment (dmod-boot): -# it mounts its config filesystem at /configs and installs a module's -# untagged config entries under /configs//, so an unqualified -# `dmicmp icmp-start.dme` line in its flash.dmd puts the script exactly -# there. A placeholder that matches no deployment only looks configurable - -# it guarantees the unit fails on first boot. See ../docs/service.md. +# icmp.ini - dmsystem unit that loads and enables 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#typelibrary-services-backed-by-a-library-module-not-a-process). description=Answer ICMP Echo Requests (ping) by loading the dmicmp module -exec=dmell -args=/configs/dmicmp/icmp-start.dme -type=oneshot +exec=dmicmp +type=library -# type=oneshot: icmp-start.dme only needs to run once - dmicmp stays enabled -# 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). +# type=library: dmicmp is a Library-type DMOD module, not an Application - it +# has no main(), so it cannot be spawned as a process the way a "simple"/ +# "oneshot" unit is. type=library tells libsystemd to load+enable "exec" as a +# module instead (the DMOD loader's own module lifecycle) when the unit is +# started, and to disable+unload it when the unit is stopped - see +# ../docs/service.md and dmsystem's configuration docs linked above. diff --git a/dmicmp.dmr b/dmicmp.dmr index a074e97..316ca95 100644 --- a/dmicmp.dmr +++ b/dmicmp.dmr @@ -22,8 +22,9 @@ docs=./docs => ${destination}/${module}/docs [origin=${repo_dir}/docs] 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 +# icmp.ini dmsystem unit (type=library) that loads/enables dmicmp to turn +# ICMP echo (ping) response on, and disables/unloads it on "service stop" - +# see docs/service.md configs=./configs => ${destination}/${module}/configs [origin=${repo_dir}/configs] # === Header Files === diff --git a/docs/service.md b/docs/service.md index 002146e..3d38922 100644 --- a/docs/service.md +++ b/docs/service.md @@ -6,70 +6,56 @@ 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 +`dmod_init()` only runs once something *loads and enables* dmicmp - nothing +does that on its own at boot. `configs/icmp.ini` 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 +## `exec=dmicmp`, `type=library` 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)). +way `libsystemd` starts a `simple`/`oneshot` unit (`Dmod_RunModuleDetached`). 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. +system, then enable it" - `dmod_init()` (which is what actually registers +dmicmp with dmip) only runs on *enable*, not on load alone. + +`libsystemd` supports this natively via `type=library` (named after +`Dmod_ModuleType_Library` in the dmod core): a unit with `type=library` has +its `exec` loaded and enabled (`Dmod_LoadModuleByName` + `Dmod_EnableModule`) +when started, and disabled and unloaded (`Dmod_DisableModule` + +`Dmod_UnloadModule`) when stopped - no separate launcher script needed, +unlike the process-spawning `simple`/`oneshot` types. See dmsystem's +[configuration docs](https://github.com/choco-technologies/dmsystem/blob/main/app/libsystemd/docs/configuration.md#typelibrary-services-backed-by-a-library-module-not-a-process) +for the full mechanism. ## 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" +| [`configs/icmp.ini`](../configs/icmp.ini) | The dmsystem unit: `exec=dmicmp`, `type=library`. | -`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"). +## `service start`/`service stop` both work -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. +Unlike the old `type=oneshot` + `.dme` script setup, `type=library` gives +`libsystemd` a real notion of "running" for this unit - `service status icmp` +reports it as running exactly while dmicmp is enabled, and `service stop +icmp` actually turns ping response off again (disables and unloads the +module), no separate script to run by hand: ```bash -dmell /opt/dmicmp/configs/icmp-stop.dme +service start icmp +service status icmp +service stop icmp ``` -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: +drop `icmp.ini` into `libsystemd`'s units directory: ```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" ```