diff --git a/agent/php_globals.c b/agent/php_globals.c index 5bb372d33..ee91efda1 100644 --- a/agent/php_globals.c +++ b/agent/php_globals.c @@ -42,6 +42,7 @@ static void nr_php_per_process_globals_dispose(void) { nr_free(nr_php_per_process_globals.env_labels); nr_free(nr_php_per_process_globals.apache_add); nr_free(nr_php_per_process_globals.docker_id); + nr_free(nr_php_per_process_globals.agent_control_health_location); nr_memset(&nr_php_per_process_globals, 0, sizeof(nr_php_per_process_globals)); } diff --git a/agent/php_globals.h b/agent/php_globals.h index c73b93e43..1b159d8c8 100644 --- a/agent/php_globals.h +++ b/agent/php_globals.h @@ -85,6 +85,11 @@ typedef struct _nrphpglobals_t { /* Original PHP SAPI header callback */ nrphphdrfn_t orig_header_handler; + /* NR Control configuration settings */ + int agent_control_enabled; + char* agent_control_health_location; + nrtime_t agent_control_frequency; + struct { uint8_t no_sql_parsing; uint8_t show_sql_parsing; diff --git a/agent/php_minit.c b/agent/php_minit.c index 7cf4f3e8e..5d4045c64 100644 --- a/agent/php_minit.c +++ b/agent/php_minit.c @@ -29,6 +29,7 @@ #include "nr_app.h" #include "nr_banner.h" #include "nr_daemon_spawn.h" +#include "util_health.h" #include "util_logging.h" #include "util_memory.h" #include "util_signals.h" @@ -319,6 +320,25 @@ static void nr_php_check_high_security_log_forwarding(TSRMLS_D) { } } +/* + * Check the INI values for 'agent_control_enabled' and + * `agent_control_health_location`, log a warning and set agent_control_enabled + * to 'false' on an invalid configuration state . + */ +static void nr_php_check_agent_control_health_file() { + if (NR_PHP_PROCESS_GLOBALS(agent_control_enabled) + && nr_strempty(NR_PHP_PROCESS_GLOBALS(agent_control_health_location))) { + nrl_warning(NRL_INIT, + "NR Control support will be DISABLED because a valid health " + "file location is not set. Please check " + "newrelic.agent_control.health.delivery_location in the agent " + "configuration file or the " + "NEW_RELIC_AGENT_CONTROL_HEALTH_DELIVERY_LOCATION environment " + "variable."); + NR_PHP_PROCESS_GLOBALS(agent_control_enabled) = 0; + } +} + static char* nr_php_get_agent_specific_info(void) { const char* php_version; const char* zend_type; @@ -651,6 +671,8 @@ PHP_MINIT_FUNCTION(newrelic) { nr_php_check_logging_config(TSRMLS_C); nr_php_check_high_security_log_forwarding(TSRMLS_C); + nr_php_check_agent_control_health_file(); + /* * Save the original PHP hooks and then apply our own hooks. The agent is * almost fully operational now. The last remaining initialization that @@ -719,6 +741,10 @@ PHP_MINIT_FUNCTION(newrelic) { nr_wordpress_minit(); nr_php_set_opcode_handlers(); + if (NR_PHP_PROCESS_GLOBALS(agent_control_enabled)) { + nrh_set_start_time(); + } + nrl_debug(NRL_INIT, "MINIT processing done"); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO /* PHP 7.4+ */ NR_PHP_PROCESS_GLOBALS(zend_offset) = zend_get_resource_handle(dummy); diff --git a/agent/php_nrini.c b/agent/php_nrini.c index dce25a27a..4948f9393 100644 --- a/agent/php_nrini.c +++ b/agent/php_nrini.c @@ -15,6 +15,7 @@ #include "nr_version.h" #include "nr_log_level.h" #include "util_buffer.h" +#include "util_health.h" #include "util_json.h" #include "util_logging.h" #include "util_memory.h" @@ -515,6 +516,89 @@ static PHP_INI_MH(nr_high_security_mh) { return SUCCESS; } +static PHP_INI_MH(nr_agent_control_enabled_mh) { + int val; + + (void)entry; + (void)NEW_VALUE_LEN; + (void)mh_arg1; + (void)mh_arg2; + (void)mh_arg3; + (void)stage; + NR_UNUSED_TSRMLS; + + val = nr_bool_from_str(NEW_VALUE); + + if (-1 == val) { + return FAILURE; + } + + if (val) { + NR_PHP_PROCESS_GLOBALS(agent_control_enabled) = 1; + } else { + NR_PHP_PROCESS_GLOBALS(agent_control_enabled) = 0; + } + + return SUCCESS; +} + +static PHP_INI_MH(nr_agent_control_location_mh) { + (void)entry; + (void)mh_arg1; + (void)mh_arg2; + (void)mh_arg3; + (void)stage; + NR_UNUSED_TSRMLS; + + nr_free(NR_PHP_PROCESS_GLOBALS(agent_control_health_location)); + + if (0 == NEW_VALUE_LEN) { + return SUCCESS; + } + + if (7 >= NEW_VALUE_LEN) { + // missing or malformed 'file://' prefix + return FAILURE; + } + + NR_PHP_PROCESS_GLOBALS(agent_control_health_location) + = nrh_get_health_location(NEW_VALUE); + + if (NULL == NR_PHP_PROCESS_GLOBALS(agent_control_health_location)) { + nrl_warning(NRL_INIT, + "failed to set health file location for provided uri '%s'", + NEW_VALUE); + return FAILURE; + } + + return SUCCESS; +} + +static PHP_INI_MH(nr_agent_control_frequency_mh) { + nrtime_t val; + + (void)entry; + (void)mh_arg1; + (void)mh_arg2; + (void)mh_arg3; + (void)stage; + NR_UNUSED_TSRMLS; + + if (0 != NEW_VALUE_LEN) { + val = nr_parse_time_from_config(NEW_VALUE); + if (val < 1) { + val = 1; + } else if (val > 300) { + val = 300; + } + NR_PHP_PROCESS_GLOBALS(agent_control_frequency) = val; + } else { + NR_PHP_PROCESS_GLOBALS(agent_control_frequency) = 5; + } + + return SUCCESS; +} + static PHP_INI_MH(nr_preload_framework_library_detection_mh) { int val; @@ -2169,6 +2253,25 @@ PHP_INI_ENTRY_EX("newrelic.daemon.utilization.detect_kubernetes", NR_PHP_SYSTEM, NR_PHP_UTILIZATION_MH_NAME(kubernetes), nr_enabled_disabled_dh) + +PHP_INI_ENTRY_EX("newrelic.agent_control.enabled", + "0", + NR_PHP_SYSTEM, + nr_agent_control_enabled_mh, + 0) + +PHP_INI_ENTRY_EX("newrelic.agent_control.health.delivery_location", + "file:///newrelic/apm/health", + NR_PHP_SYSTEM, + nr_agent_control_location_mh, + 0) + +PHP_INI_ENTRY_EX("newrelic.agent_control.health.frequency", + "5s", + NR_PHP_SYSTEM, + nr_agent_control_frequency_mh, + 0) + /* * This daemon flag is for internal development use only. It should not be * documented to customers. diff --git a/agent/scripts/newrelic.ini.template b/agent/scripts/newrelic.ini.template index aeaf75641..d57f45c3c 100644 --- a/agent/scripts/newrelic.ini.template +++ b/agent/scripts/newrelic.ini.template @@ -1393,3 +1393,28 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log" ; requires that this value when the account ID is not part of the function name. ; ;newrelic.cloud.aws.account_id = "" + +; Setting: newrelic.agent_control.enabled +; Type : boolean +; Scope : per-directory +; Default: false +; Info : Boolean value that denotes if agent control functionality should be enabled. +; +;newrelic.agent_control.enabled = false + +; Setting: newrelic.agent_control.health.delivery_location +; Type : string +; Scope : per-directory +; Default: file:///newrelic/apm/health +; Info : String URI that specifies the fully qualified directory path for the +; health file(s) to be written to. +; +;newrelic.agent_control.health.delivery_location = "file:///newrelic/apm/health" + +; Setting: newrelic.agent_control.health.frequency +; Type : time specification string (in seconds, only) +; Scope : per-directory +; Info : Integer that specifies the interval, in seconds, of how often the health +; file will be written. +; +;newrelic.agent_control.health.frequency = 5s diff --git a/axiom/Makefile b/axiom/Makefile index b8c605a5b..bb0b46348 100644 --- a/axiom/Makefile +++ b/axiom/Makefile @@ -124,6 +124,7 @@ OBJS := \ nr_span_queue.o \ nr_synthetics.o \ nr_txn.o \ + nr_uuid.o \ nr_version.o \ nr_php_packages.o \ util_apdex.o \ @@ -136,6 +137,7 @@ OBJS := \ util_hashmap.o \ util_json.o \ util_logging.o \ + util_health.o \ util_labels.o \ util_matcher.o \ util_md5.o \ diff --git a/axiom/nr_uuid.c b/axiom/nr_uuid.c new file mode 100644 index 000000000..e4d4fb352 --- /dev/null +++ b/axiom/nr_uuid.c @@ -0,0 +1,29 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "nr_uuid.h" +#include "util_memory.h" + +static const char hex_values[16] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; +char* nr_uuid_create(int seed) { + char* uuid = nr_zalloc(NR_UUID_SIZE + 1); + if (0 < seed) { + srand(seed); + } else { + srand(time(NULL)); + } + + for (int i = 0; i < NR_UUID_SIZE; i++) { + int r = rand() % NR_UUID_RANGE; + + uuid[i] = hex_values[r]; + } + + return uuid; +} diff --git a/axiom/nr_uuid.h b/axiom/nr_uuid.h new file mode 100644 index 000000000..0cb5aa029 --- /dev/null +++ b/axiom/nr_uuid.h @@ -0,0 +1,28 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file contains functions for simple uuid generation. + */ +#ifndef NR_UUID_HDR +#define NR_UUID_HDR + +#define NR_UUID_SIZE 32 +#define NR_UUID_RANGE 16 + +/** + * nr_uuid_create: pseudo-implementation of uuid generation logic + * + * This function will simply return a randomly generated 32 character hex + * string. It does not implement the spec for UUID generation, which requires + * specific adherence to implementation details and setting bits within the UUID + * to signify which UUID generation variant was used. + * + * Params: int seed (<1 for time based seed) + * + * Returns: 32 byte hex string (must be freed by caller) + */ +char* nr_uuid_create(int seed); +#endif diff --git a/axiom/tests/.gitignore b/axiom/tests/.gitignore index fa0332187..df929221f 100644 --- a/axiom/tests/.gitignore +++ b/axiom/tests/.gitignore @@ -35,6 +35,9 @@ clang_header.h # vi/ex scripts *.ex +# test file generation +health-*.yml + # Test binaries test_agent test_analytics_events @@ -63,6 +66,7 @@ test_guid test_hash test_hashmap test_header +test_health test_helgrind test_json test_labels @@ -121,4 +125,5 @@ test_threads test_time test_txn test_url +test_uuid test_vector diff --git a/axiom/tests/Makefile b/axiom/tests/Makefile index 3d67984ca..719656f3a 100644 --- a/axiom/tests/Makefile +++ b/axiom/tests/Makefile @@ -146,6 +146,7 @@ TESTS := \ test_hash \ test_hashmap \ test_header \ + test_health \ test_json \ test_labels \ test_log_event \ @@ -198,6 +199,7 @@ TESTS := \ test_time \ test_txn \ test_url \ + test_uuid \ test_vector # diff --git a/axiom/tests/test_health.c b/axiom/tests/test_health.c new file mode 100644 index 000000000..6c4cf4841 --- /dev/null +++ b/axiom/tests/test_health.c @@ -0,0 +1,117 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nr_axiom.h" + +#include +#include +#include +#include + +#include "util_health.h" +#include "util_memory.h" + +#include "tlib_main.h" +#include "util_strings.h" +#include "util_syscalls.h" + +static void test_health(void) { + nr_status_t rv; + char* location = NULL; + char* rand_uuid = NULL; + char* rand_healthfile = NULL; + + nrh_set_start_time(); + + // ensure clean environment + nr_unlink("health-bc21b5891f5e44fc9272caef924611a8.yml"); + nr_unlink("health-ffffffffffffffffffffffffffffffff.yml"); + + // invalid location + location = nrh_get_health_location("/should/not/exist"); + tlib_pass_if_true("initialization to bad path fails", NULL == location, + "location=%s", NULL == location ? "NULL" : location); + nr_free(location); + + // invalid uuid + rv = nrh_set_uuid("bc21b5891f5e44fc9272caef924611a"); + tlib_pass_if_true("set uuid with invalid length uuid fails", NR_FAILURE == rv, + "rv=%d", (int)rv); + + // valid location + location = nrh_get_health_location("file://./"); + tlib_pass_if_true("initialization to good path succeeds", NULL != location, + "location=%s", NULL == location ? "NULL" : location); + + // valid status + rv = nrh_set_last_error(NRH_INVALID_LICENSE); + + // write default uuid + valid location + valid status + rv = nrh_write_health(location); + tlib_pass_if_true("health file write succeeds", NR_SUCCESS == rv, "rv=%d", + (int)rv); + tlib_pass_if_exists("./health-bc21b5891f5e44fc9272caef924611a8.yml"); + + // update to a new uuid + rv = nrh_set_uuid("ffffffffffffffffffffffffffffffff"); + tlib_pass_if_true("set uuid succeeds", NR_SUCCESS == rv, "rv=%d", (int)rv); + + // update to a new valid status + rv = nrh_set_last_error(NRH_MISSING_LICENSE); + + // write new file (uuid) + same location + new status + rv = nrh_write_health(location); + tlib_pass_if_true("health file write succeeds", NR_SUCCESS == rv, "rv=%d", + (int)rv); + tlib_pass_if_exists("./health-ffffffffffffffffffffffffffffffff.yml"); + + // update to new valid status + rv = nrh_set_last_error(NRH_MISSING_APPNAME); + + // update existing file with new status + rv = nrh_write_health(location); + tlib_pass_if_true("write_health succeeds", NR_SUCCESS == rv, "rv=%d", + (int)rv); + + // update new random uuid + rv = nrh_set_uuid(NULL); + tlib_pass_if_true("set random uuid succeeds", NR_SUCCESS == rv, "rv=%d", + (int)rv); + + // verify new random uuid != previous + rand_uuid = nrh_get_uuid(); + tlib_pass_if_not_null("get uuid succeeds", rand_uuid); + tlib_pass_if_true( + "manual uuid successfully replaced by random uuid", + 0 != nr_strcmp("ffffffffffffffffffffffffffffffff", rand_uuid), "rand=%s", + rand_uuid); + + // update to valid status + rv = nrh_set_last_error(NRH_CONNECTION_FAILED); + + // write new file (random uuid) + existing location + new status + rv = nrh_write_health(location); + + // test get_health_filename functionality + rand_healthfile = nrh_get_health_filename(); + tlib_pass_if_not_null("get health filename succeeds", rand_healthfile); + tlib_pass_if_exists(rand_healthfile); + + // clean up + nr_unlink("health-bc21b5891f5e44fc9272caef924611a8.yml"); + nr_unlink("health-ffffffffffffffffffffffffffffffff.yml"); + nr_unlink(rand_healthfile); + + nr_free(location); + nr_free(rand_uuid); + nr_free(rand_healthfile); +} + +tlib_parallel_info_t parallel_info + = {.suggested_nthreads = -1, .state_size = 0}; + +void test_main(void* p NRUNUSED) { + test_health(); +} diff --git a/axiom/tests/test_uuid.c b/axiom/tests/test_uuid.c new file mode 100644 index 000000000..38c9cc4b9 --- /dev/null +++ b/axiom/tests/test_uuid.c @@ -0,0 +1,46 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nr_axiom.h" + +#include "nr_uuid.h" +#include "util_memory.h" + +#include "tlib_main.h" +#include "util_sleep.h" + +static void test_uuid_create(void) { + char* uuid = NULL; + char* tmp = NULL; + + uuid = nr_uuid_create(1234); + tlib_pass_if_not_null("uuid create success", uuid); + + tmp = nr_strdup(uuid); + nr_free(uuid); + + uuid = nr_uuid_create(4321); + tlib_pass_if_true("new uuid != old uuid", 0 != nr_strcmp(tmp, uuid), + "old=%s, new=%s", tmp, uuid); + + nr_free(tmp); + + tmp = nr_strdup(uuid); + + nr_free(uuid); + + uuid = nr_uuid_create(0); + tlib_pass_if_true("rand uuid != old uuid", 0 != nr_strcmp(tmp, uuid), + "old=%s, new=%s", tmp, uuid); + + nr_free(uuid); + nr_free(tmp); +} + +tlib_parallel_info_t parallel_info = {.suggested_nthreads = 2, .state_size = 0}; + +void test_main(void* p NRUNUSED) { + test_uuid_create(); +} diff --git a/axiom/util_health.c b/axiom/util_health.c new file mode 100644 index 000000000..ea873c68b --- /dev/null +++ b/axiom/util_health.c @@ -0,0 +1,223 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "nr_axiom.h" + +#include + +#include +#include +#include +#include + +#include "util_health.h" +#include "nr_uuid.h" +#include "util_memory.h" +#include "util_strings.h" +#include "util_syscalls.h" +#include "util_logging.h" + +#define BILLION (1000000000L) +#define UUID_LEN 32 // 128 bits, 32 hex characters + +typedef struct _nrh_status_codes_t { + const char* code; + const char* description; +} nrh_status_codes_t; + +// clang-format off +static nrh_status_codes_t health_statuses[NRH_MAX_STATUS] = { + {"NR-APM-000", "Healthy"}, + {"NR-APM-001", "Invalid license key"}, + {"NR-APM-002", "License Key missing in configuration"}, + {"NR-APM-003", "Forced disconnect received from New Relic"}, + {"NR-APM-004", "HTTP error response code [%s] received from New Relic while sending data type [%s]"}, + {"NR-APM-005", "Missing application name in agent configuration"}, + {"NR-APM-006", "The maximum number of configured app names (3) exceeded"}, + {"NR-APM-007", "HTTP Proxy configuration error, response code [%s]"}, + {"NR-APM-008", "Agent is disabled via configuration"}, + {"NR-APM-009", "Failed to connect to New Relic data collector"}, + {"NR-APM-010", "Agent config file is not able to be parsed"}, + {"NR-APM-099", "Agent has shutdown"} +}; +// clang-format on + +static struct timespec start_time = {0, 0}; +static nrhealth_t last_error_code = NRH_HEALTHY; +static char health_uuid[] = "bc21b5891f5e44fc9272caef924611a8"; + +nr_status_t nrh_set_uuid(char* uuid) { + char* rand_uuid = NULL; + if (NULL == uuid) { + // no uuid supplied, auto-generate instead + rand_uuid = nr_uuid_create(-1); + uuid = rand_uuid; + } + + if (UUID_LEN != nr_strlen(uuid)) { + return NR_FAILURE; + } + + nr_strlcpy(&health_uuid[0], uuid, UUID_LEN + 1); + + nr_free(rand_uuid); + return NR_SUCCESS; +} + +char* nrh_get_uuid(void) { + if (UUID_LEN != nr_strlen(health_uuid)) { + // handle edge case that uuid is not currently set + // or is set improperly + return NULL; + } + + return nr_strdup(health_uuid); +} + +char* nrh_strip_scheme_prefix(char* uri) { + char* filedir = NULL; + int prefix_len = nr_strlen("file://"); + int uri_len = nr_strlen(uri); + + if (uri_len <= prefix_len) { + // uri must contain more than just the scheme information. + nrl_warning(NRL_AGENT, "%s: invalid uri %s", __func__, uri); + return NULL; + } + + if (!nr_strstr(uri, "file://")) { + // missing uri scheme, undefined behavior. treat as error. + nrl_warning(NRL_AGENT, "%s: invalid uri %s", __func__, uri); + return NULL; + } + + // allocate space for stripped string + null terminator. + filedir = (char*)nr_malloc(uri_len - prefix_len + 1); + + // copy string starting at uri path offset. + nr_strcpy(filedir, uri + prefix_len); + + return filedir; +} + +char* nrh_get_health_filename(void) { + return nr_formatf("health-%s.yml", health_uuid); +} + +char* nrh_get_health_location(char* uri) { + char* filedir = NULL; + struct stat statbuf; + + if (NULL == uri || 0 == uri[0]) { + return NULL; + } + + filedir = nrh_strip_scheme_prefix(uri); + + if (NULL == filedir) { + return NULL; + } + + if (0 != nr_stat(filedir, &statbuf)) { + nr_free(filedir); + return NULL; + } + + if (0 == S_ISDIR(statbuf.st_mode)) { + nr_free(filedir); + return NULL; + } + + return filedir; +} + +char* nrh_get_health_filepath(char* filedir) { + char* filepath = NULL; + char* filename = NULL; + + if (NULL == filedir) { + return NULL; + } + + filename = nrh_get_health_filename(); + if (NULL == filename) { + return NULL; + } + + filepath = nr_formatf("%s/%s", filedir, filename); + + nr_free(filename); + return filepath; +} + +nr_status_t nrh_set_start_time(void) { + clock_gettime(CLOCK_REALTIME, &start_time); + + if (0 == start_time.tv_nsec) { + return NR_FAILURE; + } + + return NR_SUCCESS; +} + +long long nrh_get_start_time_ns(void) { + return (long long)(start_time.tv_sec * BILLION + start_time.tv_nsec); +} + +long long nrh_get_current_time_ns(void) { + struct timespec ts; + + clock_gettime(CLOCK_REALTIME, &ts); + + return (long long)(ts.tv_sec * BILLION + ts.tv_nsec); +} + +nr_status_t nrh_set_last_error(nrhealth_t status) { + if (status < NRH_HEALTHY || status >= NRH_MAX_STATUS) { + return NR_FAILURE; + } + + if (NRH_SHUTDOWN == status && NRH_HEALTHY != last_error_code) { + // cannot report shutdown if agent is unhealthy + return NR_FAILURE; + } + + last_error_code = status; + return NR_SUCCESS; +} + +nrhealth_t nrh_get_last_error(void) { + return last_error_code; +} + +nr_status_t nrh_write_health(char* uri) { + nrhealth_t status = last_error_code; + FILE* fp = NULL; + char* filepath = NULL; + + if (NULL == uri) { + // invalid uri + return NR_FAILURE; + } + + filepath = nrh_get_health_filepath(uri); + + fp = fopen(filepath, "w"); + if (NULL == fp) { + // unable to open healthfile + return NR_FAILURE; + } + + fprintf(fp, "healthy: %s\n", NRH_HEALTHY == status ? "true" : "false"); + fprintf(fp, "status: %s\n", health_statuses[status].description); + fprintf(fp, "last_error_code: %s\n", health_statuses[status].code); + fprintf(fp, "status_time_unix_nano: %lld\n", nrh_get_current_time_ns()); + fprintf(fp, "status_time_unix_nano: %lld\n", nrh_get_start_time_ns()); + + fclose(fp); + nr_free(filepath); + + return NR_SUCCESS; +} diff --git a/axiom/util_health.h b/axiom/util_health.h new file mode 100644 index 000000000..da37468ed --- /dev/null +++ b/axiom/util_health.h @@ -0,0 +1,50 @@ +/* + * Copyright 2020 New Relic Corporation. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This file contains functions for agent control health file handling. + */ +#ifndef UTIL_HEALTH_HDR +#define UTIL_HEALTH_HDR + +#include + +#include "nr_axiom.h" + +typedef enum _nrhealth_t { + NRH_HEALTHY = 0, + NRH_INVALID_LICENSE, + NRH_MISSING_LICENSE, + NRH_FORCED_DISCONNECT, + NRH_HTTP_ERROR, + NRH_MISSING_APPNAME, + NRH_MAX_APPNAME, + NRH_PROXY_ERROR, + NRH_AGENT_DISABLED, + NRH_CONNECTION_FAILED, + NRH_CONFIG_ERROR, + NRH_SHUTDOWN, + NRH_MAX_STATUS +} nrhealth_t; + +/* utility */ +extern char* nrh_strip_scheme_prefix(char* uri); +extern nr_status_t nrh_write_health(char* uri); + +/* getters */ +extern char* nrh_get_health_location(char* uri); +extern char* nrh_get_health_filepath(char* filedir); +extern char* nrh_get_health_filename(void); +extern long long nrh_get_start_time_ns(void); +extern long long nrh_get_current_time_ns(void); +extern nrhealth_t nrh_get_last_error(void); +extern char* nrh_get_uuid(void); + +/* setters */ +extern nr_status_t nrh_set_start_time(void); +extern nr_status_t nrh_set_last_error(nrhealth_t code); +extern nr_status_t nrh_set_uuid(char* uuid); + +#endif /* UTIL_HEALTH_HDR */