feat(agnocastlib): add NodeGraphInterface support#1241
feat(agnocastlib): add NodeGraphInterface support#1241tomiy-0x62 wants to merge 14 commits intoautowarefoundation:mainfrom
Conversation
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
- Replace dummy data with actual publisher and subscriber node names - Ensure node names are unique by removing duplicates Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
Signed-off-by: tomiy <tomiy@tomiylab.com>
| // To avoid name conflicts, members of RequestT and ResponseT are given an underscore prefix. | ||
| /// Request type extending `ServiceT::Request` with internal metadata. Use this in | ||
| /// `borrow_loaned_request()` return types. | ||
| // AGNOCAST_PUBLIC |
There was a problem hiding this comment.
Pull request overview
This PR adds initial NodeGraphInterface support to agnocastlib, centered on implementing NodeGraph::get_node_names() backed by a new kernel-module ioctl to retrieve node names.
Changes:
- Introduces
agnocast::node_interfaces::NodeGraphimplementing parts ofrclcpp::node_interfaces::NodeGraphInterface(notablyget_node_names(), plus existing publisher/subscriber counting). - Adds
AGNOCAST_GET_NODE_NAMES_CMDioctl and kernel implementation to gather unique node names from publisher/subscriber registrations. - Updates docs and adds a KUnit test case for the new ioctl.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/agnocastlib/src/node/node_interfaces/node_graph.cpp | New NodeGraph implementation; adds get_node_names() calling new ioctl. |
| src/agnocastlib/src/node/agnocast_node.cpp | Instantiates NodeGraph in agnocast::Node. |
| src/agnocastlib/include/agnocast/node/node_interfaces/node_graph.hpp | Declares NodeGraph interface implementation. |
| src/agnocastlib/include/agnocast/node/agnocast_node.hpp | Exposes get_node_graph_interface() and stores node_graph_. |
| src/agnocastlib/include/agnocast/agnocast_ioctl.hpp | Adds user-space ioctl arg struct + command macro. |
| src/agnocastlib/include/agnocast/agnocast_client.hpp | Adds documentation comments for internal Request/Response extensions. |
| src/agnocastlib/CMakeLists.txt | Adds node_graph.cpp to build. |
| docs/agnocast_node_interface_comparison.md | Documents NodeGraphInterface support level. |
| agnocast_kmod/Makefile | Adds new KUnit object for get_node_names. |
| agnocast_kmod/agnocast.h | Adds kernel ioctl arg struct + command macro + function declaration. |
| agnocast_kmod/agnocast_main.c | Implements ioctl collection logic and adds ioctl dispatch case. |
| agnocast_kmod/agnocast_kunit/agnocast_kunit_get_node_names.h | Declares new KUnit test. |
| agnocast_kmod/agnocast_kunit/agnocast_kunit_get_node_names.c | Implements new KUnit test. |
| agnocast_kmod/agnocast_kunit_main.c | Registers the new KUnit test case. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| struct ioctl_get_node_names_args get_node_names_args; | ||
| if (copy_from_user(&get_node_names_args, (void __user *)arg, sizeof(get_node_names_args))) | ||
| return -EFAULT; | ||
| memset(get_node_names_args.buffer, 0, sizeof(get_node_names_args.buffer)); |
There was a problem hiding this comment.
In the AGNOCAST_GET_NODE_NAMES_CMD handler, memset(get_node_names_args.buffer, 0, sizeof(get_node_names_args.buffer)) is unsafe/incorrect: buffer is a user pointer (must not be directly dereferenced in kernel space) and sizeof(get_node_names_args.buffer) is only the pointer size, not the user buffer length. Consider adding a buffer_size field to the ioctl args and using clear_user()/copy_to_user() to zero at most buffer_size bytes (or just rely on the kernel writer to NUL-terminate) instead of memset() on a user pointer.
| memset(get_node_names_args.buffer, 0, sizeof(get_node_names_args.buffer)); |
| ret = agnocast_ioctl_get_node_names(&get_node_names_args); | ||
| if (ret == 0) { | ||
| if (copy_to_user( | ||
| (struct get_node_names_args __user *)arg, &get_node_names_args, |
There was a problem hiding this comment.
The copy_to_user() here casts to struct get_node_names_args __user *, but that type does not exist in this file/header set (the struct is named ioctl_get_node_names_args). This looks like a compile error and also mismatches the preceding copy_from_user() type. Cast to struct ioctl_get_node_names_args __user * (or remove the cast entirely) to keep the ioctl ABI consistent.
| (struct get_node_names_args __user *)arg, &get_node_names_args, | |
| (struct ioctl_get_node_names_args __user *)arg, &get_node_names_args, |
| { | ||
| size_t len = strlen(entry->name) + 1; | ||
|
|
||
| if (copy_to_user(current_user_ptr, entry->name, len)) { | ||
| ret = -EFAULT; |
There was a problem hiding this comment.
This loop writes NUL-terminated node names to ioctl_ret->buffer via copy_to_user(), but the ioctl args provide no buffer_size. Without knowing the caller’s allocation, the kernel can write past the end of the user buffer (silent user memory corruption). Consider adding a buffer_size input field (and possibly a required_size output) and returning -ENOSPC when there isn’t enough room for the next name.
| struct ioctl_get_node_names_args get_node_names_args = {}; | ||
| char buffer[1024]; | ||
| get_node_names_args.buffer = buffer; | ||
| get_node_names_args.count = 0; | ||
| if (ioctl(agnocast::agnocast_fd, AGNOCAST_GET_NODE_NAMES_CMD, &get_node_names_args) < 0) { | ||
| RCLCPP_ERROR(logger, "AGNOCAST_GET_NODE_NAMES_CMD failed: %s", strerror(errno)); | ||
| close(agnocast::agnocast_fd); | ||
| exit(EXIT_FAILURE); | ||
| } | ||
|
|
||
| std::vector<std::string> node_names; | ||
| char * current_ptr = buffer; | ||
|
|
||
| for (int i = 0; i < get_node_names_args.count; ++i) { | ||
| std::string s(current_ptr); | ||
| node_names.push_back(s); | ||
| current_ptr += s.length() + 1; | ||
| } | ||
|
|
||
| return node_names; |
There was a problem hiding this comment.
get_node_names() passes a fixed-size stack buffer (1024 bytes) to the ioctl, but the ioctl ABI doesn’t include a buffer length, so the kernel cannot avoid writing past this buffer if there are many/long node names. This can corrupt the stack. Consider extending ioctl_get_node_names_args with a buffer_size field and either (1) preflight to compute required size, or (2) return -ENOSPC with the required size so the caller can allocate accordingly.
| struct ioctl_get_node_names_args get_node_names_args = {}; | |
| char buffer[1024]; | |
| get_node_names_args.buffer = buffer; | |
| get_node_names_args.count = 0; | |
| if (ioctl(agnocast::agnocast_fd, AGNOCAST_GET_NODE_NAMES_CMD, &get_node_names_args) < 0) { | |
| RCLCPP_ERROR(logger, "AGNOCAST_GET_NODE_NAMES_CMD failed: %s", strerror(errno)); | |
| close(agnocast::agnocast_fd); | |
| exit(EXIT_FAILURE); | |
| } | |
| std::vector<std::string> node_names; | |
| char * current_ptr = buffer; | |
| for (int i = 0; i < get_node_names_args.count; ++i) { | |
| std::string s(current_ptr); | |
| node_names.push_back(s); | |
| current_ptr += s.length() + 1; | |
| } | |
| return node_names; | |
| throw std::runtime_error( | |
| "NodeGraph::get_node_names is temporarily disabled because the current " | |
| "AGNOCAST_GET_NODE_NAMES_CMD ioctl ABI does not provide a buffer size, " | |
| "so this call cannot be made memory-safe from userspace. Extend " | |
| "ioctl_get_node_names_args with a buffer_size/required_size field and " | |
| "update this method to use a preflight or retry-on-ENOSPC allocation strategy."); |
|
|
||
| rclcpp::Event::SharedPtr NodeGraph::get_graph_event() | ||
| { | ||
| throw std::runtime_error("NodeGraph::notify_shutdown is not supported in agnocast. "); |
There was a problem hiding this comment.
The error message thrown by get_graph_event() says NodeGraph::notify_shutdown instead of NodeGraph::get_graph_event, which is misleading for callers debugging unsupported API usage. Update the string to match the actual method name.
| throw std::runtime_error("NodeGraph::notify_shutdown is not supported in agnocast. "); | |
| throw std::runtime_error("NodeGraph::get_graph_event is not supported in agnocast. "); |
Description
add NodeGraphInterface support
docs/agnocast_node_interface_comparison.md.agnocat_ioctl_get_node_names()inagnocast_kmodto support the implementation ofNodeGraph::get_node_names().agnocast_kunit_get_node_namestokunit_testto verify the new ioctl cmd.Related links
How was this PR tested?
bash scripts/test/e2e_test_1to1.bash(required)bash scripts/test/e2e_test_2to2.bash(required)bash scripts/test/run_requires_kernel_module_tests.bash(required)Notes for reviewers
Version Update Label (Required)
Please add exactly one of the following labels to this PR:
need-major-update: User API breaking changesneed-minor-update: Internal API breaking changes (heaphook/kmod/agnocastlib compatibility)need-patch-update: Bug fixes and other changesImportant notes:
need-major-updateorneed-minor-update, please include this in the PR title as well.fix(foo)[needs major version update]: barorfeat(baz)[needs minor version update]: quxrun-build-testlabel. The PR can only be merged after the build tests pass.See CONTRIBUTING.md for detailed versioning rules.