diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md
index 1828d1e8..9b67a92b 100644
--- a/doc/ChangeLog.md
+++ b/doc/ChangeLog.md
@@ -19,6 +19,14 @@ All notable changes to the project are documented in this file.
- Fix annoying "cannot deselect all services" or reset to YANG default in the
web interface's firewall configuration page
+- Fix `statd` timeout warnings in the log. Updates to the mDNS neighbor
+ table, and status snapshots, could fail with:
+
+ statd[3658]: mdns: sr_apply_changes: Timeout expired
+ statd[3658]: Error, getting operational data: User callback failed
+
+ Such operations are now allowed up to 60 seconds to complete, same as for
+ other services in the system
[v26.06.0][] - 2026-07-01
-------------------------
diff --git a/doc/virtual.md b/doc/virtual.md
index 52c12d5a..bda20426 100644
--- a/doc/virtual.md
+++ b/doc/virtual.md
@@ -1,5 +1,5 @@
Virtual Environments
-=====================
+====================
Infix primarily targets real hardware, deployment to the cloud is not a
priority at the moment. However, for development and testing purposes
@@ -14,28 +14,35 @@ QEMU
> [!TIP]
> Installation for Debian/Ubuntu based systems can be done by "simply":
-> sudo apt install virt-manager -- dependencies ensure the
-> relevant Qemu packages are pulled in as well. This trick, installing
+> sudo apt install virt-manager -- dependencies ensure the relevant
+> Qemu packages are pulled in as well. This trick, installing
> [virt-manager][virt], helps set up Qemu networking on your system.
-A virtualized Infix x86_64 instance can be launched from a Linux system,
-with [Qemu][] installed, by issuing:
+A virtualized Infix x86_64 instance can be launched from a Linux system, with
+[Qemu][] installed, by issuing:
```
$ ./qemu/run.sh
...
```
-from an unpacked [release tarball][rels]. From a built source tree of
-Infix the same functionality is bundled as:
+from an unpacked [release tarball][rels]. From a built source tree of Infix
+the same functionality is bundled as:
```
$ make run
...
```
-To change settings, e.g. networking, make run-menuconfig, or
-from a pre-built Infix release tarball, using ./qemu/run.sh -c
+To change settings, e.g. networking, RAM, or number of interfaces, use
+make run-menuconfig from a built source tree. The same
+functionality is available from a release tarball as the optional `-c`
+argument: ./qemu/run.sh -c brings up a menuconfig dialog, select
+`Exit` and save the changes, then start Qemu as usual.
+
+> [!NOTE]
+> The `-c` option requires the `kconfig-frontends` package, on
+> Debian/Ubuntu systems: sudo apt install kconfig-frontends
The Infix test suite is built around Qemu and [Qeneth][qeth], see:
@@ -45,17 +52,24 @@ The Infix test suite is built around Qemu and [Qeneth][qeth], see:
GNS3
----
-Download the [latest build][rels] of the `x86_64`, or `aarch64` if your
-host machine is Arm. Unpack the tarball in a dedicated directory and
-use ["Import Appliance"][APPL] to install the `.gns3a` file into
-[GNS3][].
+The Infix appliance is available directly from the [GNS3 Marketplace][mket] --
+release tarballs no longer include a `.gns3a` file. To [install it][APPL],
+open GNS3 and go to **File → New Template**, select *Install an appliance from
+the GNS3 server*, and search for Infix. When asked for a disk image, click
+**Download** to fetch it directly, or point GNS3 to a `.qcow2` file downloaded
+from the [releases page][rels].
-Infix is in the "Router" category, it comes with 10 interfaces available
-by default for use as switch ports or routing.
+Infix is in the "Router" category, it comes with 10 interfaces available by
+default for use as switch ports or routing.
+
+For a complete walk-through, from installing GNS3 to building and verifying
+your first topology, see the blog post [Infix in GNS3][blog].
[Qemu]: https://www.qemu.org/
[GNS3]: https://gns3.com/
[virt]: https://virt-manager.org/
[rels]: https://github.com/kernelkit/infix/releases
[qeth]: https://github.com/wkz/qeneth
-[APPL]: https://docs.gns3.com/docs/using-gns3/beginners/import-gns3-appliance/
+[mket]: https://gns3.com/marketplace/appliances/infix
+[blog]: https://www.kernelkit.org/posts/infix-in-gns3/
+[APPL]: https://docs.gns3.com/docs/using-gns3/beginners/install-from-marketplace/
diff --git a/src/confd/src/factory.c b/src/confd/src/factory.c
index 283aa1dc..450163e9 100644
--- a/src/confd/src/factory.c
+++ b/src/confd/src/factory.c
@@ -23,7 +23,7 @@ static int rpc(sr_session_ctx_t *session, uint32_t sub_id, const char *xpath,
DEBUG("%s", xpath);
sr_session_switch_ds(session, SR_DS_RUNNING);
- rc = sr_copy_config(session, NULL, SR_DS_FACTORY_DEFAULT, 60000);
+ rc = sr_copy_config(session, NULL, SR_DS_FACTORY_DEFAULT, SYSREPO_TIMEOUT);
if (rc) {
sr_session_set_netconf_error(session, "application", "operation-failed",
NULL, xpath, sr_strerror(rc), 0);
diff --git a/src/libsrx/src/common.h b/src/libsrx/src/common.h
index cb6fdaab..a336490e 100644
--- a/src/libsrx/src/common.h
+++ b/src/libsrx/src/common.h
@@ -10,6 +10,12 @@
extern int debug;
+/*
+ * Default timeout (ms) for sysrepo operations, same as our frontends.
+ * Use instead of 0 -- callbacks may be starved out on loaded systems.
+ */
+#define SYSREPO_TIMEOUT 60000
+
/* In IETF referred to LOG_AUDIT */
#ifndef LOG_AUDIT
#define LOG_AUDIT (13<<3) /* Log audit, for audit trails */
diff --git a/src/statd/avahi.c b/src/statd/avahi.c
index b6619cc5..d54bf3c4 100644
--- a/src/statd/avahi.c
+++ b/src/statd/avahi.c
@@ -441,7 +441,7 @@ static void ds_push_resolver(struct mdns_ctx *ctx, struct avahi_service *svc,
return;
}
- err = sr_apply_changes(ctx->sr_ses, 0);
+ err = sr_apply_changes(ctx->sr_ses, SYSREPO_TIMEOUT);
if (err)
ERROR("mdns: sr_apply_changes: %s", sr_strerror(err));
}
@@ -470,7 +470,7 @@ static void ds_delete_neighbor(struct mdns_ctx *ctx, const char *hostname)
static void ds_clear_all(struct mdns_ctx *ctx)
{
sr_delete_item(ctx->sr_ses, XPATH_BASE, 0);
- sr_apply_changes(ctx->sr_ses, 0);
+ sr_apply_changes(ctx->sr_ses, SYSREPO_TIMEOUT);
}
/* --------------------------------------------------------------------------
@@ -641,7 +641,7 @@ static void service_browser_cb(AvahiServiceBrowser *b,
}
}
- sr_apply_changes(ctx->sr_ses, 0);
+ sr_apply_changes(ctx->sr_ses, SYSREPO_TIMEOUT);
break;
}
diff --git a/src/statd/journal.c b/src/statd/journal.c
index b515c1f1..3b4870f7 100644
--- a/src/statd/journal.c
+++ b/src/statd/journal.c
@@ -133,7 +133,7 @@ static void journal_timer_cb(struct ev_loop *, struct ev_timer *w, int)
* This triggers our own operational callbacks running in main thread
*/
DEBUG("Calling sr_get_data on session %p", jctx->sr_query_ses);
- err = sr_get_data(jctx->sr_query_ses, "/*", 0, 0, 0, &sr_data);
+ err = sr_get_data(jctx->sr_query_ses, "/*", 0, SYSREPO_TIMEOUT, 0, &sr_data);
if (err != SR_ERR_OK) {
ERROR("Error, getting operational data: %s", sr_strerror(err));
sr_release_context(con);
diff --git a/src/test-mode/src/test-mode.c b/src/test-mode/src/test-mode.c
index ac197d88..1db989b2 100644
--- a/src/test-mode/src/test-mode.c
+++ b/src/test-mode/src/test-mode.c
@@ -14,7 +14,6 @@
#include
#include
-#define SYSREPO_TIMEOUT 60000 /* 60s, this is the timeout we use in our frontends. */
sr_subscription_ctx_t *sub = NULL;
sr_conn_ctx_t *conn;