Skip to content

Commit f30642f

Browse files
authored
Merge pull request #978 from newrelic/dev
Release 11.3
2 parents 2321b9e + dc162a4 commit f30642f

31 files changed

+781
-162
lines changed

VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
11.2.0
1+
11.3.0

agent/Makefile.frag

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ TEST_BINARIES = \
9393
tests/test_internal_instrument \
9494
tests/test_hash \
9595
tests/test_lib_aws_sdk_php \
96+
tests/test_memcached \
9697
tests/test_mongodb \
9798
tests/test_monolog \
9899
tests/test_mysql \

agent/config.m4

+1-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ if test "$PHP_NEWRELIC" = "yes"; then
215215
php_error.c php_execute.c php_explain.c php_explain_mysqli.c \
216216
php_explain_pdo_mysql.c php_extension.c php_file_get_contents.c \
217217
php_globals.c php_hash.c php_header.c php_httprequest_send.c \
218-
php_internal_instrument.c php_minit.c php_mshutdown.c php_mysql.c \
218+
php_internal_instrument.c php_memcached.c php_minit.c php_mshutdown.c php_mysql.c \
219219
php_mysqli.c php_newrelic.c php_nrini.c php_observer.c php_output.c php_pdo.c \
220220
php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c \
221221
php_rinit.c php_rshutdown.c php_samplers.c php_stack.c \

agent/newrelic-install.sh

+19
Original file line numberDiff line numberDiff line change
@@ -1371,6 +1371,25 @@ EOF
13711371
if [ -d "${cfg_pfx}/fpm/conf.d" ]; then
13721372
pi_inidir_dso="${cfg_pfx}/fpm/conf.d"
13731373
fi
1374+
1375+
#
1376+
# Debian can use a mods-available directory to store the ini files.
1377+
# It creates a symlink from the ini file in the conf.d directory that
1378+
# our installer can fail to find (because the symlink is prefixed with
1379+
# "20-" (notably the number can change based on configurations).
1380+
# While this install script will not install into the mods-available
1381+
# directory, our .deb installer can. Therefore, we want to detect if
1382+
# newrelic has previously been installed in the mods-available directory
1383+
# so that we do not create an additional ini file -- which would result in
1384+
# the conf.d directory having both newrelic.ini and 20-newrelic.ini.
1385+
#
1386+
1387+
if [ -d "${cfg_pfx}/mods-available" -a -f "${cfg_pfx}/mods-available/newrelic.ini" ]; then
1388+
pi_inidir_cli="${cfg_pfx}/mods-available"
1389+
if [ -n "${pi_inidir_dso}" ]; then
1390+
pi_inidir_dso="${cfg_pfx}/mods-available"
1391+
fi
1392+
fi
13741393
fi
13751394
done
13761395

agent/php_execute.c

+12-70
Original file line numberDiff line numberDiff line change
@@ -523,59 +523,7 @@ static nr_library_table_t libraries[] = {
523523
*/
524524
{"Laminas_Http", NR_PSTR("laminas-http/src/client.php"), nr_laminas_http_enable},
525525

526-
/*
527-
* Other frameworks, detected only, but not specifically
528-
* instrumented. We detect these as libraries so that we don't prevent
529-
* detection of a supported framework or library later (since a transaction
530-
* can only have one framework).
531-
*/
532-
{"Aura1", NR_PSTR("aura/framework/system.php"), NULL},
533-
{"Aura2", NR_PSTR("aura/di/src/containerinterface.php"), NULL},
534-
{"Aura3", NR_PSTR("aura/di/src/containerconfiginterface.php"), NULL},
535526
{"CakePHP3", NR_PSTR("cakephp/src/core/functions.php"), NULL},
536-
{"Fuel", NR_PSTR("fuel/core/classes/fuel.php"), NULL},
537-
{"Lithium", NR_PSTR("lithium/core/libraries.php"), NULL},
538-
{"Phpbb", NR_PSTR("phpbb/request/request.php"), NULL},
539-
{"Phpixie2", NR_PSTR("phpixie/core/classes/phpixie/pixie.php"), NULL},
540-
{"Phpixie3", NR_PSTR("phpixie/framework.php"), NULL},
541-
{"React", NR_PSTR("react/event-loop/src/loopinterface.php"), NULL},
542-
{"SilverStripe", NR_PSTR("injector/silverstripeinjectioncreator.php"), NULL},
543-
{"SilverStripe4", NR_PSTR("silverstripeserviceconfigurationlocator.php"), NULL},
544-
{"Typo3", NR_PSTR("classes/typo3/flow/core/bootstrap.php"), NULL},
545-
{"Typo3", NR_PSTR("typo3/sysext/core/classes/core/bootstrap.php"), NULL},
546-
547-
/*
548-
* Other CMS (content management systems), detected only, but
549-
* not specifically instrumented.
550-
*/
551-
{"Moodle", NR_PSTR("moodlelib.php"), NULL},
552-
/*
553-
* It is likely that this will never be found, since the CodeIgniter.php
554-
* will get loaded first, and as such mark this transaction as belonging to
555-
* CodeIgniter, and not Expession Engine.
556-
*/
557-
{"ExpressionEngine", NR_PSTR("system/expressionengine/config/config.php"), NULL},
558-
/*
559-
* ExpressionEngine 5, however, has a very obvious file we can look for.
560-
*/
561-
{"ExpressionEngine5", NR_PSTR("expressionengine/boot/boot.php"), NULL},
562-
/*
563-
* DokuWiki uses doku.php as an entry point, but has other files that are
564-
* loaded directly that this won't pick up. That's probably OK for
565-
* supportability metrics, but we'll add the most common name for the
566-
* configuration file as well just in case.
567-
*/
568-
{"DokuWiki", NR_PSTR("doku.php"), NULL},
569-
{"DokuWiki", NR_PSTR("conf/dokuwiki.php"), NULL},
570-
571-
/*
572-
* SugarCRM no longer has a community edition, so this likely only works
573-
* with older versions.
574-
*/
575-
{"SugarCRM", NR_PSTR("sugarobjects/sugarconfig.php"), NULL},
576-
577-
{"Xoops", NR_PSTR("class/xoopsload.php"), NULL},
578-
{"E107", NR_PSTR("e107_handlers/e107_class.php"), NULL},
579527
};
580528
// clang-format: on
581529

@@ -590,9 +538,6 @@ static nr_library_table_t logging_frameworks[] = {
590538
/* laminas-log - Logging for PHP */
591539
{"laminas-log", NR_PSTR("laminas-log/src/logger.php"), NULL},
592540
/* cakephp-log - Logging for PHP */
593-
{"cakephp-log", NR_PSTR("cakephp/log/log.php"), NULL},
594-
/* Analog - Logging for PHP */
595-
{"Analog", NR_PSTR("analog/analog.php"), NULL},
596541
};
597542
// clang-format: on
598543

@@ -603,14 +548,17 @@ static size_t num_logging_frameworks
603548
typedef struct _nr_vuln_mgmt_table_t {
604549
const char* package_name;
605550
const char* file_to_check;
551+
size_t file_to_check_len;
606552
nr_vuln_mgmt_enable_fn_t enable;
607553
} nr_vuln_mgmt_table_t;
608554

609555
/* Note that all paths should be in lowercase. */
556+
// clang-format: off
610557
static const nr_vuln_mgmt_table_t vuln_mgmt_packages[] = {
611-
{"Drupal", "drupal/component/dependencyinjection/container.php", nr_drupal_version},
612-
{"Wordpress", "wp-includes/version.php", nr_wordpress_version},
558+
{"Drupal", NR_PSTR("drupal/component/dependencyinjection/container.php"), nr_drupal_version},
559+
{"Wordpress", NR_PSTR("wp-includes/version.php"), nr_wordpress_version},
613560
};
561+
// clang-format: on
614562

615563
static const size_t num_packages
616564
= sizeof(vuln_mgmt_packages) / sizeof(nr_vuln_mgmt_table_t);
@@ -990,28 +938,22 @@ static void nr_execute_handle_logging_framework(const char* filename,
990938
}
991939
}
992940

993-
#undef STR_AND_LEN
994-
995-
static void nr_execute_handle_package(const char* filename) {
996-
if (NULL == filename || 0 >= nr_strlen(filename)) {
997-
nrl_verbosedebug(NRL_FRAMEWORK, "%s: The file name is NULL",
998-
__func__);
999-
return;
1000-
}
1001-
char* filename_lower = nr_string_to_lowercase(filename);
941+
static void nr_execute_handle_package(const char* filename,
942+
const size_t filename_len) {
1002943
size_t i = 0;
1003944

1004945
for (i = 0; i < num_packages; i++) {
1005-
if (nr_stridx(filename_lower, vuln_mgmt_packages[i].file_to_check) >= 0) {
946+
if (nr_striendswith(STR_AND_LEN(filename),
947+
STR_AND_LEN(vuln_mgmt_packages[i].file_to_check))) {
1006948
if (NULL != vuln_mgmt_packages[i].enable) {
1007949
vuln_mgmt_packages[i].enable();
1008950
}
1009951
}
1010952
}
1011-
1012-
nr_free(filename_lower);
1013953
}
1014954

955+
#undef STR_AND_LEN
956+
1015957
/*
1016958
* Purpose : Detect library and framework usage from a PHP file.
1017959
*
@@ -1036,7 +978,7 @@ static void nr_php_user_instrumentation_from_file(const char* filename,
1036978
nr_execute_handle_autoload(filename, filename_len);
1037979
nr_execute_handle_logging_framework(filename, filename_len TSRMLS_CC);
1038980
if (NRINI(vulnerability_management_package_detection_enabled)) {
1039-
nr_execute_handle_package(filename);
981+
nr_execute_handle_package(filename, filename_len);
1040982
}
1041983
}
1042984

agent/php_internal_instrument.c

+59
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#include "php_explain_mysqli.h"
1414
#include "php_file_get_contents.h"
1515
#include "php_globals.h"
16+
#include "php_hash.h"
1617
#include "php_httprequest_send.h"
1718
#include "php_internal_instrument.h"
19+
#include "php_memcached.h"
1820
#include "php_mysql.h"
1921
#include "php_mysqli.h"
2022
#include "php_pdo.h"
@@ -1531,6 +1533,57 @@ NR_INNER_WRAPPER(memcache_function) {
15311533
INTERNAL_FUNCTION_PARAM_PASSTHRU);
15321534
}
15331535

1536+
NR_INNER_WRAPPER(memcached_add_server) {
1537+
char* host = NULL;
1538+
nr_string_len_t host_len = 0;
1539+
zend_long port = 0;
1540+
zend_long weight = 0;
1541+
int zcaught = 0;
1542+
1543+
if (SUCCESS
1544+
== zend_parse_parameters_ex(
1545+
ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "s|ll", &host,
1546+
&host_len, &port, &weight) &&
1547+
NULL != host) {
1548+
nr_php_memcached_create_instance_metric(host, port);
1549+
}
1550+
zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler,
1551+
INTERNAL_FUNCTION_PARAM_PASSTHRU);
1552+
if (zcaught) {
1553+
zend_bailout();
1554+
/* NOTREACHED */
1555+
}
1556+
}
1557+
1558+
NR_INNER_WRAPPER(memcached_add_servers) {
1559+
zval* servers = NULL;
1560+
zval* server = NULL;
1561+
int zcaught = 0;
1562+
1563+
if (SUCCESS
1564+
== zend_parse_parameters_ex(
1565+
ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS(), "a", &servers)) {
1566+
if (NULL != servers && Z_TYPE_P(servers) == IS_ARRAY) {
1567+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(servers), server) {
1568+
zval* host = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 0);
1569+
zval* port = nr_php_zend_hash_index_find(Z_ARRVAL_P(server), 1);
1570+
if (nr_php_is_zval_valid_string(host) &&
1571+
nr_php_is_zval_valid_integer(port)) {
1572+
nr_php_memcached_create_instance_metric(Z_STRVAL_P(host), Z_LVAL_P(port));
1573+
}
1574+
}
1575+
ZEND_HASH_FOREACH_END();
1576+
}
1577+
}
1578+
zcaught = nr_zend_call_old_handler(nr_wrapper->oldhandler,
1579+
INTERNAL_FUNCTION_PARAM_PASSTHRU);
1580+
1581+
if (zcaught) {
1582+
zend_bailout();
1583+
/* NOTREACHED */
1584+
}
1585+
}
1586+
15341587
/*
15351588
* Handle
15361589
* bool redis::connect ( string $host[, int $port = 6379 ... ] )
@@ -3098,6 +3151,8 @@ NR_OUTER_WRAPPER(memcached_set)
30983151
NR_OUTER_WRAPPER(memcached_setbykey)
30993152
NR_OUTER_WRAPPER(memcached_setmulti)
31003153
NR_OUTER_WRAPPER(memcached_setmultibykey)
3154+
NR_OUTER_WRAPPER(memcached_addserver)
3155+
NR_OUTER_WRAPPER(memcached_addservers)
31013156

31023157
NR_OUTER_WRAPPER(redis_append)
31033158
NR_OUTER_WRAPPER(redis_bitcount)
@@ -3511,6 +3566,10 @@ void nr_php_generate_internal_wrap_records(void) {
35113566
memcache_function, 0, "set")
35123567
NR_INTERNAL_WRAPREC("memcached::setmultibykey", memcached_setmultibykey,
35133568
memcache_function, 0, "set")
3569+
NR_INTERNAL_WRAPREC("memcached::addserver", memcached_addserver,
3570+
memcached_add_server, 0, 0);
3571+
NR_INTERNAL_WRAPREC("memcached::addservers", memcached_addservers,
3572+
memcached_add_servers, 0, 0);
35143573

35153574
NR_INTERNAL_WRAPREC("redis::connect", redis_connect, redis_connect, 0,
35163575
"connect")

agent/php_memcached.c

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#include "php_memcached.h"
7+
#include "nr_datastore_instance.h"
8+
#include "php_agent.h"
9+
10+
nr_datastore_instance_t* nr_php_memcached_create_datastore_instance(
11+
const char* host_or_socket,
12+
zend_long port) {
13+
nr_datastore_instance_t* instance = NULL;
14+
if (port == 0) { // local socket
15+
instance = nr_datastore_instance_create("localhost", host_or_socket, NULL);
16+
} else {
17+
char* port_str = nr_formatf("%ld", (long)port);
18+
instance = nr_datastore_instance_create(host_or_socket, port_str, NULL);
19+
nr_free(port_str);
20+
}
21+
return instance;
22+
}
23+
24+
void nr_php_memcached_create_instance_metric(
25+
const char* host_or_socket,
26+
zend_long port) {
27+
nr_datastore_instance_t* instance
28+
= nr_php_memcached_create_datastore_instance(host_or_socket, port);
29+
char* instance_metric = nr_formatf("Datastore/instance/Memcached/%s/%s",
30+
instance->host, instance->port_path_or_id);
31+
nrm_force_add(NRPRG(txn)->unscoped_metrics, instance_metric, 0);
32+
nr_datastore_instance_destroy(&instance);
33+
nr_free(instance_metric);
34+
}

agent/php_memcached.h

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright 2020 New Relic Corporation. All rights reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef PHP_MEMCACHED_HDR
7+
#define PHP_MEMCACHED_HDR
8+
9+
#include "nr_datastore_instance.h"
10+
#include "php_includes.h"
11+
12+
/*
13+
* Purpose : Create a datastore instance metadata for a Memcached server.
14+
*
15+
* Params : 1. The memcached host or socket name as given to Memcached::addServer().
16+
* 2. The memcached port as given as given to Memcached::addServer().
17+
*
18+
* Returns: nr_datastore_instance_t* that the caller is responsible for freeing
19+
*/
20+
nr_datastore_instance_t* nr_php_memcached_create_datastore_instance(
21+
const char* host_or_socket,
22+
zend_long port);
23+
24+
/*
25+
* Purpose : Create a memcached instance metric
26+
*
27+
* Params : 1. The memcached host or socket name as given to Memcached::addServer().
28+
* 2. The memcached port as given as given to Memcached::addServer().
29+
*/
30+
extern void nr_php_memcached_create_instance_metric(
31+
const char* host_or_socket,
32+
zend_long port);
33+
34+
35+
#endif

0 commit comments

Comments
 (0)