From f000c13c849afe340362772e147711ae7f80c2a4 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Mon, 20 May 2024 10:47:39 -0600 Subject: [PATCH 01/11] chore(version): bump 10.22 (#902) --- VERSION | 2 +- axiom/nr_version.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index b61c07ffd..b7604b0c8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.21.0 +10.22.0 diff --git a/axiom/nr_version.c b/axiom/nr_version.c index 18d948754..0000847fd 100644 --- a/axiom/nr_version.c +++ b/axiom/nr_version.c @@ -23,7 +23,6 @@ /* * Current version naming scheme is flowers * - * buttercup 26Apr2022 (9.21) * cosmos 29Jun2022 (10.0) * dahlia 19Sep2022 (10.1) * echinacea 03Oct2022 (10.2) @@ -45,8 +44,9 @@ * ulmus 04Mar2024 (10.18) * viburnum 18Mar2024 (10.19) * wallflower 06May2024 (10.20) + * xerophyllum 20May2024 (10.21) */ -#define NR_CODENAME "xerophyllum" +#define NR_CODENAME "yarrow" const char* nr_version(void) { return NR_STR2(NR_VERSION); From 8ff820d52c7cea7a78095ee46f08cc5d740b26dd Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Thu, 23 May 2024 10:05:38 -0400 Subject: [PATCH 02/11] fix(tests): Fixes package-name-only test case (#903) The PHP package test was not being correctly checked for the case where the package was specified as `package_name_only` and a version was actually created for the package. --- daemon/internal/newrelic/integration/test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/daemon/internal/newrelic/integration/test.go b/daemon/internal/newrelic/integration/test.go index 0f5a23aea..4a52f31a3 100644 --- a/daemon/internal/newrelic/integration/test.go +++ b/daemon/internal/newrelic/integration/test.go @@ -604,7 +604,17 @@ func (t *Test) comparePhpPackages(harvest *newrelic.Harvest) { expected_version = override_version.(string) } - if testPackageNameOnly || expected_version == actualPackages[i].Version { + if testPackageNameOnly { + if " " != actualPackages[i].Version { + t.Fail(fmt.Errorf("Expected no package version and a package version was detected - expected \" \" actual %+v. ", + actualPackages[i].Version)) + return + } else { + continue + } + } + + if expected_version == actualPackages[i].Version { continue } } From 68046f3102e0e11525993ee8f4f7775ac356d1d8 Mon Sep 17 00:00:00 2001 From: Amber Sistla Date: Thu, 23 May 2024 07:39:12 -0700 Subject: [PATCH 03/11] fix(agent): Don't send only major version to vm package (#904) Guzzle 7+ was sending only the major version to the vm package If only major version is available, it should not be sent. However, we can still use major version for the supportability metric. --- agent/lib_guzzle6.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/agent/lib_guzzle6.c b/agent/lib_guzzle6.c index a106eac71..d9003545c 100644 --- a/agent/lib_guzzle6.c +++ b/agent/lib_guzzle6.c @@ -352,14 +352,20 @@ NR_PHP_WRAPPER_START(nr_guzzle6_client_construct) { zval* this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS); char* version = nr_php_get_object_constant(this_var, "VERSION"); - if (NULL == version) { - version = nr_php_get_object_constant(this_var, "MAJOR_VERSION"); - } if (NRINI(vulnerability_management_package_detection_enabled)) { // Add php package to transaction nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); } + + /* + * If we were unable to get the full version before, at least we can extract + * the major version to send to the supportability metric. + * This is relevant to guzzle7+ which no longer supplies full version. + */ + if (NULL == version) { + version = nr_php_get_object_constant(this_var, "MAJOR_VERSION"); + } nr_fw_support_add_package_supportability_metric(NRPRG(txn), PHP_PACKAGE_NAME, version); nr_free(version); From bc0766be7b8ce2091456ed3a4cdcaa20fbada4ab Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Mon, 10 Jun 2024 10:59:35 -0400 Subject: [PATCH 04/11] fix: install correctly on bare bones OSs (#913) Some OSs stripped down to bare bones (like base OS container images) don't have all directories needed by the installer (e.g. /etc/init.d). Package installers simply create these directories. Make tarball installer behave similar to package installer and create required directories. --------- Co-authored-by: Zach Neumann --- agent/newrelic-install.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/agent/newrelic-install.sh b/agent/newrelic-install.sh index 68c2d1c45..3d0637dc0 100755 --- a/agent/newrelic-install.sh +++ b/agent/newrelic-install.sh @@ -852,8 +852,8 @@ disp_get_php_list() { set_osdifile() { osdifile= - if [ -n "${NR_INSTALL_INITFILE}" ]; then - osdifile="${NR_INSTALL_INITFILE}" + if [ -n "${NR_INSTALL_INITSCRIPT}" ]; then + osdifile="${NR_INSTALL_INITSCRIPT}" fi if [ "${ostype}" = "darwin" ]; then : ${osdifile:=/usr/bin/newrelic-daemon-service} @@ -1780,6 +1780,10 @@ EOF set_osdifile if [ -z "${ispkg}" ]; then + # ensure target directory exists + if [ ! -d "$(dirname ${osdifile})" ]; then + logcmd mkdir -p -m 0755 "$(dirname ${osdifile})" + fi if logcmd cp -f "${ilibdir}/scripts/init.${ostype}" "${osdifile}"; then logcmd chmod 755 "${osdifile}" || { fatal "failed to set permissions on ${osdifile}" @@ -1798,6 +1802,10 @@ EOF fi if [ -n "${sysconf}" -a ! -f "${sysconf}" ]; then + # ensure target directory exists + if [ ! -d "$(dirname ${sysconf})" ]; then + logcmd mkdir -p -m 0755 "$(dirname ${sysconf})" + fi if logcmd cp -f "${ilibdir}/scripts/newrelic.sysconfig" "${sysconf}"; then logcmd chmod 755 "${sysconf}" || { fatal "failed to set permissions on ${sysconf}" From 287e6a8691af5b9cab36ef218795d070e4648f88 Mon Sep 17 00:00:00 2001 From: Michal Nowacki Date: Thu, 13 Jun 2024 18:34:20 -0400 Subject: [PATCH 05/11] feat: deprecate PHPs 7.0 and 7.1 (#914) Have the newrelic-install script log warning when it detects deprecated PHPs. --- agent/newrelic-install.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/agent/newrelic-install.sh b/agent/newrelic-install.sh index 3d0637dc0..d61137721 100755 --- a/agent/newrelic-install.sh +++ b/agent/newrelic-install.sh @@ -1038,9 +1038,19 @@ for this copy of PHP. We apologize for the inconvenience. case "${pi_ver}" in 7.0.*) + warning_message="${pdir}: Support for PHP '${pi_ver}' in the New Relic PHP agent is deprecated." + if [ -z "${NR_INSTALL_SILENT}" ]; then + echo $warning_message + fi + log $warning_message ;; 7.1.*) + warning_message="${pdir}: Support for PHP '${pi_ver}' in the New Relic PHP agent is deprecated." + if [ -z "${NR_INSTALL_SILENT}" ]; then + echo $warning_message + fi + log $warning_message ;; 7.2.*) From cf3b2c36ce60ad6bbbfea0c911a7df200143cd56 Mon Sep 17 00:00:00 2001 From: Michael Fulbright <89205663+mfulb@users.noreply.github.com> Date: Fri, 14 Jun 2024 12:26:36 -0400 Subject: [PATCH 06/11] fix(agent): Fixes Doctrine 2.18+ detection (#916) Addresses changes in Doctrine 2.18+ (but not 3.x) that affected detection. There was a [commit](https://github.com/doctrine/orm/commit/073f2aa891249e582e63320cab34b035b591cf22) for Doctrine 2.18 that reworked the directory structure of the Doctrine library. This PR adds another file for Doctrine to allow it to be auto-detected. --- agent/php_execute.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/agent/php_execute.c b/agent/php_execute.c index e4300d902..80ad27bdf 100644 --- a/agent/php_execute.c +++ b/agent/php_execute.c @@ -484,7 +484,11 @@ typedef struct _nr_library_table_t { */ // clang-format: off static nr_library_table_t libraries[] = { + /* Doctrine < 2.18 */ {"Doctrine 2", NR_PSTR("doctrine/orm/query.php"), nr_doctrine2_enable}, + /* Doctrine 2.18 reworked the directory structure */ + {"Doctrine 2", NR_PSTR("doctrine/orm/src/query.php"), nr_doctrine2_enable}, + {"Guzzle 3", NR_PSTR("guzzle/http/client.php"), nr_guzzle3_enable}, {"Guzzle 4-5", NR_PSTR("hasemitterinterface.php"), nr_guzzle4_enable}, {"Guzzle 6", NR_PSTR("guzzle/src/functions_include.php"), nr_guzzle6_enable}, From 8c16b708acb84bb4e7862829b83583ae16aa3158 Mon Sep 17 00:00:00 2001 From: Amber Sistla Date: Fri, 14 Jun 2024 11:19:35 -0700 Subject: [PATCH 07/11] fix(agent): Slim txn naming updates (#917) 1) the scheme for route run to correlate with proper OAPI naming was incorrectly listed and therefore txn naming precedence was incorrectly handled 2) this PR kept same naming scheme in route:run putting it before wrapper_call and saying ok to overwrite (instead of after and not ok to overwrite) 3) number 2 needed to incorporate fallback naming scheme 4) added fallback naming scheme (since some errors and middleware intervention give the wrong txn name) using dispatcher:dispatch. When there is an exception thrown in a route that is handled by the slim routing and error middleware, it is superseded by: Uncaught exception 'Slim\\Exception\\HttpNotFoundException' with message 'Not found.' and transformed into an http 404 error and is shown in the http.statuscode field as 404. If the middleware isn't loaded, it is recorded as an uncaught exception and status code is 200. Wrap function Slim\\Routing\\Dispatcher::dispatch It will name set the txn_name in case we hit an error and Slim\\Routing\\Route::run isn't called. It is set to be overwritable do that if we encounter Slim\\Routing\\Route::run, it can overwrite with the most accurate info (this is only relevant in the case of redirects). This allows us to give the txn_name even in error cases when Slim\\Routing\\Route::run isn't called. Corresponds with multiverse updates which passed against this branch. --- agent/fw_slim.c | 77 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 9 deletions(-) diff --git a/agent/fw_slim.c b/agent/fw_slim.c index 493e325a8..a70323459 100644 --- a/agent/fw_slim.c +++ b/agent/fw_slim.c @@ -81,28 +81,65 @@ NR_PHP_WRAPPER(nr_slim2_route_dispatch) { NR_PHP_WRAPPER_END /* - * Wrap the \Slim\Route::run method, which is the happy path for Slim routing. + * Wrap the Slim 3\Slim\Route::run method + * and + * Slim 4 Slim\\Routing\\Route::run + * which are the happy paths for Slim 3/4 routing. * i.e. The router has succesfully matched the URL and dispatched the request * to a route. * - * In this case, `nr_txn_set_path` is called after `NR_PHP_WRAPPER_CALL` with - * `NR_OK_TO_OVERWRITE` and as this corresponds to calling the wrapped function - * in func_end no change is needed to ensure OAPI compatibility as it will use - * the default func_end after callback. This entails that the first wrapped - * function call of this type gets to name the txn. + * In this case, `nr_txn_set_path` is called before `NR_PHP_WRAPPER_CALL` with + * `NR_OK_TO_OVERWRITE` and as this corresponds to calling the last wrapped + * function call of this type gets to name the txn; therefore needs a before + * call for OAPI. */ NR_PHP_WRAPPER(nr_slim3_4_route_run) { zval* this_var = NULL; char* txn_name = NULL; (void)wraprec; + NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_SLIM); this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS TSRMLS_CC); txn_name = nr_slim_path_from_route(this_var TSRMLS_CC); nr_php_scope_release(&this_var); + if (txn_name) { + nr_txn_set_path("Slim", NRPRG(txn), txn_name, NR_PATH_TYPE_ACTION, + NR_OK_TO_OVERWRITE); + nr_free(txn_name); + } + NR_PHP_WRAPPER_CALL; +} +NR_PHP_WRAPPER_END + +/* + * public function dispatch(string $method, string $uri): RoutingResults + * This is fallback naming mechanism for Slim 4 routing when the Slim 4 + * Slim\\Routing\\Route::run does not run due middlware intervening on + * certain errors. + * In this case, `nr_txn_set_path` is called before `NR_PHP_WRAPPER_CALL` with + * `NR_NOT_OK_TO_OVERWRITE` and as this corresponds to calling the first wrapped + * function in func_begin. + */ +NR_PHP_WRAPPER(nr_slim4_route_dispatch) { + char* txn_name = NULL; + zval* route_name = NULL; + + (void)wraprec; + + NR_PHP_WRAPPER_REQUIRE_FRAMEWORK(NR_FW_SLIM); + + /* Get the route name. The first arg is the method, 2nd arg is routename. */ + route_name = nr_php_arg_get(2, NR_EXECUTE_ORIG_ARGS); + + if (nr_php_is_zval_valid_string(route_name)) { + txn_name = nr_strndup(Z_STRVAL_P(route_name), Z_STRLEN_P(route_name)); + } + + nr_php_arg_release(&route_name); if (txn_name) { nr_txn_set_path("Slim", NRPRG(txn), txn_name, NR_PATH_TYPE_ACTION, @@ -120,7 +157,7 @@ NR_PHP_WRAPPER(nr_slim_application_construct) { (void)wraprec; version = nr_php_get_object_constant(this_var, "VERSION"); - + if (NRINI(vulnerability_management_package_detection_enabled)) { // Add php package to transaction nr_txn_add_php_package(NRPRG(txn), PHP_PACKAGE_NAME, version); @@ -139,12 +176,34 @@ void nr_slim_enable(TSRMLS_D) { nr_php_wrap_user_function(NR_PSTR("Slim\\Route::dispatch"), nr_slim2_route_dispatch TSRMLS_CC); + +#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ + && !defined OVERWRITE_ZEND_EXECUTE_DATA + /* Slim 3 */ - nr_php_wrap_user_function(NR_PSTR("Slim\\Route::run"), - nr_slim3_4_route_run TSRMLS_CC); + nr_php_wrap_user_function_before_after_clean( + NR_PSTR("Slim\\Route::run"), nr_slim3_4_route_run, NULL, NULL); + /* Slim 4 */ + nr_php_wrap_user_function_before_after_clean( + NR_PSTR("Slim\\Routing\\Route::run"), nr_slim3_4_route_run, NULL, NULL); + + /* Slim 4 */ + nr_php_wrap_user_function_before_after_clean( + NR_PSTR("Slim\\Routing\\Dispatcher::dispatch"), nr_slim4_route_dispatch, + NULL, NULL); +#else + /* Slim 4*/ nr_php_wrap_user_function(NR_PSTR("Slim\\Routing\\Route::run"), nr_slim3_4_route_run TSRMLS_CC); + /* Slim 4 */ + nr_php_wrap_user_function(NR_PSTR("Slim\\Routing\\Dispatcher::dispatch"), + nr_slim4_route_dispatch TSRMLS_CC); + + /* Slim 3 */ + nr_php_wrap_user_function(NR_PSTR("Slim\\Route::run"), + nr_slim3_4_route_run TSRMLS_CC); +#endif /* Slim 2 does not have the same path as Slim 3/4 which is why we need to separate these*/ From 756c1aa06ee889292911614fa30b863973fd7646 Mon Sep 17 00:00:00 2001 From: ZNeumann Date: Tue, 18 Jun 2024 13:43:44 -0600 Subject: [PATCH 08/11] fix(scripts): fix rhel and alpine init scripts (#919) Systemd systems may not have access to the functions file provided by initscripts. The code from initscripts is provided under the GNU v2 license and will not be pulled in. Additionally, a header comment block must be added, using https://refspecs.linuxbase.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/initscrcomconv.html for specs --------- Co-authored-by: Michal Nowacki --- agent/scripts/init.alpine | 3 +-- agent/scripts/init.rhel | 47 +++++++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/agent/scripts/init.alpine b/agent/scripts/init.alpine index 63d143e66..0fc717dbc 100755 --- a/agent/scripts/init.alpine +++ b/agent/scripts/init.alpine @@ -1,10 +1,9 @@ +#!/sbin/openrc-run # # Copyright 2020 New Relic Corporation. All rights reserved. # SPDX-License-Identifier: Apache-2.0 # -#!/sbin/openrc-run - description="New Relic Daemon" command="${nrdaemon:-/usr/bin/newrelic-daemon}" command_args= diff --git a/agent/scripts/init.rhel b/agent/scripts/init.rhel index 82f43608c..2e88a6307 100644 --- a/agent/scripts/init.rhel +++ b/agent/scripts/init.rhel @@ -10,6 +10,15 @@ # processname: newrelic-daemon # config: /etc/newrelic/newrelic.cfg # +### BEGIN INIT INFO +# Provides: newrelic-daemon +# Required-Start: $all +# Required-Stop: +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Description: The New Relic Daemon is used by the New Relic PHP Agent to communicate +# with the New Relic Backend +### END INIT INFO LANG=C NAME=newrelic-daemon @@ -18,7 +27,11 @@ DESC="New Relic Daemon" # # Source function library. # -. /etc/init.d/functions +has_initd_functions=0 +if [ -f /etc/init.d/functions ]; then + . /etc/init.d/functions + has_initd_functions=1 +fi ulimit -n 2048 > /dev/null 2>&1 @@ -223,14 +236,22 @@ EOF if running ; then if [ -z "${NR_SILENT}" -a -z "${SILENT}" ]; then - success - echo + if [ $has_initd_functions -eq 1 ]; then + success + echo + else + echo " [ OK ]" + fi fi return 0 else if [ -z "${NR_SILENT}" -a -z "${SILENT}" ]; then - failure - echo + if [ $has_initd_functions -eq 1 ]; then + failure + echo + else + echo " [FAILED]" + fi fi return 1 fi @@ -274,14 +295,22 @@ stop() { if running ; then if [ -z "${NR_SILENT}" -a -z "${SILENT}" ]; then - failure - echo + if [ $has_initd_functions -eq 1 ]; then + failure + echo + else + echo " [FAILED]" + fi fi return 1 else if [ -z "${NR_SILENT}" -a -z "${SILENT}" ]; then - success - echo + if [ $has_initd_functions -eq 1 ]; then + success + echo + else + echo " [ OK ]" + fi fi return 0 fi From a8663c7f800db92139647b2297d28db82db4e364 Mon Sep 17 00:00:00 2001 From: bduranleau-nr <106178551+bduranleau-nr@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:36:22 -0500 Subject: [PATCH 09/11] fix(agent): fix mongodb instrumentation for PHP 8.0+ (#878) - split mongodb instrumentation into before/after inline with OAPI paradigm. - remove broken integration tests in favor of Multiverse tests --- agent/lib_mongodb.c | 168 +++++++++++++++++- agent/php_wrapper.c | 21 +++ agent/php_wrapper.h | 8 + docker-compose.yaml | 9 - tests/include/config.php | 8 - tests/integration/mongo/mongo.inc | 14 -- tests/integration/mongo/skipif.inc | 18 -- tests/integration/mongo/test_execute.php | 55 ------ .../mongo/test_execute_logging_off.php | 56 ------ tests/integration/mongo/test_find.php | 71 -------- 10 files changed, 196 insertions(+), 232 deletions(-) delete mode 100644 tests/integration/mongo/mongo.inc delete mode 100644 tests/integration/mongo/skipif.inc delete mode 100644 tests/integration/mongo/test_execute.php delete mode 100644 tests/integration/mongo/test_execute_logging_off.php delete mode 100644 tests/integration/mongo/test_find.php diff --git a/agent/lib_mongodb.c b/agent/lib_mongodb.c index 51e979abd..9b5ca9c99 100644 --- a/agent/lib_mongodb.c +++ b/agent/lib_mongodb.c @@ -108,6 +108,8 @@ void nr_mongodb_get_host_and_port_path_or_id(zval* server, } } +#if ZEND_MODULE_API_NO < ZEND_8_0_X_API_NO \ + || defined OVERWRITE_ZEND_EXECUTE_DATA NR_PHP_WRAPPER(nr_mongodb_operation) { const char* this_klass = "MongoDB\\Operation\\Executable"; zval* collection = NULL; @@ -173,7 +175,169 @@ NR_PHP_WRAPPER(nr_mongodb_operation) { } NR_PHP_WRAPPER_END -void nr_mongodb_enable(TSRMLS_D) { +#else + +NR_PHP_WRAPPER(nr_mongodb_operation_before) { + (void)wraprec; + nr_segment_t* segment = NULL; + segment = nr_segment_start(NRPRG(txn), NULL, NULL); + if (NULL != segment) { + segment->wraprec = auto_segment->wraprec; + } +} +NR_PHP_WRAPPER_END + +NR_PHP_WRAPPER(nr_mongodb_operation_after) { + const char* this_klass = "MongoDB\\Operation\\Executable"; + zval* collection = NULL; + zval* database = NULL; + zval* server = NULL; + zval* this_var = NULL; + bool discard_segment = false; + nr_datastore_instance_t instance = { + .host = NULL, + .port_path_or_id = NULL, + .database_name = NULL, + }; + + // tell the compiler to ignore the cast from const char * to char * + // to save having to do a strdup operation +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + nr_segment_datastore_params_t params = { + .collection = NULL, + .datastore = { + .type = NR_DATASTORE_MONGODB, + }, + .operation = (char *)wraprec->extra, + .instance = &instance, + .callbacks = { + .backtrace = nr_php_backtrace_callback, + }, + }; +#pragma GCC diagnostic pop + /* + * We check for the interface all Collection operations extend, rather than + * their specific class. Not all operations have the properties we need but + * the ones we hook do (as of mongo-php-library v.1.1). + */ + this_var = nr_php_scope_get(NR_EXECUTE_ORIG_ARGS); + if (!nr_php_object_instanceof_class(this_var, this_klass)) { + nrl_verbosedebug(NRL_FRAMEWORK, "%s: operation is not %s", __func__, + this_klass); + discard_segment = true; + goto leave; + } + + collection = nr_php_get_zval_object_property(this_var, "collectionName"); + if (nr_php_is_zval_valid_string(collection)) { + params.collection = Z_STRVAL_P(collection); + } + + database = nr_php_get_zval_object_property(this_var, "databaseName"); + if (nr_php_is_zval_valid_string(database)) { + instance.database_name = Z_STRVAL_P(database); + } + + server = nr_php_arg_get(1, NR_EXECUTE_ORIG_ARGS); + nr_mongodb_get_host_and_port_path_or_id(server, &instance.host, + &instance.port_path_or_id); + +leave: + if (discard_segment) { + nr_segment_discard(&auto_segment); + } else { + nr_segment_datastore_end(&auto_segment, ¶ms); + } + nr_php_arg_release(&server); + nr_php_scope_release(&this_var); + nr_free(instance.host); + nr_free(instance.port_path_or_id); +} +NR_PHP_WRAPPER_END + +#endif /* OAPI */ + +void nr_mongodb_enable() { +#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \ + && !defined OVERWRITE_ZEND_EXECUTE_DATA + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Aggregate::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "aggregate"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\BulkWrite::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "bulkWrite"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Count::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "count"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\CreateIndexes::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "createIndexes"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Delete::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "delete"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Distinct::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "distinct"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\DropCollection::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "dropCollection"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\DropIndexes::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "dropIndexes"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Find::execute"), nr_mongodb_operation_before, + nr_mongodb_operation_after, nr_mongodb_operation_after, "find"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\FindAndModify::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "findAndModify"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\InsertMany::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "insertMany"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\InsertOne::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "insertOne"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\ListIndexes::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "listIndexes"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\Update::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "update"); + + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "databaseCommand"); + +#else /* Non-OAPI */ + /* * We instrument interesting methods on the MongoDB\Collection class via their * associated MongoDB\Operation classes. @@ -265,6 +429,8 @@ void nr_mongodb_enable(TSRMLS_D) { NR_PSTR("MongoDB\\Operation\\DatabaseCommand::execute"), nr_mongodb_operation, "databaseCommand" TSRMLS_CC); +#endif /* OAPI */ + if (NRINI(vulnerability_management_package_detection_enabled)) { nr_txn_add_php_package(NRPRG(txn), "mongodb/mongodb", PHP_PACKAGE_VERSION_UNKNOWN); diff --git a/agent/php_wrapper.c b/agent/php_wrapper.c index daa01bb4f..6631c06d2 100644 --- a/agent/php_wrapper.c +++ b/agent/php_wrapper.c @@ -73,6 +73,27 @@ nruserfn_t* nr_php_wrap_user_function_before_after_clean( return wraprec; } +nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( + const char* name, + size_t namelen, + nrspecialfn_t before_callback, + nrspecialfn_t after_callback, + nrspecialfn_t clean_callback, + const char* extra) { + nruserfn_t* wraprec = nr_php_wrap_user_function_before_after_clean( + name, namelen, before_callback, after_callback, clean_callback); + + if (nrunlikely(NULL == wraprec)) { + nrl_warning(NRL_INSTRUMENT, "%s: unable to wrap '%s'", __func__, + NRSAFESTR(name)); + return wraprec; + } + + wraprec->extra = extra; + + return wraprec; +} + nruserfn_t* nr_php_wrap_callable_before_after_clean( zend_function* callable, nrspecialfn_t before_callback, diff --git a/agent/php_wrapper.h b/agent/php_wrapper.h index 27e0dba91..058e06e08 100644 --- a/agent/php_wrapper.h +++ b/agent/php_wrapper.h @@ -150,6 +150,14 @@ extern nruserfn_t* nr_php_wrap_callable_before_after_clean( nrspecialfn_t before_callback, nrspecialfn_t after_callback, nrspecialfn_t clean_callback); + +extern nruserfn_t* nr_php_wrap_user_function_before_after_clean_extra( + const char* name, + size_t namelen, + nrspecialfn_t before_callback, + nrspecialfn_t after_callback, + nrspecialfn_t clean_callback, + const char* extra); #endif extern nruserfn_t* nr_php_wrap_user_function(const char* name, size_t namelen, diff --git a/docker-compose.yaml b/docker-compose.yaml index 4626f2523..4d57753ff 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -36,11 +36,6 @@ services: ports: - "11211:11211" container_name: memcached -# mongodb: -# image: mongo:latest -# restart: always -# ports: -# - "27019:27019" postgres: image: postgres restart: always @@ -57,8 +52,6 @@ services: environment: MEMCACHE_HOST: memcached -# MONGO_HOST: mongodb - MYSQL_DB: database MYSQL_USER: admin MYSQL_PASSWD: admin @@ -86,8 +79,6 @@ services: environment: MEMCACHE_HOST: memcached -# MONGO_HOST: mongodb - MYSQL_DB: database MYSQL_USER: admin MYSQL_PASSWD: admin diff --git a/tests/include/config.php b/tests/include/config.php index 8f29f3faf..4cde7cfb2 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -24,14 +24,6 @@ function isset_or($check, $alternate = NULL) $MYSQL_SERVER = $MYSQL_HOST . ":" . $MYSQL_PORT; } -if (class_exists('MongoClient')) { - $MONGO_HOST = isset_or('MONGO_HOST', MongoClient::DEFAULT_HOST); - $MONGO_PORT = isset_or('MONGO_PORT', MongoClient::DEFAULT_PORT); -} else { - $MONGO_HOST = null; - $MONGO_PORT = null; -} - $MEMCACHE_HOST = isset_or('MEMCACHE_HOST', '127.0.0.1'); $MEMCACHE_PORT = isset_or('MEMCACHE_PORT', '11211'); diff --git a/tests/integration/mongo/mongo.inc b/tests/integration/mongo/mongo.inc deleted file mode 100644 index ddc83282f..000000000 --- a/tests/integration/mongo/mongo.inc +++ /dev/null @@ -1,14 +0,0 @@ -close(); -} catch (MongoConnectionException $e) { - die('skip: ' . $e->getMessage() . "\n"); -} diff --git a/tests/integration/mongo/test_execute.php b/tests/integration/mongo/test_execute.php deleted file mode 100644 index 245e92036..000000000 --- a/tests/integration/mongo/test_execute.php +++ /dev/null @@ -1,55 +0,0 @@ - -*/ - -/*INI -*/ - -/*EXPECT_METRICS -[ - "?? agent run id", - "?? start time", - "?? stop time", - [ - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] - ] -] -*/ - - - - -/*EXPECT_TRACED_ERRORS -null -*/ - -require_once(realpath(dirname(__FILE__ )) . '/mongo.inc'); - -$client = new MongoClient(mongo_server()); -$db = $client->selectDB('test'); -$db->execute("17.0;"); diff --git a/tests/integration/mongo/test_execute_logging_off.php b/tests/integration/mongo/test_execute_logging_off.php deleted file mode 100644 index 29661c7c7..000000000 --- a/tests/integration/mongo/test_execute_logging_off.php +++ /dev/null @@ -1,56 +0,0 @@ - -*/ - -/*INI -newrelic.application_logging.enabled = false -newrelic.application_logging.forwarding.enabled = false -newrelic.application_logging.metrics.enabled = false -*/ - -/*EXPECT_METRICS -[ - "?? agent run id", - "?? start time", - "?? stop time", - [ - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/execute", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Forwarding/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Metrics/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] - ] -] -*/ - - -/*EXPECT_TRACED_ERRORS -null -*/ - -require_once(realpath(dirname(__FILE__ )) . '/mongo.inc'); - -$client = new MongoClient(mongo_server()); -$db = $client->selectDB('test'); -$db->execute("17.0;"); diff --git a/tests/integration/mongo/test_find.php b/tests/integration/mongo/test_find.php deleted file mode 100644 index 00d023364..000000000 --- a/tests/integration/mongo/test_find.php +++ /dev/null @@ -1,71 +0,0 @@ - -*/ - -/*INI -*/ - -/*EXPECT_METRICS -[ - "?? agent run id", - "?? start time", - "?? stop time", - [ - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"DurationByCaller/Unknown/Unknown/Unknown/Unknown/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/MongoDB/allOther"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/operation/MongoDB/find"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/statement/MongoDB/test.produce/find"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Datastore/statement/MongoDB/test.produce/find", - "scope":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/all"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransaction/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"OtherTransactionTotalTime/php__FILE__"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Forwarding/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/Metrics/PHP/enabled"}, [1, "??", "??", "??", "??", "??"]], - [{"name":"Supportability/Logging/LocalDecorating/PHP/disabled"}, [1, "??", "??", "??", "??", "??"]] - ] -] -*/ - - - - -/*EXPECT_TRACED_ERRORS -null -*/ - -require_once(realpath (dirname ( __FILE__ )) . '/mongo.inc'); - -/* See http://php.net/manual/en/mongocollection.find.php */ -function test_find($db) { - $produce = new MongoCollection($db, 'produce'); - $fruitQuery = array('Type' => 'Fruit'); - $cursor = $produce->find($fruitQuery); - foreach ($cursor as $doc) { - var_dump($doc); - } -} - -function main() { - $client = new MongoClient(mongo_server()); - $db = $client->selectDB('test'); - - test_find($db); -} - -main(); From 46d9eb5beca7d40013df7792bc27c428761725eb Mon Sep 17 00:00:00 2001 From: bduranleau-nr <106178551+bduranleau-nr@users.noreply.github.com> Date: Fri, 21 Jun 2024 13:06:13 -0500 Subject: [PATCH 10/11] feat(agent): add mongodb countDocuments instrumentation (#924) --- agent/lib_mongodb.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/agent/lib_mongodb.c b/agent/lib_mongodb.c index 9b5ca9c99..ad1786373 100644 --- a/agent/lib_mongodb.c +++ b/agent/lib_mongodb.c @@ -277,6 +277,11 @@ void nr_mongodb_enable() { nr_mongodb_operation_before, nr_mongodb_operation_after, nr_mongodb_operation_after, "count"); + nr_php_wrap_user_function_before_after_clean_extra( + NR_PSTR("MongoDB\\Operation\\CountDocuments::execute"), + nr_mongodb_operation_before, nr_mongodb_operation_after, + nr_mongodb_operation_after, "countDocuments"); + nr_php_wrap_user_function_before_after_clean_extra( NR_PSTR("MongoDB\\Operation\\CreateIndexes::execute"), nr_mongodb_operation_before, nr_mongodb_operation_after, From 5154931a3245cd4161eb5c924a52a253a4d21960 Mon Sep 17 00:00:00 2001 From: Amber Sistla Date: Fri, 21 Jun 2024 11:31:52 -0700 Subject: [PATCH 11/11] fix(agent): NULL check before accessing segment wraprec (#923) --- agent/fw_drupal.c | 4 +++- agent/lib_predis.c | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/agent/fw_drupal.c b/agent/fw_drupal.c index 67efc1dab..12e43b5cf 100644 --- a/agent/fw_drupal.c +++ b/agent/fw_drupal.c @@ -265,7 +265,9 @@ NR_PHP_WRAPPER(nr_drupal_http_request_before) { * fcall_end is able to properly dispatch to the after wrapper, as * this new segment is now at the top of the segment stack. */ - NRPRG(drupal_http_request_segment)->wraprec = auto_segment->wraprec; + if (NULL != NRPRG(drupal_http_request_segment)) { + NRPRG(drupal_http_request_segment)->wraprec = auto_segment->wraprec; + } } } NR_PHP_WRAPPER_END diff --git a/agent/lib_predis.c b/agent/lib_predis.c index 862649d3b..b299c5595 100644 --- a/agent/lib_predis.c +++ b/agent/lib_predis.c @@ -764,7 +764,9 @@ NR_PHP_WRAPPER(nr_predis_webdisconnection_executeCommand_before) { nr_segment_t* segment = NULL; segment = nr_segment_start(NRPRG(txn), NULL, NULL); - segment->wraprec = auto_segment->wraprec; + if (NULL != segment) { + segment->wraprec = auto_segment->wraprec; + } } NR_PHP_WRAPPER_END @@ -894,5 +896,4 @@ void nr_predis_enable(TSRMLS_D) { NR_PSTR("Predis\\Connection\\WebdisConnection::executeCommand"), nr_predis_webdisconnection_executeCommand TSRMLS_CC); #endif /* OAPI */ - }