Skip to content
Open
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
6 changes: 6 additions & 0 deletions doc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ All notable changes to the project are documented in this file.

### Added

- Add a `log` RPC to `infix-syslog`, and a matching admin-exec `log`
command in the CLI, for injecting messages in the system log over
NETCONF/RESTCONF, issue #1639. The full RFC 5424 header is supported:
severity, facility, app-name, msgid, and structured data. Messages
are time stamped on arrival and follow the configured syslog filtering
and forwarding rules, like any locally generated message
- Add `/system/advanced` for low-level system customization, issue #463:
- `rc.d`: user scripts stored in the configuration, run once at boot
after the startup configuration has been applied, in the order listed
Expand Down
58 changes: 58 additions & 0 deletions doc/syslog.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,64 @@ admin@example:/config/syslog/…/file:foobar/> <b>leave</b>
admin@example:/>
</code></pre>

## Logging Messages

Scripts and test systems can add their own messages to the system log,
e.g., to mark the start and end of a test run. Messages are handed to
the local syslog daemon as if generated on the device: they are time
stamped on arrival and follow the same filtering and forwarding rules
as any other message. So where a message ends up, a log file, a remote
server, or both, is decided by the syslog configuration, not the
caller.

From the CLI, the admin-exec `log` command takes the message text, with
optional `severity`, `facility`, and `msgid` keywords before it:

<pre class="cli"><code>admin@example:/> <b>log Kilroy was here</b>
admin@example:/> <b>log severity warning facility daemon msgid test-start Test 42 starting</b>
admin@example:/> <b>show log tail 2</b>
Sep 15 15:29:01 example admin: Kilroy was here
Sep 15 15:29:07 example admin: Test 42 starting
</code></pre>

Over NETCONF and RESTCONF the same operation is available as the
`infix-syslog:log` RPC, which also exposes RFC 5424 structured data:

```bash
~$ curl -k -u admin:admin -X POST \
-H "Content-Type: application/yang-data+json" \
https://example.local/restconf/operations/infix-syslog:log \
-d '{"infix-syslog:input": {
"message": "Test 42 starting",
"severity": "warning",
"facility": "ietf-syslog:daemon",
"app-name": "infamy",
"msgid": "test-start",
"structured-data": [{
"id": "test@32473",
"param": [{"name": "name", "value": "syslog/rpc_log"}]
}]
}}'
```

| **Field** | **Default** | **Description** |
|-------------------|--------------|--------------------------------------------------------------|
| `message` | *mandatory* | Free-form message text |
| `severity` | `notice` | Same levels as in the facility filters, `emergency`..`debug` |
| `facility` | `user` | Any facility from the table at the end of this document |
| `app-name` | calling user | RFC 5424 APP-NAME, shown as the tag in log files |
| `msgid` | none | RFC 5424 MSGID, e.g., `test-start` |
| `structured-data` | none | RFC 5424 SD elements, each an `id` with `name`/`value` params |

In an [RFC5424][] formatted log file the message above is logged as:

```
2026-09-15T15:29:07.123456+02:00 example infamy - test-start [test@32473 name="syslog/rpc_log"] Test 42 starting
```

The `msgid` property filter, see [Property-Based Filtering](#property-based-filtering),
can be used to route such messages to a dedicated log file.

## Log to Remote Server

Logging to a remote syslog server is the recommended way of supervising
Expand Down
2 changes: 1 addition & 1 deletion package/confd/confd.mk
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
CONFD_LICENSE = BSD-3-Clause
CONFD_LICENSE_FILES = LICENSE
CONFD_REDISTRIBUTE = NO
CONFD_DEPENDENCIES = host-sysrepo sysrepo rousette netopeer2 jansson libite sysrepo libsrx libglib2 libev
CONFD_DEPENDENCIES = host-sysrepo sysrepo rousette netopeer2 jansson libite sysrepo libsrx libglib2 libev sysklogd
CONFD_AUTORECONF = YES
CONFD_CONF_OPTS += --disable-silent-rules --with-crypt=$(BR2_PACKAGE_CONFD_DEFAULT_CRYPT)
CONFD_SYSREPO_SHM_PREFIX = sr_buildroot$(subst /,_,$(CONFIG_DIR))_confd
Expand Down
26 changes: 23 additions & 3 deletions src/bin/copy.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <sys/types.h>
#include <sys/wait.h>

#include <libyang/libyang.h>
#include <sysrepo.h>
#include <sysrepo/netconf_acm.h>
#include <sysrepo/values.h>
Expand Down Expand Up @@ -341,6 +342,9 @@ static int sysrepo_init(sr_conn_ctx_t **conn, sr_session_ctx_t **sess,
goto fail;
}

/* Like klish-plugin-sysrepo, lets RPC callbacks see who called */
sr_session_set_orig_name(*sess, user);

return SR_ERR_OK;
fail:
sysrepo_print_error(*sess);
Expand Down Expand Up @@ -859,7 +863,7 @@ static int usage_rpc(int rc)
"Arguments:\n"
" rpc-xpath RPC XPath (e.g., /ietf-system:set-current-datetime)\n"
" key value Pairs of RPC argument names and values\n"
" Values can be comma-separated for lists/leaf-lists\n"
" Leaf-list values can be comma-separated\n"
"\n"
"Examples:\n"
" %s /ietf-system:set-current-datetime current-datetime \"2025-01-01T00:00:00Z\"\n"
Expand All @@ -870,6 +874,22 @@ static int usage_rpc(int rc)
return rc;
}

static bool is_leaflist(sr_conn_ctx_t *conn, const char *rpc_xpath, const char *key)
{
char xpath[strlen(rpc_xpath) + strlen(key) + 2];
const struct lysc_node *node;
const struct ly_ctx *ctx;
bool rc;

snprintf(xpath, sizeof(xpath), "%s/%s", rpc_xpath, key);
ctx = sr_acquire_context(conn);
node = lys_find_path(ctx, NULL, xpath, 0);
rc = node && node->nodetype == LYS_LEAFLIST;
sr_release_context(conn);

return rc;
}

/* Execute RPC from CLI arguments: xpath and key-value pairs */
static int rpc_exec(const char *rpc_xpath, int argc, char *argv[])
{
Expand All @@ -892,8 +912,8 @@ static int rpc_exec(const char *rpc_xpath, int argc, char *argv[])
const char *val = argv[i + 1];
char *val_copy, *token, *saveptr;

/* Check if value contains commas - split into multiple values */
if (strchr(val, ',')) {
/* Comma-separated values are only a list for leaf-lists */
if (strchr(val, ',') && is_leaflist(conn, rpc_xpath, key)) {
val_copy = strdup(val);
if (!val_copy) {
warnx("Memory allocation failed");
Expand Down
1 change: 1 addition & 0 deletions src/confd/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ PKG_CHECK_MODULES([libite], [libite >= 2.6.1])
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 4.2.10])
PKG_CHECK_MODULES([libyang], [libyang >= 4.2.2])
PKG_CHECK_MODULES([libsrx], [libsrx >= 1.0.0])
PKG_CHECK_MODULES([libsyslog], [libsyslog >= 2.7.0])
PKG_CHECK_MODULES([libcrypto], [libcrypto])

AC_CHECK_HEADER([ev.h],
Expand Down
4 changes: 3 additions & 1 deletion src/confd/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ confd_plugin_la_CFLAGS = \
$(libcrypto_CFLAGS) \
$(sysrepo_CFLAGS) \
$(libsrx_CFLAGS) \
$(libsyslog_CFLAGS) \
$(CFLAGS)

confd_plugin_la_LIBADD = \
Expand All @@ -29,7 +30,8 @@ confd_plugin_la_LIBADD = \
$(libite_LIBS) \
$(libcrypto_LIBS) \
$(sysrepo_LIBS) \
$(libsrx_LIBS)
$(libsrx_LIBS) \
$(libsyslog_LIBS)

confd_plugin_la_SOURCES = \
base64.c base64.h \
Expand Down
4 changes: 4 additions & 0 deletions src/confd/src/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,10 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
if (rc)
goto err;

rc = syslog_rpc_init(&confd);
if (rc)
goto err;

/* Candidate infer configurations */
rc = interfaces_cand_init(&confd);
if (rc)
Expand Down
14 changes: 14 additions & 0 deletions src/confd/src/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ typedef enum {
if ((rc = register_rpc(s, x, c, a, u))) \
goto fail

#define REGISTER_RPC_TREE(s,x,c,a,u) \
if ((rc = register_rpc_tree(s, x, c, a, u))) \
goto fail

struct confd {
sr_session_ctx_t *session; /* running datastore */
sr_session_ctx_t *startup; /* startup datastore */
Expand Down Expand Up @@ -192,6 +196,15 @@ static inline int register_rpc(sr_session_ctx_t *session, const char *xpath,
return rc;
}

static inline int register_rpc_tree(sr_session_ctx_t *session, const char *xpath,
sr_rpc_tree_cb cb, void *arg, sr_subscription_ctx_t **sub)
{
int rc = sr_rpc_subscribe_tree(session, xpath, cb, arg, 0, SR_SUBSCR_NO_THREAD, sub);
if (rc)
ERROR("failed subscribing to %s rpc: %s", xpath, sr_strerror(rc));
return rc;
}


/* core.c */
int finit_enable(const char *svc);
Expand All @@ -211,6 +224,7 @@ int interfaces_cand_init(struct confd *confd);

/* syslog.c */
int syslog_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
int syslog_rpc_init(struct confd *confd);

/* system.c */
int system_rpc_init (struct confd *confd);
Expand Down
Loading