diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 0de1b557568f15f9fb3462b0b995c6646c069c30..ff2512deb055fe8fe3ff354c8301057b3d3e546e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -33,6 +33,9 @@ if(SCORD_BUILD_TESTS)
   set(TEST_ENV)
   list(APPEND TEST_ENV SCORD_LOG_OUTPUT=${TEST_DIRECTORY}/scord_daemon.log)
 
+  set(SCORD_ADDRESS_STRING
+    ${SCORD_TRANSPORT_PROTOCOL}://${SCORD_BIND_ADDRESS}:${SCORD_BIND_PORT})
+
   add_test(start_scord_daemon
     ${CMAKE_SOURCE_DIR}/scripts/runner.sh start scord.pid
            ${CMAKE_BINARY_DIR}/src/scord/scord -f
@@ -48,6 +51,27 @@ if(SCORD_BUILD_TESTS)
 
   set_tests_properties(stop_scord_daemon PROPERTIES FIXTURES_CLEANUP
     scord_daemon)
+
+  set(SCORD_CTL_TRANSPORT_PROTOCOL ${SCORD_TRANSPORT_PROTOCOL})
+  set(SCORD_CTL_BIND_ADDRESS ${SCORD_BIND_ADDRESS})
+  math(EXPR SCORD_CTL_BIND_PORT "${SCORD_BIND_PORT} + 1")
+  set(SCORD_CTL_ADDRESS_STRING
+    ${SCORD_CTL_TRANSPORT_PROTOCOL}://${SCORD_CTL_BIND_ADDRESS}:${SCORD_CTL_BIND_PORT})
+
+  add_test(start_scord_ctl
+    ${CMAKE_SOURCE_DIR}/scripts/runner.sh start scord-ctl.pid
+    ${CMAKE_BINARY_DIR}/src/scord-ctl/scord-ctl -l ${SCORD_CTL_ADDRESS_STRING} -o ${TEST_DIRECTORY}/scord_ctl.log
+    )
+
+  set_tests_properties(start_scord_ctl
+    PROPERTIES FIXTURES_SETUP scord_ctl)
+
+  add_test(stop_scord_ctl
+    ${CMAKE_SOURCE_DIR}/scripts/runner.sh stop TERM scord-ctl.pid
+    )
+
+  set_tests_properties(stop_scord_ctl PROPERTIES FIXTURES_CLEANUP scord_ctl)
+
 endif()
 
 add_subdirectory(c)
diff --git a/examples/c/ADM_cancel_transfer.c b/examples/c/ADM_cancel_transfer.c
index 8a3bc88d5de2249e4aa6cf568001296795806e5d..77bb2a0b2ecf5c768390990dce1225f6b274653f 100644
--- a/examples/c/ADM_cancel_transfer.c
+++ b/examples/c/ADM_cancel_transfer.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_cancel_transfer <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_connect_data_operation.c b/examples/c/ADM_connect_data_operation.c
index 10398544879200167247455b52fa4515b7da4468..8ae8f55ea0a3ce493f1eca9288ae299f85f285f5 100644
--- a/examples/c/ADM_connect_data_operation.c
+++ b/examples/c/ADM_connect_data_operation.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no server address provided\n");
-        fprintf(stderr, "Usage: ADM_connect_data_operation <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -60,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_define_data_operation.c b/examples/c/ADM_define_data_operation.c
index 8b4fa0ca0e57248850cd392f053cf62fd28b4e7f..04a151c497c4e88b95c434b7a4b6506458e11a2e 100644
--- a/examples/c/ADM_define_data_operation.c
+++ b/examples/c/ADM_define_data_operation.c
@@ -36,15 +36,20 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_define_data_operation <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
 
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -65,7 +70,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_deploy_adhoc_storage.c b/examples/c/ADM_deploy_adhoc_storage.c
index 1dc7c0359a8b267da272b1ecb5ed427be34b673d..4d6d91dd6fe184808aeaba98ce91ea03665c76c7 100644
--- a/examples/c/ADM_deploy_adhoc_storage.c
+++ b/examples/c/ADM_deploy_adhoc_storage.c
@@ -34,9 +34,14 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_deploy_adhoc_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -76,7 +81,8 @@ main(int argc, char* argv[]) {
     }
 
     // 3. the adhoc storage execution context
-    adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                         ADM_ADHOC_MODE_SEPARATE_NEW,
                                          ADM_ADHOC_ACCESS_RDWR, 100, false);
 
     if(adhoc_ctx == NULL) {
@@ -89,7 +95,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
@@ -108,7 +114,8 @@ main(int argc, char* argv[]) {
     // system, let's prepare a new execution context for the adhoc
     // storage system
 
-    new_adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    new_adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                             ADM_ADHOC_MODE_SEPARATE_NEW,
                                              ADM_ADHOC_ACCESS_RDWR, 200, false);
 
     if(new_adhoc_ctx == NULL) {
diff --git a/examples/c/ADM_finalize_data_operation.c b/examples/c/ADM_finalize_data_operation.c
index 9851326ec8e4333f976b34b334b59401d2cee9b1..89c421ea1c78e51ab66f1141a0a4e570c283236e 100644
--- a/examples/c/ADM_finalize_data_operation.c
+++ b/examples/c/ADM_finalize_data_operation.c
@@ -36,15 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr,
-                "Usage: ADM_finalize_data_operation <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -61,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_get_pending_transfers.c b/examples/c/ADM_get_pending_transfers.c
index e2c109bf7d75360e2801e16195cf5d65674b50e1..6789136cf19e5018d395e82067fe8df395f80feb 100644
--- a/examples/c/ADM_get_pending_transfers.c
+++ b/examples/c/ADM_get_pending_transfers.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_get_pending_transfers <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_get_qos_constraints.c b/examples/c/ADM_get_qos_constraints.c
index 6644c99ba1e1213e64f61dd05637afb90b5ea8d1..be8498ee58579ce6312c60101a29f936146d1cc3 100644
--- a/examples/c/ADM_get_qos_constraints.c
+++ b/examples/c/ADM_get_qos_constraints.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_get_qos_constraints <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -60,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_get_statistics.c b/examples/c/ADM_get_statistics.c
index 9686246bb2a270854e293c3b83d5df982723edd3..4f0c7db51a95a18898b27b71aa4eea33039328f5 100644
--- a/examples/c/ADM_get_statistics.c
+++ b/examples/c/ADM_get_statistics.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_get_statistics <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -60,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_get_transfer_priority.c b/examples/c/ADM_get_transfer_priority.c
index 2be2ac0f277a9bb9512fc83048d41ac7b62cbf11..5a5957bdeb46646c39045ec455a06d4b3ecd4008 100644
--- a/examples/c/ADM_get_transfer_priority.c
+++ b/examples/c/ADM_get_transfer_priority.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_get_transfer_priority <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_link_transfer_to_data_operation.c b/examples/c/ADM_link_transfer_to_data_operation.c
index 66bd656b3854a7bbe5df86f6cee9a0c3c3aac7ee..bfab8b6b1098e511859f9b2567d430fbe01ecb4e 100644
--- a/examples/c/ADM_link_transfer_to_data_operation.c
+++ b/examples/c/ADM_link_transfer_to_data_operation.c
@@ -36,15 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr,
-                "Usage: ADM_link_transfer_to_data_operation <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -61,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_ping.c b/examples/c/ADM_ping.c
index a197642f7dcf986d8844f701c1df73609ffee507..7ec297200bc0726a8640088e6fbbbe8cd896f127 100644
--- a/examples/c/ADM_ping.c
+++ b/examples/c/ADM_ping.c
@@ -26,17 +26,23 @@
 #include <stdio.h>
 #include <scord/scord.h>
 #include <assert.h>
+#include "common.h"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_ping <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_return_t ret = ADM_ping(server);
 
diff --git a/examples/c/ADM_register_adhoc_storage.c b/examples/c/ADM_register_adhoc_storage.c
index 5f2a86dc3a6caeb6dfb012d02aa6685bebef0b79..0d2ff17e3ac23b331683feec6be8c819dd9b9fec 100644
--- a/examples/c/ADM_register_adhoc_storage.c
+++ b/examples/c/ADM_register_adhoc_storage.c
@@ -34,9 +34,14 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_register_adhoc_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -73,7 +78,8 @@ main(int argc, char* argv[]) {
     }
 
     // 3. define the adhoc execution context
-    adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                         ADM_ADHOC_MODE_SEPARATE_NEW,
                                          ADM_ADHOC_ACCESS_RDWR, 100, false);
 
     if(adhoc_ctx == NULL) {
@@ -85,7 +91,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
diff --git a/examples/c/ADM_register_job.c b/examples/c/ADM_register_job.c
index ebab9efc0ecd7e57728015a7c461d85f16210051..e4be41538f28117bcaec7e0ad95911a76039e14d 100644
--- a/examples/c/ADM_register_job.c
+++ b/examples/c/ADM_register_job.c
@@ -35,9 +35,14 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_register_job <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -83,7 +88,8 @@ main(int argc, char* argv[]) {
     }
 
     // 3. the adhoc storage execution context
-    adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                         ADM_ADHOC_MODE_SEPARATE_NEW,
                                          ADM_ADHOC_ACCESS_RDWR, 100, false);
 
     if(adhoc_ctx == NULL) {
@@ -96,7 +102,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
diff --git a/examples/c/ADM_register_pfs_storage.c b/examples/c/ADM_register_pfs_storage.c
index 8510bfc373e721776595a58525a68cee7e9d6962..5c7ecc2fdfcb657591cd7f7fa385755e253a2357 100644
--- a/examples/c/ADM_register_pfs_storage.c
+++ b/examples/c/ADM_register_pfs_storage.c
@@ -25,13 +25,19 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <scord/scord.h>
+#include "common.h"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no server address provided\n");
-        fprintf(stderr, "Usage: ADM_register_pfs_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -62,7 +68,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
diff --git a/examples/c/ADM_remove_adhoc_storage.c b/examples/c/ADM_remove_adhoc_storage.c
index 23e404bb596759944af91e9c8467375bd77c4294..17d8cf78292b611b48f94fbe0a2ed2566e740c1b 100644
--- a/examples/c/ADM_remove_adhoc_storage.c
+++ b/examples/c/ADM_remove_adhoc_storage.c
@@ -34,9 +34,14 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_remove_adhoc_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -75,7 +80,8 @@ main(int argc, char* argv[]) {
     }
 
     // 3. the adhoc storage execution context
-    adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                         ADM_ADHOC_MODE_SEPARATE_NEW,
                                          ADM_ADHOC_ACCESS_RDWR, 100, false);
 
     if(adhoc_ctx == NULL) {
@@ -88,7 +94,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
diff --git a/examples/c/ADM_remove_job.c b/examples/c/ADM_remove_job.c
index c4d0a7c887d949f1a6c7114599dd7489bc9f5330..ddf7a2d6f2064df0241c056c767b485e16ba55dc 100644
--- a/examples/c/ADM_remove_job.c
+++ b/examples/c/ADM_remove_job.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_remove_job <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_remove_pfs_storage.c b/examples/c/ADM_remove_pfs_storage.c
index d2bc0017e46b406062a4167a0c50c838c6df329b..34423b8e645b944722872674d01cee1369b5d1f3 100644
--- a/examples/c/ADM_remove_pfs_storage.c
+++ b/examples/c/ADM_remove_pfs_storage.c
@@ -18,7 +18,9 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with scord.  If not, see <https://www.gnu.org/licenses/>.
- *
+         if(test_info.requires_server) {
+        }
+*
  * SPDX-License-Identifier: GPL-3.0-or-later
  *****************************************************************************/
 
@@ -26,13 +28,19 @@
 #include <stdio.h>
 #include <scord/scord.h>
 #include <assert.h>
+#include "common.h"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no server address provided\n");
-        fprintf(stderr, "Usage: ADM_remove_pfs_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -63,7 +71,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
diff --git a/examples/c/ADM_set_dataset_information.c b/examples/c/ADM_set_dataset_information.c
index ddeefe0cf235080cb87a1f24d901b68e1aceac6e..548befd80a4773c717dcff6645cf35455784dee3 100644
--- a/examples/c/ADM_set_dataset_information.c
+++ b/examples/c/ADM_set_dataset_information.c
@@ -36,15 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr,
-                "Usage: ADM_set_dataset_information <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -61,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_set_io_resources.c b/examples/c/ADM_set_io_resources.c
index 37989fa5b6056b76118a525b0046345e650f5a18..cdf07a3bbefc20c5f840cb60522bf980ad37ee77 100644
--- a/examples/c/ADM_set_io_resources.c
+++ b/examples/c/ADM_set_io_resources.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_set_io_resources <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -60,7 +65,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_set_qos_constraints.c b/examples/c/ADM_set_qos_constraints.c
index dec01f69424b0795655198b2e0f9c3678c8879a7..a7f07bc11165cafae34ad9b1fff07328e0019b15 100644
--- a/examples/c/ADM_set_qos_constraints.c
+++ b/examples/c/ADM_set_qos_constraints.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_set_qos_constraints <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_set_transfer_priority.c b/examples/c/ADM_set_transfer_priority.c
index 60e33761c5f888bb5a6d181a51fb04b5ce7903fc..8ba26be5909a38059d6f4e5377b8016b8b645af8 100644
--- a/examples/c/ADM_set_transfer_priority.c
+++ b/examples/c/ADM_set_transfer_priority.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_set_transfer_priority <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_tear_down_adhoc_storage.c b/examples/c/ADM_tear_down_adhoc_storage.c
index 10e6546c46c279b03fc29f7fdd4e7493066a3d99..996d03e13246eb38ed70ec0cb8314c0a7d594e4c 100644
--- a/examples/c/ADM_tear_down_adhoc_storage.c
+++ b/examples/c/ADM_tear_down_adhoc_storage.c
@@ -34,9 +34,14 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_deploy_adhoc_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -76,7 +81,8 @@ main(int argc, char* argv[]) {
     }
 
     // 3. the adhoc storage execution context
-    adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                         ADM_ADHOC_MODE_SEPARATE_NEW,
                                          ADM_ADHOC_ACCESS_RDWR, 100, false);
 
     if(adhoc_ctx == NULL) {
@@ -89,7 +95,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
@@ -108,7 +114,8 @@ main(int argc, char* argv[]) {
     // system, let's prepare a new execution context for the adhoc
     // storage system
 
-    new_adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    new_adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                             ADM_ADHOC_MODE_SEPARATE_NEW,
                                              ADM_ADHOC_ACCESS_RDWR, 200, false);
 
     if(new_adhoc_ctx == NULL) {
diff --git a/examples/c/ADM_transfer_datasets.c b/examples/c/ADM_transfer_datasets.c
index 73af36c5e14e7c80bc9f5f3b355a0579df1869cc..343c603e223ae667938eed9b86808488ec65d82a 100644
--- a/examples/c/ADM_transfer_datasets.c
+++ b/examples/c/ADM_transfer_datasets.c
@@ -39,14 +39,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_transfer_datasets <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job = NULL;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -67,7 +72,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_update_adhoc_storage.c b/examples/c/ADM_update_adhoc_storage.c
index f8f063979cdebf9fd480752b3798fabeb0b72a0c..823f5504ef32b92dffcc1ae6f4489effbcb41e6a 100644
--- a/examples/c/ADM_update_adhoc_storage.c
+++ b/examples/c/ADM_update_adhoc_storage.c
@@ -35,9 +35,14 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_update_adhoc_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -79,7 +84,8 @@ main(int argc, char* argv[]) {
     }
 
     // 3. the adhoc storage execution context
-    adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                         ADM_ADHOC_MODE_SEPARATE_NEW,
                                          ADM_ADHOC_ACCESS_RDWR, 100, false);
 
     if(adhoc_ctx == NULL) {
@@ -92,7 +98,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
@@ -118,7 +124,8 @@ main(int argc, char* argv[]) {
         goto cleanup;
     }
 
-    new_adhoc_ctx = ADM_adhoc_context_create(ADM_ADHOC_MODE_SEPARATE_NEW,
+    new_adhoc_ctx = ADM_adhoc_context_create(cli_args.controller_address,
+                                             ADM_ADHOC_MODE_SEPARATE_NEW,
                                              ADM_ADHOC_ACCESS_RDWR, 200, false);
 
     if(new_adhoc_ctx == NULL) {
diff --git a/examples/c/ADM_update_job.c b/examples/c/ADM_update_job.c
index 2e94ddedb7fe255fe007ea11c90776b8636dfece..25ca673525c4455c0438cbfa9498bb6b9c6cf5d7 100644
--- a/examples/c/ADM_update_job.c
+++ b/examples/c/ADM_update_job.c
@@ -36,14 +36,19 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no location provided\n");
-        fprintf(stderr, "Usage: ADM_update_job <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
     int exit_status = EXIT_SUCCESS;
-    ADM_server_t server = ADM_server_create("tcp", argv[1]);
+    ADM_server_t server = ADM_server_create("tcp", cli_args.server_address);
 
     ADM_job_t job;
     ADM_node_t* job_nodes = prepare_nodes(NJOB_NODES);
@@ -64,7 +69,8 @@ main(int argc, char* argv[]) {
     assert(adhoc_resources);
 
     ADM_adhoc_context_t ctx = ADM_adhoc_context_create(
-            ADM_ADHOC_MODE_SEPARATE_NEW, ADM_ADHOC_ACCESS_RDWR, 100, false);
+            cli_args.controller_address, ADM_ADHOC_MODE_SEPARATE_NEW,
+            ADM_ADHOC_ACCESS_RDWR, 100, false);
     assert(ctx);
 
     const char* name = "adhoc_storage_42";
diff --git a/examples/c/ADM_update_pfs_storage.c b/examples/c/ADM_update_pfs_storage.c
index 95aea01cdf943cb98d4eb5e1cb51609896dba29d..dce5f27e5e67e0d38fbd31c3f3884fb75525158c 100644
--- a/examples/c/ADM_update_pfs_storage.c
+++ b/examples/c/ADM_update_pfs_storage.c
@@ -26,13 +26,19 @@
 #include <stdio.h>
 #include <scord/scord.h>
 #include <assert.h>
+#include "common.h"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fprintf(stderr, "ERROR: no server address provided\n");
-        fprintf(stderr, "Usage: ADM_update_pfs_storage <SERVER_ADDRESS>\n");
+    test_info_t test_info = {
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    cli_args_t cli_args;
+    if(process_args(argc, argv, test_info, &cli_args)) {
         exit(EXIT_FAILURE);
     }
 
@@ -65,7 +71,7 @@ main(int argc, char* argv[]) {
     // now ready. Let's actually contact the server:
 
     // 1. Find the server endpoint
-    if((server = ADM_server_create("tcp", argv[1])) == NULL) {
+    if((server = ADM_server_create("tcp", cli_args.server_address)) == NULL) {
         fprintf(stderr, "Fatal error creating server\n");
         goto cleanup;
     }
diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt
index ce29ffbdc3845c92ff2102ce4c67569cb1acac09..d2a25be81e26518709d00e67f17c7143f5f0559b 100644
--- a/examples/c/CMakeLists.txt
+++ b/examples/c/CMakeLists.txt
@@ -22,16 +22,12 @@
 # SPDX-License-Identifier: GPL-3.0-or-later                                    #
 ################################################################################
 
-list(APPEND examples_c
-  # ping
-  ADM_ping
+list(APPEND c_examples_with_controller
   # job
   ADM_register_job ADM_update_job ADM_remove_job
   # adhoc storage
   ADM_register_adhoc_storage ADM_update_adhoc_storage ADM_remove_adhoc_storage
   ADM_deploy_adhoc_storage ADM_tear_down_adhoc_storage
-  # pfs storage
-  ADM_register_pfs_storage ADM_update_pfs_storage ADM_remove_pfs_storage
   # transfers
   ADM_transfer_datasets ADM_get_transfer_priority ADM_set_transfer_priority
   ADM_cancel_transfer ADM_get_pending_transfers
@@ -45,11 +41,18 @@ list(APPEND examples_c
   ADM_get_statistics ADM_set_dataset_information ADM_set_io_resources
   )
 
+list(APPEND c_examples_without_controller
+  # ping
+  ADM_ping
+  # pfs storage
+  ADM_register_pfs_storage ADM_update_pfs_storage ADM_remove_pfs_storage
+  )
+
 add_library(c_examples_common STATIC)
 target_sources(c_examples_common PUBLIC common.h PRIVATE common.c)
 target_link_libraries(c_examples_common libscord_c_types)
 
-foreach(example IN LISTS examples_c)
+foreach(example IN LISTS c_examples_with_controller c_examples_without_controller)
   add_executable(${example}_c)
   target_sources(${example}_c PRIVATE ${example}.c)
   target_link_libraries(${example}_c PUBLIC libscord c_examples_common)
@@ -57,7 +60,7 @@ foreach(example IN LISTS examples_c)
 endforeach()
 
 if(SCORD_BUILD_TESTS)
-  foreach(example IN LISTS examples_c)
+  foreach(example IN LISTS c_examples_with_controller)
 
     # prepare environment for the RPC test itself and its validation test
     set(TEST_NAME "${example}_c_test")
@@ -69,7 +72,35 @@ if(SCORD_BUILD_TESTS)
     list(APPEND TEST_ENV LIBSCORD_LOG_OUTPUT=${TEST_DIRECTORY}/libscord.log)
 
     add_test(run_${TEST_NAME} ${example}
-      ${SCORD_TRANSPORT_PROTOCOL}://${SCORD_BIND_ADDRESS}:${SCORD_BIND_PORT})
+      ${SCORD_ADDRESS_STRING}
+      ${SCORD_CTL_ADDRESS_STRING})
+    set_tests_properties(run_${TEST_NAME}
+      PROPERTIES FIXTURES_REQUIRED "scord_daemon;scord_ctl"
+      ENVIRONMENT "${TEST_ENV}")
+
+    add_test(validate_${TEST_NAME}
+      ${CMAKE_SOURCE_DIR}/ci/check_rpcs.py
+      ${TEST_DIRECTORY}/libscord.log
+      ${SCORD_TESTS_DIRECTORY}/scord_daemon/scord_daemon.log
+      ${example}
+      )
+    set_tests_properties(validate_${TEST_NAME}
+      PROPERTIES DEPENDS stop_scord_daemon
+      )
+  endforeach()
+
+  foreach(example IN LISTS c_examples_without_controller)
+
+    # prepare environment for the RPC test itself and its validation test
+    set(TEST_NAME "${example}_c_test")
+    set(TEST_DIRECTORY "${SCORD_TESTS_DIRECTORY}/${TEST_NAME}")
+    file(MAKE_DIRECTORY ${TEST_DIRECTORY})
+
+    set(TEST_ENV)
+    list(APPEND TEST_ENV LIBSCORD_LOG=1)
+    list(APPEND TEST_ENV LIBSCORD_LOG_OUTPUT=${TEST_DIRECTORY}/libscord.log)
+
+    add_test(run_${TEST_NAME} ${example} ${SCORD_ADDRESS_STRING})
     set_tests_properties(run_${TEST_NAME}
       PROPERTIES FIXTURES_REQUIRED scord_daemon
       ENVIRONMENT "${TEST_ENV}")
diff --git a/examples/c/common.c b/examples/c/common.c
index c5b97ae499228e8edf5b5aacd02f2a852b349389..b19935986c31a73fde8798e65a0b0d2e660fa8a0 100644
--- a/examples/c/common.c
+++ b/examples/c/common.c
@@ -2,8 +2,37 @@
 #define SCORD_COMMON_H
 
 #include <stdio.h>
+#include <string.h>
 #include "common.h"
 
+int
+process_args(int argc, char* argv[], test_info_t test_info, cli_args_t* args) {
+
+    int required_args = 1;
+
+    if(test_info.requires_server) {
+        ++required_args;
+    }
+
+    if(test_info.requires_controller) {
+        ++required_args;
+    }
+
+    if(argc != required_args) {
+        fprintf(stderr, "ERROR: missing arguments\n");
+        fprintf(stderr, "Usage: %s%s%s\n", test_info.name,
+                test_info.requires_server ? " <SERVER_ADDRESS>" : "",
+                test_info.requires_controller ? " <CONTROLLER_ADDRESS>" : "");
+        return -1;
+    }
+
+    args->server_address = test_info.requires_server ? argv[1] : NULL;
+    args->controller_address = test_info.requires_controller ? argv[2] : NULL;
+
+    return 0;
+}
+
+
 ADM_node_t*
 prepare_nodes(size_t n) {
 
diff --git a/examples/c/common.h b/examples/c/common.h
index 0aeab9c92e388c8e0c8fb52b176e413053a7b193..53d472113ab6c0e1d831319f0979adb99fd4001a 100644
--- a/examples/c/common.h
+++ b/examples/c/common.h
@@ -3,6 +3,24 @@
 
 #include <scord/types.h>
 
+#define TESTNAME                                                               \
+    (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1   \
+                                      : __FILE__)
+
+typedef struct {
+    const char* name;
+    bool requires_server;
+    bool requires_controller;
+} test_info_t;
+
+typedef struct {
+    const char* server_address;
+    const char* controller_address;
+} cli_args_t;
+
+int
+process_args(int argc, char* argv[], test_info_t test_info, cli_args_t* args);
+
 ADM_node_t*
 prepare_nodes(size_t n);
 
diff --git a/examples/cxx/ADM_cancel_transfer.cpp b/examples/cxx/ADM_cancel_transfer.cpp
index 1abff9ca24d7511e339a18f477dac2b87b5cbe6e..d2791147037f6c826c404c9f8e470d93b8a81a8e 100644
--- a/examples/cxx/ADM_cancel_transfer.cpp
+++ b/examples/cxx/ADM_cancel_transfer.cpp
@@ -24,18 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_cancel_transfer <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_transfer_t tx{};
diff --git a/examples/cxx/ADM_connect_data_operation.cpp b/examples/cxx/ADM_connect_data_operation.cpp
index 5ff68267834cfa56691f29726ea60650ed96882b..d3bc9ac1d63c15841e4cea875d81ff3b7a5d1176 100644
--- a/examples/cxx/ADM_connect_data_operation.cpp
+++ b/examples/cxx/ADM_connect_data_operation.cpp
@@ -24,18 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_connect_data_operation <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_dataset_t input{};
diff --git a/examples/cxx/ADM_define_data_operation.cpp b/examples/cxx/ADM_define_data_operation.cpp
index bf087508b244b9daa5de31b5512c954e6b8e8524..e34a8d05568000ab5243c7f1e2c980b756825aef 100644
--- a/examples/cxx/ADM_define_data_operation.cpp
+++ b/examples/cxx/ADM_define_data_operation.cpp
@@ -24,19 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_define_data_operation <SERVER_ADDRESS> \n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     const char* path = "/tmpxxxxx";
diff --git a/examples/cxx/ADM_deploy_adhoc_storage.cpp b/examples/cxx/ADM_deploy_adhoc_storage.cpp
index 38077ccaad2cab36c369f2f90278abf901860059..ac37c49fac9f3088d15d30c2f5265d659cc388b3 100644
--- a/examples/cxx/ADM_deploy_adhoc_storage.cpp
+++ b/examples/cxx/ADM_deploy_adhoc_storage.cpp
@@ -34,14 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_deploy_adhoc_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
     const auto inputs = prepare_datasets("input-dataset-{}", NINPUTS);
@@ -49,6 +50,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_finalize_data_operation.cpp b/examples/cxx/ADM_finalize_data_operation.cpp
index 147ae68664c1ed78b57dc2b50dde2258181b125b..a9148a199b83d1d589412b3a1f612b0bac99a588 100644
--- a/examples/cxx/ADM_finalize_data_operation.cpp
+++ b/examples/cxx/ADM_finalize_data_operation.cpp
@@ -24,18 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_finalize_data_operation <SERVER_ADDRESS> \n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_data_operation_t op{};
diff --git a/examples/cxx/ADM_get_pending_transfers.cpp b/examples/cxx/ADM_get_pending_transfers.cpp
index f60be4b2ead9e643624e7732564464c557417fce..60f7d9eb7976f33614b0d2ad16798050016204ed 100644
--- a/examples/cxx/ADM_get_pending_transfers.cpp
+++ b/examples/cxx/ADM_get_pending_transfers.cpp
@@ -24,19 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_get_pending_transfers <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_transfer_t** tx = nullptr;
diff --git a/examples/cxx/ADM_get_qos_constraints.cpp b/examples/cxx/ADM_get_qos_constraints.cpp
index ba34766c8e117e37cf806888f8ebb16e25087f62..389ac2b199d14312b396c109f156eb1e8bf2ad26 100644
--- a/examples/cxx/ADM_get_qos_constraints.cpp
+++ b/examples/cxx/ADM_get_qos_constraints.cpp
@@ -24,18 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_get_qos_constraints <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_qos_entity_t entity{};
diff --git a/examples/cxx/ADM_get_statistics.cpp b/examples/cxx/ADM_get_statistics.cpp
index cb8c0cdc720e1779afa9ad275f445cf00ea3d047..a19e506b061a2dd7b21ec8ea9c3f909ad9e06294 100644
--- a/examples/cxx/ADM_get_statistics.cpp
+++ b/examples/cxx/ADM_get_statistics.cpp
@@ -24,17 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_get_statistics <SERVER_ADDRESS> \n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_job_stats_t* stats = nullptr;
diff --git a/examples/cxx/ADM_get_transfer_priority.cpp b/examples/cxx/ADM_get_transfer_priority.cpp
index 7dbd585b91eb93b5f2dcbbed2e721b422debf71d..983c39c18bed25e01368f5744a79f8f91b78c45c 100644
--- a/examples/cxx/ADM_get_transfer_priority.cpp
+++ b/examples/cxx/ADM_get_transfer_priority.cpp
@@ -24,19 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_get_transfer_priority <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_transfer_t tx{};
diff --git a/examples/cxx/ADM_in_situ_ops.cpp b/examples/cxx/ADM_in_situ_ops.cpp
index 517b0fd6ae59813ecc1293d0551fadcceea0ad9e..563cf0bc3e1687404580756ba128cb1c50b8f7cf 100644
--- a/examples/cxx/ADM_in_situ_ops.cpp
+++ b/examples/cxx/ADM_in_situ_ops.cpp
@@ -24,26 +24,28 @@
 
 #include <fmt/format.h>
 #include <engine.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_in_situ_ops <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
     scord::network::rpc_client rpc_client{"tcp"};
     rpc_client.register_rpcs();
 
-    auto endp = rpc_client.lookup(argv[1]);
+    auto endp = rpc_client.lookup(cli_args.server_address);
 
     fmt::print(
             stdout,
             "Calling ADM_in_situ_ops remote procedure on {} -> access method: {} ...\n",
-            argv[1], argv[2]);
+            cli_args.controller_address, argv[2]);
     ADM_in_situ_ops_in_t in;
     in.in_situ = argv[2];
     ADM_in_situ_ops_out_t out;
diff --git a/examples/cxx/ADM_in_transit_ops.cpp b/examples/cxx/ADM_in_transit_ops.cpp
index bf3ce2b054d8abefc31f898ef91542828fccbb19..8e743799f8061f616750707834b5d511ae702de4 100644
--- a/examples/cxx/ADM_in_transit_ops.cpp
+++ b/examples/cxx/ADM_in_transit_ops.cpp
@@ -24,26 +24,28 @@
 
 #include <fmt/format.h>
 #include <engine.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_in_transit_ops <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
     scord::network::rpc_client rpc_client{"tcp"};
     rpc_client.register_rpcs();
 
-    auto endp = rpc_client.lookup(argv[1]);
+    auto endp = rpc_client.lookup(cli_args.server_address);
 
     fmt::print(
             stdout,
             "Calling ADM_in_transit_ops remote procedure on {} -> access method: {} ...\n",
-            argv[1], argv[2]);
+            cli_args.controller_address, argv[2]);
     ADM_in_transit_ops_in_t in;
     in.in_transit = argv[2];
     ADM_in_transit_ops_out_t out;
diff --git a/examples/cxx/ADM_link_transfer_to_data_operation.cpp b/examples/cxx/ADM_link_transfer_to_data_operation.cpp
index 9c993c08af15db4d4a986cac1df2001af5905381..2227afd1e9ef3095e524c8d65252c0cbd9c863dd 100644
--- a/examples/cxx/ADM_link_transfer_to_data_operation.cpp
+++ b/examples/cxx/ADM_link_transfer_to_data_operation.cpp
@@ -24,19 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(
-                stderr,
-                "Usage: ADM_link_transfer_to_data_operation <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_data_operation_t op{};
diff --git a/examples/cxx/ADM_ping.cpp b/examples/cxx/ADM_ping.cpp
index 8570c17baadd6e4607098227f8d83841c0270e93..ef796074bc7d6f9ba4aad2dbf59d2394157421de 100644
--- a/examples/cxx/ADM_ping.cpp
+++ b/examples/cxx/ADM_ping.cpp
@@ -24,17 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_ping <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     try {
         scord::ping(server);
diff --git a/examples/cxx/ADM_register_adhoc_storage.cpp b/examples/cxx/ADM_register_adhoc_storage.cpp
index 98b00c56d4808ced12d4894cbd2ec909a7e86227..0dfd0f9ba7ccfea5e2de05ec45105d0c67ebbb5b 100644
--- a/examples/cxx/ADM_register_adhoc_storage.cpp
+++ b/examples/cxx/ADM_register_adhoc_storage.cpp
@@ -34,14 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_register_adhoc_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
     const auto inputs = prepare_datasets("input-dataset-{}", NINPUTS);
@@ -49,6 +50,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_register_job.cpp b/examples/cxx/ADM_register_job.cpp
index 267f56ad418b7ec97cfb0e919704fa489a2ec65f..55ac84d6570dc2739a12d9ce07846cdbfcc78e26 100644
--- a/examples/cxx/ADM_register_job.cpp
+++ b/examples/cxx/ADM_register_job.cpp
@@ -34,13 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_register_job <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto job_nodes = prepare_nodes(NJOB_NODES);
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
@@ -49,6 +51,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_register_pfs_storage.cpp b/examples/cxx/ADM_register_pfs_storage.cpp
index 712409dc1d287ccbd438180da46365f27b0aaa39..1078262c91a9233a35a53175064961d7fe7f28dd 100644
--- a/examples/cxx/ADM_register_pfs_storage.cpp
+++ b/examples/cxx/ADM_register_pfs_storage.cpp
@@ -24,19 +24,21 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_register_pfs_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     std::string pfs_name = "gpfs_scratch";
     std::string pfs_mount = "/gpfs/scratch";
diff --git a/examples/cxx/ADM_remove_adhoc_storage.cpp b/examples/cxx/ADM_remove_adhoc_storage.cpp
index 9459663b809ac91d783ed5d51d569710e1e8e27b..aa496ee6fd002006a39fa7ce4626b5a698b85980 100644
--- a/examples/cxx/ADM_remove_adhoc_storage.cpp
+++ b/examples/cxx/ADM_remove_adhoc_storage.cpp
@@ -34,14 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_remove_adhoc_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
     const auto inputs = prepare_datasets("input-dataset-{}", NINPUTS);
@@ -49,6 +50,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_remove_job.cpp b/examples/cxx/ADM_remove_job.cpp
index 37498e12334b4de078305a6a25e2b53f1c4d2628..4a3ae71cdf7b09ad908520267e879a989ae1a6b6 100644
--- a/examples/cxx/ADM_remove_job.cpp
+++ b/examples/cxx/ADM_remove_job.cpp
@@ -34,13 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_remove_job <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto job_nodes = prepare_nodes(NJOB_NODES);
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
@@ -49,6 +51,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
 
diff --git a/examples/cxx/ADM_remove_pfs_storage.cpp b/examples/cxx/ADM_remove_pfs_storage.cpp
index c2cdaeec2247e8f13e387e05c18f3c6d7d5e73dc..8e5bd527ea2a0de334092552c4b4b3616a11f082 100644
--- a/examples/cxx/ADM_remove_pfs_storage.cpp
+++ b/examples/cxx/ADM_remove_pfs_storage.cpp
@@ -24,18 +24,21 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_remove_pfs_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     std::string pfs_name = "gpfs_scratch";
     std::string pfs_mount = "/gpfs/scratch";
diff --git a/examples/cxx/ADM_set_dataset_information.cpp b/examples/cxx/ADM_set_dataset_information.cpp
index 9f7b7d33f519a248ccffc4b0f2cfd415c18f62a1..863e677424d113350f2eaeb9f5b4c7a95ab5a1e6 100644
--- a/examples/cxx/ADM_set_dataset_information.cpp
+++ b/examples/cxx/ADM_set_dataset_information.cpp
@@ -24,19 +24,21 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_set_dataset_information <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_dataset_t target{};
diff --git a/examples/cxx/ADM_set_io_resources.cpp b/examples/cxx/ADM_set_io_resources.cpp
index c36f2c65c9739eb34fd05101170a6368a75a8ad1..425b4642379614b73bd0115e6669b2c64fdec12e 100644
--- a/examples/cxx/ADM_set_io_resources.cpp
+++ b/examples/cxx/ADM_set_io_resources.cpp
@@ -24,18 +24,21 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_set_io_resources <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_adhoc_storage_t tier{};
diff --git a/examples/cxx/ADM_set_qos_constraints.cpp b/examples/cxx/ADM_set_qos_constraints.cpp
index 65b798f12fcb07996a744e8ad232d3762af8b8c5..d487b57603e81a9adc07f0bd3a248aea628c9c2f 100644
--- a/examples/cxx/ADM_set_qos_constraints.cpp
+++ b/examples/cxx/ADM_set_qos_constraints.cpp
@@ -24,18 +24,20 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
-
+#include "common.hpp"
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr, "Usage: ADM_set_qos_constraints <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_qos_entity_t entity{};
diff --git a/examples/cxx/ADM_set_transfer_priority.cpp b/examples/cxx/ADM_set_transfer_priority.cpp
index 040cde0e5dc70d80290aaa604e3150406ef97705..39819b478966842685c233044fc90d3c84d48b09 100644
--- a/examples/cxx/ADM_set_transfer_priority.cpp
+++ b/examples/cxx/ADM_set_transfer_priority.cpp
@@ -24,19 +24,21 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_set_transfer_priority <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     ADM_job_t job{};
     ADM_transfer_t tx{};
diff --git a/examples/cxx/ADM_tear_down_adhoc_storage.cpp b/examples/cxx/ADM_tear_down_adhoc_storage.cpp
index 229242744f1c6e001891e10401d09d3a01eef293..561676edb1d388cbdf319e7895f3d851e3384936 100644
--- a/examples/cxx/ADM_tear_down_adhoc_storage.cpp
+++ b/examples/cxx/ADM_tear_down_adhoc_storage.cpp
@@ -34,14 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_tear_down_adhoc_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
     const auto inputs = prepare_datasets("input-dataset-{}", NINPUTS);
@@ -49,6 +50,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_transfer_datasets.cpp b/examples/cxx/ADM_transfer_datasets.cpp
index d13cc8ad7a1681b93c3d9719fac6d72bee02c2a1..2b4e6f02c6373f7f404c305a449b97bdb328ba57 100644
--- a/examples/cxx/ADM_transfer_datasets.cpp
+++ b/examples/cxx/ADM_transfer_datasets.cpp
@@ -37,13 +37,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_transfer_datasets <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto job_nodes = prepare_nodes(NJOB_NODES);
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
@@ -57,6 +59,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_update_adhoc_storage.cpp b/examples/cxx/ADM_update_adhoc_storage.cpp
index 36cdaf555feccddc51829a5c334781d57b163471..dbb2ac93c1c3fa9be59fed26ae1c89e38db0c3ce 100644
--- a/examples/cxx/ADM_update_adhoc_storage.cpp
+++ b/examples/cxx/ADM_update_adhoc_storage.cpp
@@ -33,14 +33,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no location provided\n");
-        fmt::print(stderr,
-                   "Usage: ADM_update_adhoc_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto adhoc_nodes = prepare_nodes(NADHOC_NODES);
     const auto new_adhoc_nodes = prepare_nodes(NADHOC_NODES * 2);
@@ -49,6 +50,7 @@ main(int argc, char* argv[]) {
 
     std::string name = "adhoc_storage_42";
     const auto adhoc_storage_ctx = scord::adhoc_storage::ctx{
+            cli_args.controller_address,
             scord::adhoc_storage::execution_mode::separate_new,
             scord::adhoc_storage::access_type::read_write, 100, false};
     const auto adhoc_resources = scord::adhoc_storage::resources{adhoc_nodes};
diff --git a/examples/cxx/ADM_update_job.cpp b/examples/cxx/ADM_update_job.cpp
index 72c82031c62875295d49912bb72b4628921ea560..1ee22d589cfbcf9573c5d315aa44344478f00e8e 100644
--- a/examples/cxx/ADM_update_job.cpp
+++ b/examples/cxx/ADM_update_job.cpp
@@ -34,13 +34,15 @@
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_update_job <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = true,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     const auto job_nodes = prepare_nodes(NJOB_NODES);
     const auto new_job_nodes = prepare_nodes(NJOB_NODES * 2);
@@ -51,6 +53,7 @@ main(int argc, char* argv[]) {
     const auto gkfs_storage = scord::register_adhoc_storage(
             server, "foobar", scord::adhoc_storage::type::gekkofs,
             scord::adhoc_storage::ctx{
+                    cli_args.controller_address,
                     scord::adhoc_storage::execution_mode::separate_new,
                     scord::adhoc_storage::access_type::read_write, 100, false},
             scord::adhoc_storage::resources{adhoc_nodes});
diff --git a/examples/cxx/ADM_update_pfs_storage.cpp b/examples/cxx/ADM_update_pfs_storage.cpp
index 74fea88b54c0df55286dd25c28d7e0aaa3e1e5ff..019fe1104fa0ffb466822cacb1db9dcca6a8168b 100644
--- a/examples/cxx/ADM_update_pfs_storage.cpp
+++ b/examples/cxx/ADM_update_pfs_storage.cpp
@@ -24,18 +24,21 @@
 
 #include <fmt/format.h>
 #include <scord/scord.hpp>
+#include "common.hpp"
 
 
 int
 main(int argc, char* argv[]) {
 
-    if(argc != 2) {
-        fmt::print(stderr, "ERROR: no server address provided\n");
-        fmt::print(stderr, "Usage: ADM_update_pfs_storage <SERVER_ADDRESS>\n");
-        exit(EXIT_FAILURE);
-    }
+    test_info test_info{
+            .name = TESTNAME,
+            .requires_server = true,
+            .requires_controller = false,
+    };
+
+    const auto cli_args = process_args(argc, argv, test_info);
 
-    scord::server server{"tcp", argv[1]};
+    scord::server server{"tcp", cli_args.server_address};
 
     std::string pfs_name = "gpfs_scratch";
     std::string pfs_mount = "/gpfs/scratch";
diff --git a/examples/cxx/CMakeLists.txt b/examples/cxx/CMakeLists.txt
index 0b4ca7469cc0a3a3a06ac4c5d61be2320d7707fe..efb0258e9cfa18423dd2e3cc5ed6096955ac3023 100644
--- a/examples/cxx/CMakeLists.txt
+++ b/examples/cxx/CMakeLists.txt
@@ -22,16 +22,12 @@
 # SPDX-License-Identifier: GPL-3.0-or-later                                    #
 ################################################################################
 
-list(APPEND examples_cxx
-  # ping
-  ADM_ping
+list(APPEND cxx_examples_with_controller
   # job
   ADM_register_job ADM_update_job ADM_remove_job
   # adhoc storage
   ADM_register_adhoc_storage ADM_update_adhoc_storage ADM_remove_adhoc_storage
   ADM_deploy_adhoc_storage ADM_tear_down_adhoc_storage
-  # pfs storage
-  ADM_register_pfs_storage ADM_update_pfs_storage ADM_remove_pfs_storage
   # transfers
   ADM_transfer_datasets ADM_get_transfer_priority ADM_set_transfer_priority
   ADM_cancel_transfer ADM_get_pending_transfers
@@ -45,11 +41,18 @@ list(APPEND examples_cxx
   ADM_get_statistics ADM_set_dataset_information ADM_set_io_resources
   )
 
+list(APPEND cxx_examples_without_controller
+  # ping
+  ADM_ping
+  # pfs storage
+  ADM_register_pfs_storage ADM_update_pfs_storage ADM_remove_pfs_storage
+  )
+
 add_library(cxx_examples_common STATIC)
 target_sources(cxx_examples_common PUBLIC common.hpp PRIVATE common.cpp)
 target_link_libraries(cxx_examples_common libscord_cxx_types)
 
-foreach(example IN LISTS examples_cxx)
+foreach(example IN LISTS cxx_examples_with_controller cxx_examples_without_controller)
   add_executable(${example}_cxx)
   target_sources(${example}_cxx PRIVATE ${example}.cpp)
   target_link_libraries(${example}_cxx
@@ -57,10 +60,8 @@ foreach(example IN LISTS examples_cxx)
   set_target_properties(${example}_cxx PROPERTIES OUTPUT_NAME ${example})
 endforeach()
 
-set(CXX_TEST_ID 0)
-
 if(SCORD_BUILD_TESTS)
-  foreach(example IN LISTS examples_cxx)
+  foreach(example IN LISTS cxx_examples_with_controller)
 
     # prepare environment for the RPC test itself and its validation test
     set(TEST_NAME "${example}_cxx_test")
@@ -72,11 +73,38 @@ if(SCORD_BUILD_TESTS)
     list(APPEND TEST_ENV LIBSCORD_LOG_OUTPUT=${TEST_DIRECTORY}/libscord.log)
 
     add_test(run_${TEST_NAME} ${example}
-      ${SCORD_TRANSPORT_PROTOCOL}://${SCORD_BIND_ADDRESS}:${SCORD_BIND_PORT})
+      ${SCORD_ADDRESS_STRING}
+      ${SCORD_CTL_ADDRESS_STRING})
     set_tests_properties(run_${TEST_NAME}
-      PROPERTIES FIXTURES_REQUIRED scord_daemon
-      ENVIRONMENT "${TEST_ENV}"
+      PROPERTIES FIXTURES_REQUIRED "scord_daemon;scord_ctl"
+      ENVIRONMENT "${TEST_ENV}")
+
+    add_test(validate_${TEST_NAME}
+      ${CMAKE_SOURCE_DIR}/ci/check_rpcs.py
+      ${TEST_DIRECTORY}/libscord.log
+      ${SCORD_TESTS_DIRECTORY}/scord_daemon/scord_daemon.log
+      ${example}
       )
+    set_tests_properties(validate_${TEST_NAME}
+      PROPERTIES DEPENDS stop_scord_daemon
+      )
+  endforeach()
+
+  foreach(example IN LISTS cxx_examples_without_controller)
+
+    # prepare environment for the RPC test itself and its validation test
+    set(TEST_NAME "${example}_cxx_test")
+    set(TEST_DIRECTORY "${SCORD_TESTS_DIRECTORY}/${TEST_NAME}")
+    file(MAKE_DIRECTORY ${TEST_DIRECTORY})
+
+    set(TEST_ENV)
+    list(APPEND TEST_ENV LIBSCORD_LOG=1)
+    list(APPEND TEST_ENV LIBSCORD_LOG_OUTPUT=${TEST_DIRECTORY}/libscord.log)
+
+    add_test(run_${TEST_NAME} ${example} ${SCORD_ADDRESS_STRING})
+    set_tests_properties(run_${TEST_NAME}
+      PROPERTIES FIXTURES_REQUIRED scord_daemon
+      ENVIRONMENT "${TEST_ENV}")
 
     add_test(validate_${TEST_NAME}
       ${CMAKE_SOURCE_DIR}/ci/check_rpcs.py
diff --git a/examples/cxx/common.cpp b/examples/cxx/common.cpp
index d0acaafcf4ed24949996f57ecf5f51dd30c28fa4..cf83a8a90369d9b5145576297ac78f0870c8bf36 100644
--- a/examples/cxx/common.cpp
+++ b/examples/cxx/common.cpp
@@ -1,5 +1,33 @@
 #include "common.hpp"
 
+using std::string_literals::operator""s;
+
+cli_args
+process_args(int argc, char* argv[], const test_info& test_info) {
+
+    int required_args = 1;
+
+    if(test_info.requires_server) {
+        ++required_args;
+    }
+
+    if(test_info.requires_controller) {
+        ++required_args;
+    }
+
+    if(argc != required_args) {
+        fmt::print(stderr, "ERROR: missing arguments\n");
+        fmt::print(stderr, "Usage: {}{}{}\n", test_info.name,
+                   test_info.requires_server ? " <SERVER_ADDRESS>" : "",
+                   test_info.requires_controller ? " <CONTROLLER_ADDRESS>"
+                                                 : "");
+        exit(EXIT_FAILURE);
+    }
+
+    return cli_args{test_info.requires_server ? std::string{argv[1]} : ""s,
+                    test_info.requires_controller ? std::string{argv[2]} : ""s};
+}
+
 std::vector<scord::node>
 prepare_nodes(size_t n) {
     std::vector<scord::node> nodes;
diff --git a/examples/cxx/common.hpp b/examples/cxx/common.hpp
index ee76624c23f51dc0478600766950c045a31bc212..374ad2644fff45251062ba13f54644f12fff9754 100644
--- a/examples/cxx/common.hpp
+++ b/examples/cxx/common.hpp
@@ -4,6 +4,24 @@
 #include <vector>
 #include <scord/types.hpp>
 
+#define TESTNAME                                                               \
+    (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1   \
+                                      : __FILE__)
+
+struct test_info {
+    std::string name;
+    bool requires_server;
+    bool requires_controller;
+};
+
+struct cli_args {
+    std::string server_address;
+    std::string controller_address;
+};
+
+cli_args
+process_args(int argc, char* argv[], const test_info& test_info);
+
 std::vector<scord::node>
 prepare_nodes(size_t n);
 
diff --git a/src/lib/scord/types.h b/src/lib/scord/types.h
index ed5bc8e01bd8f22862f3c31fbc804db6180f4d80..badda31d26fef81ad335dd862b972bbc02b36204 100644
--- a/src/lib/scord/types.h
+++ b/src/lib/scord/types.h
@@ -485,6 +485,8 @@ ADM_adhoc_resources_destroy(ADM_adhoc_resources_t res);
  * @remark ADM_ADHOC_CONTEXTs need to be freed by calling
  * ADM_adhoc_context_destroy().
  *
+ * @param[in] ctl_address The address of the control node for the
+ * adhoc storage system
  * @param[in] exec_mode The adhoc storage system execution mode
  * @param[in] access_type The adhoc storage system execution type
  * @param[in] walltime The adhoc storage system walltime
@@ -493,7 +495,8 @@ ADM_adhoc_resources_destroy(ADM_adhoc_resources_t res);
  * @return A valid ADM_ADHOC_CONTEXT if successful. NULL otherwise.
  */
 ADM_adhoc_context_t
-ADM_adhoc_context_create(ADM_adhoc_mode_t exec_mode,
+ADM_adhoc_context_create(const char* ctl_address,
+                         ADM_adhoc_mode_t exec_mode,
                          ADM_adhoc_access_t access_type, uint32_t walltime,
                          bool should_flush);
 
diff --git a/src/lib/scord/types.hpp b/src/lib/scord/types.hpp
index 99c0802a2692d81510f06e05cb723be6a879985f..a1002f49e8b14d455141897a62c6e76f286fe466 100644
--- a/src/lib/scord/types.hpp
+++ b/src/lib/scord/types.hpp
@@ -223,12 +223,14 @@ struct adhoc_storage {
 
         ctx() = default;
 
-        ctx(execution_mode exec_mode, access_type access_type,
-            std::uint32_t walltime, bool should_flush);
+        ctx(std::string controller_address, execution_mode exec_mode,
+            access_type access_type, std::uint32_t walltime, bool should_flush);
 
         explicit ctx(ADM_adhoc_context_t ctx);
         explicit operator ADM_adhoc_context_t() const;
 
+        std::string
+        controller_address() const;
         execution_mode
         exec_mode() const;
         enum access_type
@@ -241,6 +243,7 @@ struct adhoc_storage {
         template <class Archive>
         void
         serialize(Archive&& ar) {
+            ar & m_controller_address;
             ar & m_exec_mode;
             ar & m_access_type;
             ar & m_walltime;
@@ -248,6 +251,7 @@ struct adhoc_storage {
         }
 
     private:
+        std::string m_controller_address;
         execution_mode m_exec_mode;
         enum access_type m_access_type;
         std::uint32_t m_walltime;
@@ -814,10 +818,11 @@ struct fmt::formatter<scord::adhoc_storage::ctx> : formatter<std::string_view> {
     auto
     format(const scord::adhoc_storage::ctx& c, FormatContext& ctx) const {
 
-        const auto str = fmt::format("{{execution_mode: {}, access_type: {}, "
-                                     "walltime: {}, should_flush: {}}}",
-                                     c.exec_mode(), c.access_type(),
-                                     c.walltime(), c.should_flush());
+        const auto str =
+                fmt::format("{{controller: {}, execution_mode: {}, "
+                            "access_type: {}, walltime: {}, should_flush: {}}}",
+                            std::quoted(c.controller_address()), c.exec_mode(),
+                            c.access_type(), c.walltime(), c.should_flush());
 
         return formatter<std::string_view>::format(str, ctx);
     }
diff --git a/src/lib/types.c b/src/lib/types.c
index b7480a974c709fa2cbdbcadfbd61b6b26f62a360..97c9ae12adec1647a02c8dedbfc83255140dd4d1 100644
--- a/src/lib/types.c
+++ b/src/lib/types.c
@@ -657,10 +657,15 @@ ADM_data_operation_destroy(ADM_data_operation_t op) {
 }
 
 ADM_adhoc_context_t
-ADM_adhoc_context_create(ADM_adhoc_mode_t exec_mode,
+ADM_adhoc_context_create(const char* ctl_address, ADM_adhoc_mode_t exec_mode,
                          ADM_adhoc_access_t access_type, uint32_t walltime,
                          bool should_flush) {
 
+    if(!ctl_address) {
+        LOGGER_ERROR("The address to the controller cannot be NULL");
+        return NULL;
+    }
+
     struct adm_adhoc_context* adm_adhoc_context =
             (struct adm_adhoc_context*) malloc(sizeof(*adm_adhoc_context));
 
@@ -669,6 +674,12 @@ ADM_adhoc_context_create(ADM_adhoc_mode_t exec_mode,
         return NULL;
     }
 
+
+    size_t n = strlen(ctl_address);
+    adm_adhoc_context->c_ctl_address =
+            (const char*) calloc(n + 1, sizeof(char));
+    strcpy((char*) adm_adhoc_context->c_ctl_address, ctl_address);
+
     adm_adhoc_context->c_mode = exec_mode;
     adm_adhoc_context->c_access = access_type;
     adm_adhoc_context->c_walltime = walltime;
diff --git a/src/lib/types.cpp b/src/lib/types.cpp
index f0e33ef3c873c5af90798382f92b0eedff9018e8..ba584bf309dbf557e873e2a2ec98f47f5a119ff6 100644
--- a/src/lib/types.cpp
+++ b/src/lib/types.cpp
@@ -561,24 +561,33 @@ adhoc_storage::resources::nodes() const {
     return m_nodes;
 }
 
-adhoc_storage::ctx::ctx(adhoc_storage::execution_mode exec_mode,
+adhoc_storage::ctx::ctx(std::string controller_address,
+                        adhoc_storage::execution_mode exec_mode,
                         adhoc_storage::access_type access_type,
                         std::uint32_t walltime, bool should_flush)
-    : m_exec_mode(exec_mode), m_access_type(access_type), m_walltime(walltime),
+    : m_controller_address(std::move(controller_address)),
+      m_exec_mode(exec_mode), m_access_type(access_type), m_walltime(walltime),
       m_should_flush(should_flush) {}
 
 adhoc_storage::ctx::ctx(ADM_adhoc_context_t ctx)
-    : adhoc_storage::ctx(static_cast<execution_mode>(ctx->c_mode),
+    : adhoc_storage::ctx(ctx->c_ctl_address,
+                         static_cast<execution_mode>(ctx->c_mode),
                          static_cast<enum access_type>(ctx->c_access),
                          ctx->c_walltime, ctx->c_should_bg_flush) {}
 
 adhoc_storage::ctx::operator ADM_adhoc_context_t() const {
     return ADM_adhoc_context_create(
+            m_controller_address.c_str(),
             static_cast<ADM_adhoc_mode_t>(m_exec_mode),
             static_cast<ADM_adhoc_access_t>(m_access_type), m_walltime,
             m_should_flush);
 }
 
+std::string
+adhoc_storage::ctx::controller_address() const {
+    return m_controller_address;
+}
+
 adhoc_storage::execution_mode
 adhoc_storage::ctx::exec_mode() const {
     return m_exec_mode;
diff --git a/src/lib/types_private.h b/src/lib/types_private.h
index b66b0b722acf31649cadaa8762fc4fbb64a53de6..3fda9e61877a998f3aa8e2fa0d72fb78374582f0 100644
--- a/src/lib/types_private.h
+++ b/src/lib/types_private.h
@@ -76,6 +76,8 @@ struct adm_dataset_info {
 };
 
 struct adm_adhoc_context {
+    /** The address to the node responsible for this adhoc storage system */
+    const char* c_ctl_address;
     /** The adhoc storage system execution mode */
     ADM_adhoc_mode_t c_mode;
     /** The adhoc storage system access type */