From 3497b7658c9af5fc1ccb9c8fb954625f681877a4 Mon Sep 17 00:00:00 2001 From: yurekami Date: Sat, 7 Feb 2026 02:45:22 -0800 Subject: [PATCH] fix: resolve symlinks when loading plugin libraries Use stat() instead of d_type to check if a file is a regular file when scanning for plugin libraries. This allows symlinked plugins to be loaded, which is common in ROS 2 workspaces built with colcon build --symlink-install. The stat() approach also handles filesystems that report DT_UNKNOWN for d_type (e.g., NFS, XFS), making the code more robust. Fixes #3072 --- src/engine/engine_plugin.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/engine/engine_plugin.cc b/src/engine/engine_plugin.cc index e9255d8b..31eb4f5f 100644 --- a/src/engine/engine_plugin.cc +++ b/src/engine/engine_plugin.cc @@ -35,6 +35,7 @@ extern "C" { #else #include #include + #include #endif } @@ -613,13 +614,14 @@ void mj_loadAllPluginLibraries(const char* directory, // go through each entry in the directory for (struct dirent* dp; (dp = readdir(dirp));) { - // only look at regular files (skip symlinks, pipes, directories, etc.) - if (dp->d_type == DT_REG) { - const std::string name(dp->d_name); - if (name.size() > dso_suffix.size() && - name.substr(name.size() - dso_suffix.size()) == dso_suffix) { - // load the library - const std::string dso_path = directory + sep + name; + const std::string name(dp->d_name); + if (name.size() > dso_suffix.size() && + name.substr(name.size() - dso_suffix.size()) == dso_suffix) { + const std::string dso_path = directory + sep + name; + + // use stat to resolve symlinks and check that the target is a regular file + struct stat file_stat; + if (stat(dso_path.c_str(), &file_stat) == 0 && S_ISREG(file_stat.st_mode)) { load_dso_and_call_callback(name.c_str(), dso_path.c_str()); } }