diff --git a/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h b/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h index aeb61a8e28..06b2d8e329 100644 --- a/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h +++ b/resource_detectors/include/opentelemetry/resource_detectors/detail/process_detector_utils.h @@ -5,6 +5,7 @@ #include #include +#include #include "opentelemetry/version.h" @@ -32,20 +33,20 @@ std::string FormFilePath(const int32_t &pid, const char *process_type); std::string GetExecutablePath(const int32_t &pid); /** - * Retrieves the command used to launch the process for a given PID. + * Extracts the command-line arguments and the command. * Platform-specific behavior: - * - Windows: Uses GetCommandLineW() to get the command of the current process. - * - Linux/Unix: Reads the zeroth string of /proc//cmdline file. + * - Windows: Uses CommandLineToArgvW() to parse the command line. + * - Linux/Unix: Reads the /proc//cmdline file and splits it into command and arguments. * - TODO: Need to implement for Darwin */ -std::string ExtractCommand(const std::string &command_line_path); +std::vector ExtractCommandWithArgs(const std::string &command_line_path); /** - * Retrieves the command used to launch the process for a given PID. - * This function is a wrapper around ExtractCommand() and is provided for convenience and - * testability of ExtractCommand(). + * Retrieves the command-line arguments and the command used to launch the process for a given PID. + * This function is a wrapper around ExtractCommandWithArgs() and is provided for convenience and + * testability of ExtractCommandWithArgs(). */ -std::string GetCommand(const int32_t &pid); +std::vector GetCommandWithArgs(const int32_t &pid); } // namespace detail } // namespace resource_detector diff --git a/resource_detectors/process_detector.cc b/resource_detectors/process_detector.cc index c0bde2555a..217ecc7c6c 100644 --- a/resource_detectors/process_detector.cc +++ b/resource_detectors/process_detector.cc @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef _MSC_VER # include @@ -50,16 +51,19 @@ opentelemetry::sdk::resource::Resource ProcessResourceDetector::Detect() noexcep try { - std::string command = opentelemetry::resource_detector::detail::GetCommand(pid); - if (!command.empty()) + std::vector command_with_args = + opentelemetry::resource_detector::detail::GetCommandWithArgs(pid); + if (!command_with_args.empty()) { - attributes[semconv::process::kProcessCommand] = std::move(command); + // Commented until they are properly sanitized + // attributes[semconv::process::kProcessCommand] = command_with_args[0]; + // attributes[semconv::process::kProcessCommandArgs] = std::move(command_with_args); } } catch (const std::exception &ex) { - OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] " << "Error extracting command: " - << ex.what()); + OTEL_INTERNAL_LOG_ERROR("[Process Resource Detector] " + << "Error extracting command with arguments: " << ex.what()); } return ResourceDetector::Create(attributes); diff --git a/resource_detectors/process_detector_utils.cc b/resource_detectors/process_detector_utils.cc index 27a083ea7e..9da40a52a8 100644 --- a/resource_detectors/process_detector_utils.cc +++ b/resource_detectors/process_detector_utils.cc @@ -5,11 +5,14 @@ #include #include +#include #ifdef _MSC_VER // clang-format off # include # include +# include +# pragma comment(lib, "shell32.lib") // clang-format on #else # include @@ -68,43 +71,52 @@ std::string GetExecutablePath(const int32_t &pid) #endif } -std::string GetCommand(const int32_t &pid) +std::vector ExtractCommandWithArgs(const std::string &command_line_path) { -#ifdef _MSC_VER - // On Windows, GetCommandLineW only works for the CURRENT process, - // so we ignore `pid` and just return the current process's command line. - LPCWSTR wcmd = GetCommandLineW(); - if (!wcmd) + std::vector commands; + std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); + std::string command; + while (std::getline(command_line_file, command, '\0')) { - return std::string(); + if (!command.empty()) + { + commands.push_back(command); + } } + return commands; +} - // Convert UTF-16 to UTF-8 - int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL); - if (size_needed <= 0) +std::vector GetCommandWithArgs(const int32_t &pid) +{ +#ifdef _MSC_VER + int argc = 0; + LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argc); + if (!argvW) { - return std::string(); + return {}; // returns an empty vector if CommandLineToArgvW fails } - std::string utf8_command(size_needed - 1, 0); // exclude null terminator - WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL); + std::vector args; + for (int i = 0; i < argc; i++) + { + // Convert UTF-16 to UTF-8 + int size_needed = WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, NULL, 0, NULL, NULL); + if (size_needed > 0) + { + std::string arg(size_needed - 1, 0); + WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, &arg[0], size_needed, NULL, NULL); + args.push_back(arg); + } + } - return utf8_command; + LocalFree(argvW); + return args; #else - // This is the path to get the command that was used to start the process std::string command_line_path = FormFilePath(pid, kCmdlineName); - return ExtractCommand(command_line_path); + return ExtractCommandWithArgs(command_line_path); #endif } -std::string ExtractCommand(const std::string &command_line_path) -{ - std::string command; - std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); - std::getline(command_line_file, command, '\0'); - return command; -} - std::string FormFilePath(const int32_t &pid, const char *process_type) { char buff[64]; diff --git a/resource_detectors/test/process_detector_test.cc b/resource_detectors/test/process_detector_test.cc index 374a3a61cb..8985958f77 100644 --- a/resource_detectors/test/process_detector_test.cc +++ b/resource_detectors/test/process_detector_test.cc @@ -5,6 +5,7 @@ #include #include #include +#include #ifdef _MSC_VER // clang-format off @@ -31,9 +32,9 @@ TEST(ProcessDetectorUtilsTest, FormFilePath) EXPECT_EQ(exe_path, "/proc/1234/exe"); } -TEST(ProcessDetectorUtilsTest, ExtractCommand) +TEST(ProcessDetectorUtilsTest, ExtractCommandWithArgs) { - std::string filename{"test_command.txt"}; + std::string filename{"test_command_args.txt"}; { std::ofstream outfile(filename, std::ios::binary); @@ -41,20 +42,22 @@ TEST(ProcessDetectorUtilsTest, ExtractCommand) outfile.write(raw_data, sizeof(raw_data) - 1); } - std::string command = opentelemetry::resource_detector::detail::ExtractCommand(filename); - EXPECT_EQ(command, std::string{"test_command"}); + std::vector args = + opentelemetry::resource_detector::detail::ExtractCommandWithArgs(filename); + EXPECT_EQ(args, (std::vector{"test_command", "arg1", "arg2", "arg3"})); std::remove(filename.c_str()); // Cleanup } -TEST(ProcessDetectorUtilsTest, EmptyCommandFile) +TEST(ProcessDetectorUtilsTest, EmptyCommandWithArgsFile) { - std::string filename{"empty_command.txt"}; + std::string filename{"empty_command_args.txt"}; std::ofstream outfile(filename, std::ios::binary); outfile.close(); - std::string command = opentelemetry::resource_detector::detail::ExtractCommand(filename); - EXPECT_EQ(command, std::string{""}); + std::vector args = + opentelemetry::resource_detector::detail::ExtractCommandWithArgs(filename); + EXPECT_TRUE(args.empty()); std::remove(filename.c_str()); // Cleanup } @@ -109,40 +112,79 @@ TEST(ProcessDetectorUtilsTest, GetExecutablePathTest) EXPECT_EQ(path, expected_path); } -TEST(ProcessDetectorUtilsTest, GetCommandTest) +TEST(ProcessDetectorUtilsTest, CommandTest) { int32_t pid = getpid(); std::string command; #ifdef _MSC_VER - // On Windows, GetCommandLineW only works for the CURRENT process, - // so we ignore `pid` and just return the current process's command line. - LPCWSTR wcmd = GetCommandLineW(); - if (!wcmd) + int argc = 0; + LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argc); + + if (argvW && argc > 0) { - command = std::string(); + int size_needed = WideCharToMultiByte(CP_UTF8, 0, argvW[0], -1, NULL, 0, NULL, NULL); + if (size_needed > 0) + { + std::string arg(size_needed - 1, 0); + WideCharToMultiByte(CP_UTF8, 0, argvW[0], -1, &arg[0], size_needed, NULL, NULL); + command = arg; + } + + LocalFree(argvW); } else { + command = std::string(); + } +#else + std::string command_line_path = + opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline"); + std::ifstream command_line_file(command_line_path, std::ios::in | std::ios::binary); + std::getline(command_line_file, command, '\0'); +#endif + std::vector expected_command_with_args = + opentelemetry::resource_detector::detail::GetCommandWithArgs(pid); + std::string expected_command; + if (!expected_command_with_args.empty()) + { + expected_command = expected_command_with_args[0]; + } + EXPECT_EQ(command, expected_command); +} - // Convert UTF-16 to UTF-8 - int size_needed = WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, NULL, 0, NULL, NULL); - if (size_needed <= 0) - { - command = std::string(); - } - else +TEST(ProcessDetectorUtilsTest, GetCommandWithArgsTest) +{ + int32_t pid = getpid(); + std::vector args; +#ifdef _MSC_VER + int argc = 0; + LPWSTR *argvW = CommandLineToArgvW(GetCommandLineW(), &argc); + if (!argvW) + { + args = {}; + } + else + { + for (int i = 0; i < argc; i++) { - std::string utf8_command(size_needed - 1, 0); // exclude null terminator - WideCharToMultiByte(CP_UTF8, 0, wcmd, -1, &utf8_command[0], size_needed, NULL, NULL); - command = utf8_command; + // Convert UTF-16 to UTF-8 + int size_needed = WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, NULL, 0, NULL, NULL); + if (size_needed > 0) + { + std::string arg(size_needed - 1, 0); + WideCharToMultiByte(CP_UTF8, 0, argvW[i], -1, &arg[0], size_needed, NULL, NULL); + args.push_back(arg); + } } } + + LocalFree(argvW); #else - // This is the path to get the command that was used to start the process std::string command_line_path = opentelemetry::resource_detector::detail::FormFilePath(pid, "cmdline"); - command = opentelemetry::resource_detector::detail::ExtractCommand(command_line_path); + args = opentelemetry::resource_detector::detail::ExtractCommandWithArgs(command_line_path); #endif - std::string expected_command = opentelemetry::resource_detector::detail::GetCommand(pid); - EXPECT_EQ(command, expected_command); + std::vector expected_args = + opentelemetry::resource_detector::detail::GetCommandWithArgs(pid); + EXPECT_EQ(args, expected_args); }