Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,15 @@ install: $(PROG) install_man install_protocol install_tests
install -m 755 $(PROBE) $(DESTDIR)$(BINDIR)

# the declared protocol: the JSON for readers that are not C, which the package carries; the header, its pkg-config
# file and the schema of the JSON, which the development package carries
# file and the schema of the JSON, which the development package carries; and the pins_dir other packages drop their
# pins in
install_protocol:
install -d $(DESTDIR)$(DATADIR)/plugin-hostd
install -m 644 protocol/plugin-hostd.json $(DESTDIR)$(DATADIR)/plugin-hostd/protocol.json
install -m 644 protocol/mod-host.json $(DESTDIR)$(DATADIR)/plugin-hostd/mod-host.json
install -m 644 protocol/plugin-hostd.schema.json $(DESTDIR)$(DATADIR)/plugin-hostd/protocol.schema.json
install -m 644 protocol/probe-output.schema.json $(DESTDIR)$(DATADIR)/plugin-hostd/probe-output.schema.json
install -d $(DESTDIR)$(DATADIR)/plugin-hostd/pins.d
install -d $(DESTDIR)$(INCLUDEDIR)/plugin-hostd
install -m 644 include/plugin-hostd/protocol.h $(DESTDIR)$(INCLUDEDIR)/plugin-hostd/protocol.h
install -m 644 include/plugin-hostd/pin.h $(DESTDIR)$(INCLUDEDIR)/plugin-hostd/pin.h
Expand Down
33 changes: 28 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,10 @@ The settings live in a file, `-c`, `$PLUGIN_HOSTD_CONF` or
| `lv2_worker` | `mod-host` | who runs an LV2 plugin: mod-host (the mod_host program), omx-clap-host (the clap_host program, through its LV2 adapter, a plugin it cannot run refused with a code) or auto (the adapter first, mod-host for what it refuses, each such plugin in a worker of its own unless its add names a placement); lv2_worker_set overrides it for one plugin |
| `lv2_path` | `$LV2_PATH`, else `/usr/lib64/lv2:/usr/lib/lv2:/usr/local/lib/lv2` | where bundles are searched |
| `state_root` | `$XDG_RUNTIME_DIR`, else `/tmp` | the daemon's checkpoints and worker logs |
| `pin_mode` | `first-use` | strict or first-use, while require_pins is 1: first-use records the pin of a plugin on its first add, strict admits only a plugin a controller or a pin_file pinned |
| `pin_mode` | `first-use` | strict or first-use, while require_pins is 1: first-use records the pin of a plugin on its first add, strict admits only a plugin a controller, a pin_file or pins_dir pinned |
| `pin_store` | `` | the file first-use pins are kept in; empty is $XDG_STATE_HOME/plugin-hostd/pins, else $HOME/.local/state/plugin-hostd/pins |
| `pin_file` | `` | a file of pin lines loaded at start, as plugin-qualify --all --pins writes it; empty is none |
| `pins_dir` | `/usr/share/plugin-hostd/pins.d` | the read-only pins of packages: every file in it whose name ends in .pins, read at start in name order, under the store, the pin_file and pin_set, except that a pin of verdict=refused there always holds; empty is none |
| `ready_timeout_ms` | 5000 ms | a worker that does not accept in this long is a failed spawn |
| `rpc_timeout_ms` | 5000 ms | a worker that does not answer in this long is killed |
| `backoff_base_ms` | 250 ms | respawn backoff, doubling per death in the window |
Expand Down Expand Up @@ -158,10 +159,10 @@ Verbs the daemon adds
| `policy_set` | `<format \| *> own \| pool:<name>` | `resp 0` | the placement of an add that says default |
| `pool_config` | `<name> <max_instances>` | `resp 0` | the instances a pool holds before a sibling opens |
| `worker_env` | `<format \| *> <cpu-list\|-> <nice\|->` | `resp 0` | the cpu list and the nice value of the workers of a format, at spawn and on the ones running |
| `pin_set` | `<uri> <path>=<sha256>[,<path>=<sha256>...] <scheme>:<sha256> [verdict=<verdict>]` | `resp 0` | the pin of one plugin: the SHA-256 of every file the host loads for it, a path relative to the bundle (the directory of a single-file format's file), its layout fingerprint and, when it is known, the verdict of its qualification; it replaces an earlier pin of the uri. The pin store and a pin_file hold the same line, with or without the verb |
| `pin_clear` | `<uri> \| all` | `resp 0` | forget the pin of one plugin, or of every one |
| `pin_set` | `<uri> <path>=<sha256>[,<path>=<sha256>...] <scheme>:<sha256> [verdict=<verdict>]` | `resp 0` | the pin of one plugin: the SHA-256 of every file the host loads for it, a path relative to the bundle (the directory of a single-file format's file), its layout fingerprint and, when it is known, the verdict of its qualification; it replaces an earlier pin of the uri; -516 for a plugin whose pins_dir pin carries verdict=refused, which nothing overrides. The pin store, a pin_file and a pins_dir file hold the same line, with or without the verb |
| `pin_clear` | `<uri> \| all` | `resp 0` | forget the pin of one plugin, or of every one; a pins_dir pin is read-only and holds again |
| `lv2_worker_set` | `<uri> <mod-host \| omx-clap-host \| auto \| ->` | `resp 0` | the worker of one LV2 plugin, for its adds from now on: it overrides the lv2_worker setting, - takes the override back; -902 for a word that is none of these. Like policy_set it is the controller's, not in the ledger, gone with the daemon |
| `pin_accept` | `<uri>` | `resp 0` | pin_mode first-use only: take the plugin's files as they are now as its pin, after a pin_changed event; the layout is learned again by the next add and the verdict is dropped; -508 when the plugin has no pin, -509 when its files cannot be collected, -902 under pin_mode strict |
| `pin_accept` | `<uri>` | `resp 0` | pin_mode first-use only: take the plugin's files as they are now as its pin, after a pin_changed event; the layout is learned again by the next add and the verdict is dropped; -508 when the plugin has no pin, -509 when its files cannot be collected, -516 when its pins_dir pin carries verdict=refused, -902 under pin_mode strict |
| `pin_layout` | `<instance>` | `resp 0 <scheme>:<sha256>` | sent by the daemon to a worker of a format that checks layouts, after it accepted the add of a plugin whose layout pin is still unknown: the layout the worker found after init, which the daemon records in the pin |
| `pin_expect` | `<instance> <scheme>:<sha256>` | `resp 0` | sent by the daemon to a worker of a format that checks layouts, just before the instance's add: the layout pin that add checks after init and before activate; any other reply than resp 0 refuses the add with that code, and the add is never forwarded |

Expand Down Expand Up @@ -237,6 +238,7 @@ Error codes the daemon adds to mod-host's:
| `-513` | `PHD_ERR_NO_NOTE_INPUT` | the instance's plugin takes no notes (an effect): its worker answers note_on, note_off and all_notes_off with this code |
| `-514` | `PHD_ERR_NOTES_BY_JACK` | the instance's worker takes no notes on its socket (mod-host): the daemon answers the note verbs and note_input with this code, and the plugin is played through its jack MIDI input with connect |
| `-515` | `PHD_ERR_NO_PRESET_LIST` | the instance's worker cannot list the plugin's presets (mod-host): the daemon answers preset_list and preset_get with this code; preset_load still takes a preset the controller knows |
| `-516` | `PHD_ERR_PIN_REFUSED` | the plugin's pin carries verdict=refused: its files are the ones that failed qualification, so add is refused and no worker sees it, and a replayed add is refused the same way; files that differ from the pin answer (-509) first. A pins_dir pin of that verdict is never overridden: pin_set and pin_accept of its plugin answer this code |
<!-- END GENERATED protocol:errors -->

A worker's own refusal is returned as it said it. An `add` that killed its worker answers mod-host's `-102`.
Expand Down Expand Up @@ -362,9 +364,30 @@ files, keeps the pin in the pin store (`$XDG_STATE_HOME/plugin-hostd/pins`) and
asked of the worker after the add (`pin_layout`) and recorded too. A host that pins by itself sets `pin_mode strict`.
The store, a `pin_file` and `pin_set` carry one line format, `pin_set <uri> <files> <layout> [verdict=<word>]` with the
verb optional in a file. In `first-use` only a pin with `verdict=in-process`, which plugin-qualify writes, lets a plugin
share a worker. A plugin that changed since it was pinned is refused (-509, or -510 from the worker), the
share a worker. A pin with `verdict=refused` names files that failed qualification. Their `add` answers `-516`
(`PIN_REFUSED`), no worker sees it, and a replay is refused with the same code. A pin that changes to `refused` while
the instance runs takes effect at its next replay. Files that differ from the pin answer `-509` first, because the
verdict was given for the pinned files only. A plugin that changed since it was pinned is refused (-509, or -510 from the worker), the
`pin_changed <uri> files|layout` event names what changed, and `pin_accept <uri>` takes the new pin.

Pins that ship with packages live in a read-only layer of their own, under the host's. At start the daemon reads
every `*.pins` file of `pins_dir` (`/usr/share/plugin-hostd/pins.d` by default; empty is none), in byte order of
their names, in the same line format. A package drops its own file there, so its plugins and their pins are replaced
in one transaction and removed together; plugin-hostd owns the directory and another package may co-own it. The
daemon never writes it. A pin from this layer is trusted as it is, in both pin modes. The rules:

- A pins.d pin with `verdict=refused` always holds. The pin store, a `pin_file` and `pin_set` never override it:
`pin_set` and `pin_accept` of its uri answer `-516` (`PIN_REFUSED`), and a line of the store or of the `pin_file`
for it is kept but has no effect, which stderr says at start.
- Any other pins.d pin is under the host's layer. A pin of the same uri in the store, the `pin_file` or from
`pin_set` holds over it, and `pin_accept` of a plugin whose files differ from its package pin records the new files
in the store, which then hold. `pin_clear` forgets the host's pin only: the package pin holds again.
- When two pins.d files hold the same uri, a refusal wins; otherwise the file last in name order wins. Two lines of
one file follow the same rule, the later line in place of the later file.
- A line outside the grammar, or longer than a pin line can be, is skipped and named on stderr; the rest of the file
is read. A file that cannot be read is named on stderr and skipped. A directory that is missing or empty holds no
pins and is no error.

What cannot be pinned:

<!-- BEGIN GENERATED protocol:pin-limits -->
Expand Down
1 change: 1 addition & 0 deletions debian/plugin-hostd.dirs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
usr/share/plugin-hostd/pins.d
29 changes: 28 additions & 1 deletion doc/plugin-hostd.1
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ or in
one
.\" BEGIN GENERATED protocol:man-keys
.RI \(dq key " value" \(dq
per line: mod_host, clap_host, lv2_worker, lv2_path, state_root, pin_mode, pin_store, pin_file, ready_timeout_ms, rpc_timeout_ms, backoff_base_ms, backoff_max_ms, storm_deaths, storm_window_ms, suspect_window_ms, checkpoint_ms, idle_ms, pool_max, require_pins.
per line: mod_host, clap_host, lv2_worker, lv2_path, state_root, pin_mode, pin_store, pin_file, pins_dir, ready_timeout_ms, rpc_timeout_ms, backoff_base_ms, backoff_max_ms, storm_deaths, storm_window_ms, suspect_window_ms, checkpoint_ms, idle_ms, pool_max, require_pins.
.\" END GENERATED protocol:man-keys
.SH PINS
With require_pins on,
Expand All @@ -90,6 +90,33 @@ A change after that is refused and announced by the
event until
.B pin_accept
takes the new pin.
.PP
Packages ship pins in a read\-only layer: every
.I *.pins
file of
.B pins_dir
(by default
.IR /usr/share/plugin\-hostd/pins.d )
is read at start, in name order, in the same line format.
A pin there with verdict=refused always holds: neither the pin store, the
.B pin_file
nor
.B pin_set
overrides it, and
.B pin_set
and
.B pin_accept
of its uri answer \-516.
Any other package pin is under the host's: a pin of the same uri in the store, the
.B pin_file
or from
.B pin_set
holds over it, and
.B pin_clear
lets the package pin hold again.
When two files hold the same uri a refusal wins, otherwise the last file by name.
A bad line is skipped and named on stderr; a missing or empty directory holds no pins.
.PP
What cannot be pinned:
.\" BEGIN GENERATED protocol:man-pin-limits
.IP \(bu 2
Expand Down
28 changes: 21 additions & 7 deletions include/plugin-hostd/protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ static inline int phd_pool_name_valid(const char *name)
#define PHD_ERR_NO_NOTE_INPUT (-513)
#define PHD_ERR_NOTES_BY_JACK (-514)
#define PHD_ERR_NO_PRESET_LIST (-515)
#define PHD_ERR_PIN_REFUSED (-516)

/* X(id, meaning) */
#define PHD_ERRORS(X) \
Expand Down Expand Up @@ -294,7 +295,12 @@ static inline int phd_pool_name_valid(const char *name)
"with connect") \
X(NO_PRESET_LIST, "the instance's worker cannot list the plugin's presets (mod-host): the daemon answers " \
PHD_VERB_PRESET_LIST " and " PHD_VERB_PRESET_GET " with this code; preset_load still takes a preset " \
"the controller knows")
"the controller knows") \
X(PIN_REFUSED, "the plugin's pin carries " PHD_PIN_VERDICT_KEY PHD_PIN_VERDICT_REFUSED ": its files are the ones that " \
"failed qualification, so add is refused and no worker sees it, and a replayed add is refused the " \
"same way; files that differ from the pin answer " PHD_STR(PHD_ERR_PIN_BINARY_MISMATCH) " first. A pins_dir " \
"pin of that verdict is never overridden: " PHD_VERB_PIN_SET " and " PHD_VERB_PIN_ACCEPT " of its plugin " \
"answer this code")

/* ---------------------------------------------------------------- verbs */

Expand Down Expand Up @@ -342,7 +348,7 @@ static inline int phd_pool_name_valid(const char *name)
#define PHD_PIN_MODE_STRICT "strict"
#define PHD_PIN_MODE_FIRST_USE "first-use"
#define PHD_PIN_MODES(X) \
X(STRICT, PHD_PIN_MODE_STRICT, "add admits only a plugin that has a pin; a pin comes from pin_set or pin_file") \
X(STRICT, PHD_PIN_MODE_STRICT, "add admits only a plugin that has a pin; a pin comes from pin_set, pin_file or pins_dir") \
X(FIRST_USE, PHD_PIN_MODE_FIRST_USE, "add of a plugin with no pin records its pin, keeps it in the pin store and places " \
"the instance " PHD_PLACE_OWN "; a later change is refused until pin_accept")

Expand Down Expand Up @@ -384,16 +390,20 @@ static inline int phd_pool_name_valid(const char *name)
"<verdict>]", "resp 0", \
"the pin of one plugin: the SHA-256 of every file the host loads for it, a path relative to the bundle (the " \
"directory of a single-file format's file), its layout fingerprint and, when it is known, the verdict of its qualification; " \
"it replaces an earlier pin of the uri. The pin store and a pin_file hold the same line, with or without the verb") \
X(PIN_CLEAR, PHD_VERB_PIN_CLEAR, "<uri> | " PHD_WORD_ALL, "resp 0", "forget the pin of one plugin, or of every one") \
"it replaces an earlier pin of the uri; -516 for a plugin whose pins_dir pin carries " PHD_PIN_VERDICT_KEY \
PHD_PIN_VERDICT_REFUSED ", which nothing overrides. The pin store, a pin_file and a pins_dir file hold the same " \
"line, with or without the verb") \
X(PIN_CLEAR, PHD_VERB_PIN_CLEAR, "<uri> | " PHD_WORD_ALL, "resp 0", "forget the pin of one plugin, or of every one; a pins_dir " \
"pin is read-only and holds again") \
X(LV2_WORKER_SET, PHD_VERB_LV2_WORKER_SET, "<uri> <" PHD_LV2_WORKER_MOD_HOST " | " PHD_LV2_WORKER_CLAP_HOST " | " \
PHD_LV2_WORKER_AUTO " | " PHD_WORD_NONE ">", "resp 0", \
"the worker of one LV2 plugin, for its adds from now on: it overrides the lv2_worker setting, " PHD_WORD_NONE " takes the " \
"override back; -902 for a word that is none of these. Like policy_set it is the controller's, not in the ledger, gone with the daemon") \
X(PIN_ACCEPT, PHD_VERB_PIN_ACCEPT, "<uri>", "resp 0", \
"pin_mode " PHD_PIN_MODE_FIRST_USE " only: take the plugin's files as they are now as its pin, after a " \
PHD_EVENT_PIN_CHANGED " event; the layout is learned again by the next add and the verdict is dropped; -508 when " \
"the plugin has no pin, -509 when its files cannot be collected, -902 under pin_mode strict") \
"the plugin has no pin, -509 when its files cannot be collected, -516 when its pins_dir pin carries " \
PHD_PIN_VERDICT_KEY PHD_PIN_VERDICT_REFUSED ", -902 under pin_mode strict") \
X(PIN_LAYOUT, PHD_VERB_PIN_LAYOUT, "<instance>", "resp 0 <scheme>" PHD_PIN_SCHEME_SEPARATOR "<sha256>", \
"sent by the daemon to a worker of a format that checks layouts, after it accepted the add of a plugin whose layout pin is " \
"still unknown: the layout the worker found after init, which the daemon records in the pin") \
Expand Down Expand Up @@ -746,6 +756,7 @@ static inline int phd_is_note_verb(const char *verb)
#define PHD_DEFAULT_POOL_MAX 8
#define PHD_DEFAULT_REQUIRE_PINS 1
#define PHD_DEFAULT_PIN_MODE PHD_PIN_MODE_FIRST_USE
#define PHD_DEFAULT_PINS_DIR "/usr/share/plugin-hostd/pins.d"

/* the settings file: one "key value" per line, '#' starts a comment.
* A string setting: X(key, size, default, env, meaning); env names the variable that stands in for the default when
Expand All @@ -760,10 +771,13 @@ static inline int phd_is_note_verb(const char *verb)
X(lv2_path, pathlist, PHD_DEFAULT_LV2_PATH, "LV2_PATH", "where bundles are searched") \
X(state_root, path, PHD_DEFAULT_STATE_ROOT, "XDG_RUNTIME_DIR", "the daemon's checkpoints and worker logs") \
X(pin_mode, word, PHD_DEFAULT_PIN_MODE, "", "strict or first-use, while require_pins is 1: first-use records the pin " \
"of a plugin on its first add, strict admits only a plugin a controller or a pin_file pinned") \
"of a plugin on its first add, strict admits only a plugin a controller, a pin_file or pins_dir pinned") \
X(pin_store, path, "", "", "the file first-use pins are kept in; empty is $XDG_STATE_HOME/plugin-hostd/pins, " \
"else $HOME/.local/state/plugin-hostd/pins") \
X(pin_file, path, "", "", "a file of pin lines loaded at start, as plugin-qualify --all --pins writes it; empty is none")
X(pin_file, path, "", "", "a file of pin lines loaded at start, as plugin-qualify --all --pins writes it; empty is none") \
X(pins_dir, path, PHD_DEFAULT_PINS_DIR, "", "the read-only pins of packages: every file in it whose name ends in .pins, read at start " \
"in name order, under the store, the pin_file and pin_set, except that a pin of verdict=refused there always holds; " \
"empty is none")

/* A whole-number setting: X(key, default, unit, meaning); 0 turns off where the meaning says so */
#define PHD_CONF_INTS(X) \
Expand Down
2 changes: 2 additions & 0 deletions packaging/plugin-hostd.spec
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ make test-daemon check-generated
%{_mandir}/man1/plugin-hostd-probe.1*
%{_datadir}/plugin-hostd/protocol.json
%{_datadir}/plugin-hostd/mod-host.json
# the read-only pins of packages: a package that ships pins co-owns it with %dir
%dir %{_datadir}/plugin-hostd/pins.d

%files tests
%license COPYING
Expand Down
Loading
Loading