The management module: reads and controls the runtime through plain
operations (below) that take and return sig_value trees and sig_status_t
codes. It knows nothing of any transport or wire format; a transport
(sig_mgmt_http, or anything else) maps its own framing onto these calls.
Register the module with sig_runtime_register_module(rt,
sig_mgmt_module(), NULL) BEFORE any module that depends on it.
Finds the mgmt instance created for `rt` (SIG_ERR_NOT_FOUND if the
module is not registered, SIG_ERR_STATE if it is registered but not
yet created).
sig_mgmt_system
sig_status_t sig_mgmt_system(sig_mgmt * m, sig_value * out)
Parameter
Type
m
sig_mgmt *
out
sig_value *
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
sig_mgmt_list_services
sig_status_t sig_mgmt_list_services(sig_mgmt * m, sig_value * out)
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
sig_mgmt_list_capsules
sig_status_t sig_mgmt_list_capsules(sig_mgmt * m, sig_value * out)
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
sig_mgmt_list_configurations
sig_status_t sig_mgmt_list_configurations(sig_mgmt * m, sig_value * out)
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
---- Read operations ---------------------------------------------------
Each fills *out with a map/list tree the caller releases with
sig_value_dtor() on SIG_OK (*out is untouched on failure). The tree is built
while the shared lock is held and owns all its memory (deep copies, no
pointers into runtime state), so it stays valid after the call returns, and
concurrent readers do not block one another. Lists are sorted by name. A
property value is copied into the tree as it is, whatever its kind; deciding
how to show kinds a transport cannot express is the transport's business.
Errors: SIG_ERR_INVALID_ARG for a NULL argument; SIG_ERR_NOT_FOUND for an
object that does not exist, which includes any name that could not name one
(empty, over 64 characters, or containing a character other than letters,
digits, '_' and '-', e.g. ".." or "/"); SIG_ERR_STATE when there is no
project (runtime not running); SIG_ERR_ALLOC.
Trees (field names are part of the contract; state is "running" or
"stopped"):
system: {version, uptime_seconds, modules: [name]}
list_services: {services: [{name, state, dirty, instances}]}
get_service: {name, state, dirty,
instances: [{name, configuration, type,
inputs: [name], outputs: [name]}],
links: [{from: {instance, terminal},
to: {instance, terminal}}]}
get_instance: {name, service, configuration, type,
properties: {key: value}}
list_capsules: {capsules: [{name, category?, inputs, outputs,
properties: [{name, type, required, default?, min?,
max?, choices?, description}]}]}
list_configurations: {configurations: [{name, type}]}
get_configuration: {name, type, config: <definition>, used_by: [service]}
get_service_config: {name, config: <definition>, dirty}
---- Action and edit operations -----------------------------------------
Locking. An operation never calls another operation while holding the
lock. Edit operations hold the exclusive lock only while the project is
edited (they wait for in-flight readers to drain, and readers wait for them);
the result tree is built afterwards from text the edit copied out, so no
project pointer outlives the lock. start/stop/restart/shutdown hold none of
this module's lock while the runtime runs the service's hooks, which may
block for long; only the short lookup that builds `out_state` takes the
shared lock, plus a short exclusive section when a stopped service has to be
rebuilt from an edited definition (the old live service is freed, which no
reader may be looking at). The rebuild only ever runs in that exclusive
section; the hooks never do. Residual stall, inherent in sig_runtime_edit():
an edit or rebuild that waits on the runtime's control mutex behind a slow
start of another service holds the exclusive lock while it waits, so reads
stall until that start returns.
Actions.
sig_mgmt_start_service / _stop_service / _restart_service: on SIG_OK
*out_state is the map {name, state} ("running" | "stopped"), owned by the
caller (sig_value_dtor), untouched on failure. Stopping a stopped service
is SIG_OK. They block until the service's hooks have run. Restart is not
one atomic runtime call: it is a stop followed by a start, so a concurrent
start can win in between (SIG_ERR_EXISTS), and if the rebuild or the start
fails the service is left stopped. A start that is repeatedly overtaken by
a concurrent stop or edit of the same service gives up after a few
attempts with SIG_ERR_STATE.
Errors: SIG_ERR_NOT_FOUND (no such service, including any name that could
not name one); SIG_ERR_EXISTS (start of a running service);
SIG_ERR_STATE (runtime not running, or the service vanished before its
state could be read); SIG_ERR_INVALID_ARG (a NULL argument, or the service
could not start because a capsule configuration is invalid; the host log
has the detail); anything else the runtime returns is passed through.
sig_mgmt_request_shutdown asks the runtime to shut down and returns
SIG_OK (SIG_ERR_INVALID_ARG for NULL); it does not wait.
Edits. Each takes the document as text, `doc` of `doc_len` bytes, exactly as
the project layer's put/patch functions do; `errmsg` (optional, may be NULL)
receives a NUL-terminated reason of at most errmsg_len-1 bytes, empty unless
stated below. `out` receives an owned tree on SIG_OK and is untouched on
failure:
put/patch_configuration: {name, config: <definition>}
put/patch_service: {name, config: <definition>, dirty}
rename_configuration: {name: <new name>, config: <definition>}
`*created` (put only) tells whether the object is new. Rename takes the new
name as a plain string. On failure nothing was changed.
Errors, and what `errmsg` carries with each:
SIG_ERR_INVALID_ARG a NULL argument; a name or new name the project
refuses (empty, too long, or with a character other
than letters, digits, '_' and '-'); a new name of 80
bytes or more; an empty or NULL `doc` (errmsg is
"document required"). errmsg is empty for every
other cause, so a non-empty errmsg names the empty
document.
SIG_ERR_PARSE the document failed validation; errmsg is the
project's reason for configurations, and empty for
services (the reason is only in the host log).
SIG_ERR_NOT_FOUND no such configuration or service (put creates, so only
patch, delete and rename report it).
SIG_ERR_EXISTS delete of a configuration a service uses (errmsg
empty), or rename onto a name that is taken, which
includes renaming to its own name (errmsg is the
sentence naming the taken name).
SIG_ERR_STATE the runtime is not running.
other a failure inside the runtime or project.
An edit that succeeded in the project but could not build `out` returns
SIG_ERR_ALLOC with the edit already applied.
---- Action and edit operations -----------------------------------------
Locking. An operation never calls another operation while holding the
lock. Edit operations hold the exclusive lock only while the project is
edited (they wait for in-flight readers to drain, and readers wait for them);
the result tree is built afterwards from text the edit copied out, so no
project pointer outlives the lock. start/stop/restart/shutdown hold none of
this module's lock while the runtime runs the service's hooks, which may
block for long; only the short lookup that builds `out_state` takes the
shared lock, plus a short exclusive section when a stopped service has to be
rebuilt from an edited definition (the old live service is freed, which no
reader may be looking at). The rebuild only ever runs in that exclusive
section; the hooks never do. Residual stall, inherent in sig_runtime_edit():
an edit or rebuild that waits on the runtime's control mutex behind a slow
start of another service holds the exclusive lock while it waits, so reads
stall until that start returns.
Actions.
sig_mgmt_start_service / _stop_service / _restart_service: on SIG_OK
*out_state is the map {name, state} ("running" | "stopped"), owned by the
caller (sig_value_dtor), untouched on failure. Stopping a stopped service
is SIG_OK. They block until the service's hooks have run. Restart is not
one atomic runtime call: it is a stop followed by a start, so a concurrent
start can win in between (SIG_ERR_EXISTS), and if the rebuild or the start
fails the service is left stopped. A start that is repeatedly overtaken by
a concurrent stop or edit of the same service gives up after a few
attempts with SIG_ERR_STATE.
Errors: SIG_ERR_NOT_FOUND (no such service, including any name that could
not name one); SIG_ERR_EXISTS (start of a running service);
SIG_ERR_STATE (runtime not running, or the service vanished before its
state could be read); SIG_ERR_INVALID_ARG (a NULL argument, or the service
could not start because a capsule configuration is invalid; the host log
has the detail); anything else the runtime returns is passed through.
sig_mgmt_request_shutdown asks the runtime to shut down and returns
SIG_OK (SIG_ERR_INVALID_ARG for NULL); it does not wait.
Edits. Each takes the document as text, `doc` of `doc_len` bytes, exactly as
the project layer's put/patch functions do; `errmsg` (optional, may be NULL)
receives a NUL-terminated reason of at most errmsg_len-1 bytes, empty unless
stated below. `out` receives an owned tree on SIG_OK and is untouched on
failure:
put/patch_configuration: {name, config: <definition>}
put/patch_service: {name, config: <definition>, dirty}
rename_configuration: {name: <new name>, config: <definition>}
`*created` (put only) tells whether the object is new. Rename takes the new
name as a plain string. On failure nothing was changed.
Errors, and what `errmsg` carries with each:
SIG_ERR_INVALID_ARG a NULL argument; a name or new name the project
refuses (empty, too long, or with a character other
than letters, digits, '_' and '-'); a new name of 80
bytes or more; an empty or NULL `doc` (errmsg is
"document required"). errmsg is empty for every
other cause, so a non-empty errmsg names the empty
document.
SIG_ERR_PARSE the document failed validation; errmsg is the
project's reason for configurations, and empty for
services (the reason is only in the host log).
SIG_ERR_NOT_FOUND no such configuration or service (put creates, so only
patch, delete and rename report it).
SIG_ERR_EXISTS delete of a configuration a service uses (errmsg
empty), or rename onto a name that is taken, which
includes renaming to its own name (errmsg is the
sentence naming the taken name).
SIG_ERR_STATE the runtime is not running.
other a failure inside the runtime or project.
An edit that succeeded in the project but could not build `out` returns
SIG_ERR_ALLOC with the edit already applied.
---- Action and edit operations -----------------------------------------
Locking. An operation never calls another operation while holding the
lock. Edit operations hold the exclusive lock only while the project is
edited (they wait for in-flight readers to drain, and readers wait for them);
the result tree is built afterwards from text the edit copied out, so no
project pointer outlives the lock. start/stop/restart/shutdown hold none of
this module's lock while the runtime runs the service's hooks, which may
block for long; only the short lookup that builds `out_state` takes the
shared lock, plus a short exclusive section when a stopped service has to be
rebuilt from an edited definition (the old live service is freed, which no
reader may be looking at). The rebuild only ever runs in that exclusive
section; the hooks never do. Residual stall, inherent in sig_runtime_edit():
an edit or rebuild that waits on the runtime's control mutex behind a slow
start of another service holds the exclusive lock while it waits, so reads
stall until that start returns.
Actions.
sig_mgmt_start_service / _stop_service / _restart_service: on SIG_OK
*out_state is the map {name, state} ("running" | "stopped"), owned by the
caller (sig_value_dtor), untouched on failure. Stopping a stopped service
is SIG_OK. They block until the service's hooks have run. Restart is not
one atomic runtime call: it is a stop followed by a start, so a concurrent
start can win in between (SIG_ERR_EXISTS), and if the rebuild or the start
fails the service is left stopped. A start that is repeatedly overtaken by
a concurrent stop or edit of the same service gives up after a few
attempts with SIG_ERR_STATE.
Errors: SIG_ERR_NOT_FOUND (no such service, including any name that could
not name one); SIG_ERR_EXISTS (start of a running service);
SIG_ERR_STATE (runtime not running, or the service vanished before its
state could be read); SIG_ERR_INVALID_ARG (a NULL argument, or the service
could not start because a capsule configuration is invalid; the host log
has the detail); anything else the runtime returns is passed through.
sig_mgmt_request_shutdown asks the runtime to shut down and returns
SIG_OK (SIG_ERR_INVALID_ARG for NULL); it does not wait.
Edits. Each takes the document as text, `doc` of `doc_len` bytes, exactly as
the project layer's put/patch functions do; `errmsg` (optional, may be NULL)
receives a NUL-terminated reason of at most errmsg_len-1 bytes, empty unless
stated below. `out` receives an owned tree on SIG_OK and is untouched on
failure:
put/patch_configuration: {name, config: <definition>}
put/patch_service: {name, config: <definition>, dirty}
rename_configuration: {name: <new name>, config: <definition>}
`*created` (put only) tells whether the object is new. Rename takes the new
name as a plain string. On failure nothing was changed.
Errors, and what `errmsg` carries with each:
SIG_ERR_INVALID_ARG a NULL argument; a name or new name the project
refuses (empty, too long, or with a character other
than letters, digits, '_' and '-'); a new name of 80
bytes or more; an empty or NULL `doc` (errmsg is
"document required"). errmsg is empty for every
other cause, so a non-empty errmsg names the empty
document.
SIG_ERR_PARSE the document failed validation; errmsg is the
project's reason for configurations, and empty for
services (the reason is only in the host log).
SIG_ERR_NOT_FOUND no such configuration or service (put creates, so only
patch, delete and rename report it).
SIG_ERR_EXISTS delete of a configuration a service uses (errmsg
empty), or rename onto a name that is taken, which
includes renaming to its own name (errmsg is the
sentence naming the taken name).
SIG_ERR_STATE the runtime is not running.
other a failure inside the runtime or project.
An edit that succeeded in the project but could not build `out` returns
SIG_ERR_ALLOC with the edit already applied.
sig_mgmt_request_shutdown
sig_status_t sig_mgmt_request_shutdown(sig_mgmt * m)
---- Action and edit operations -----------------------------------------
Locking. An operation never calls another operation while holding the
lock. Edit operations hold the exclusive lock only while the project is
edited (they wait for in-flight readers to drain, and readers wait for them);
the result tree is built afterwards from text the edit copied out, so no
project pointer outlives the lock. start/stop/restart/shutdown hold none of
this module's lock while the runtime runs the service's hooks, which may
block for long; only the short lookup that builds `out_state` takes the
shared lock, plus a short exclusive section when a stopped service has to be
rebuilt from an edited definition (the old live service is freed, which no
reader may be looking at). The rebuild only ever runs in that exclusive
section; the hooks never do. Residual stall, inherent in sig_runtime_edit():
an edit or rebuild that waits on the runtime's control mutex behind a slow
start of another service holds the exclusive lock while it waits, so reads
stall until that start returns.
Actions.
sig_mgmt_start_service / _stop_service / _restart_service: on SIG_OK
*out_state is the map {name, state} ("running" | "stopped"), owned by the
caller (sig_value_dtor), untouched on failure. Stopping a stopped service
is SIG_OK. They block until the service's hooks have run. Restart is not
one atomic runtime call: it is a stop followed by a start, so a concurrent
start can win in between (SIG_ERR_EXISTS), and if the rebuild or the start
fails the service is left stopped. A start that is repeatedly overtaken by
a concurrent stop or edit of the same service gives up after a few
attempts with SIG_ERR_STATE.
Errors: SIG_ERR_NOT_FOUND (no such service, including any name that could
not name one); SIG_ERR_EXISTS (start of a running service);
SIG_ERR_STATE (runtime not running, or the service vanished before its
state could be read); SIG_ERR_INVALID_ARG (a NULL argument, or the service
could not start because a capsule configuration is invalid; the host log
has the detail); anything else the runtime returns is passed through.
sig_mgmt_request_shutdown asks the runtime to shut down and returns
SIG_OK (SIG_ERR_INVALID_ARG for NULL); it does not wait.
Edits. Each takes the document as text, `doc` of `doc_len` bytes, exactly as
the project layer's put/patch functions do; `errmsg` (optional, may be NULL)
receives a NUL-terminated reason of at most errmsg_len-1 bytes, empty unless
stated below. `out` receives an owned tree on SIG_OK and is untouched on
failure:
put/patch_configuration: {name, config: <definition>}
put/patch_service: {name, config: <definition>, dirty}
rename_configuration: {name: <new name>, config: <definition>}
`*created` (put only) tells whether the object is new. Rename takes the new
name as a plain string. On failure nothing was changed.
Errors, and what `errmsg` carries with each:
SIG_ERR_INVALID_ARG a NULL argument; a name or new name the project
refuses (empty, too long, or with a character other
than letters, digits, '_' and '-'); a new name of 80
bytes or more; an empty or NULL `doc` (errmsg is
"document required"). errmsg is empty for every
other cause, so a non-empty errmsg names the empty
document.
SIG_ERR_PARSE the document failed validation; errmsg is the
project's reason for configurations, and empty for
services (the reason is only in the host log).
SIG_ERR_NOT_FOUND no such configuration or service (put creates, so only
patch, delete and rename report it).
SIG_ERR_EXISTS delete of a configuration a service uses (errmsg
empty), or rename onto a name that is taken, which
includes renaming to its own name (errmsg is the
sentence naming the taken name).
SIG_ERR_STATE the runtime is not running.
other a failure inside the runtime or project.
An edit that succeeded in the project but could not build `out` returns
SIG_ERR_ALLOC with the edit already applied.