diff options
Diffstat (limited to 'libnetdata')
-rw-r--r-- | libnetdata/buffer/buffer.c | 48 | ||||
-rw-r--r-- | libnetdata/buffer/buffer.h | 1 | ||||
-rw-r--r-- | libnetdata/circular_buffer/circular_buffer.c | 5 | ||||
-rw-r--r-- | libnetdata/circular_buffer/circular_buffer.h | 1 | ||||
-rw-r--r-- | libnetdata/config/appconfig.c | 8 | ||||
-rw-r--r-- | libnetdata/dictionary/dictionary.c | 27 | ||||
-rw-r--r-- | libnetdata/dictionary/dictionary.h | 6 | ||||
-rw-r--r-- | libnetdata/inlined.h | 41 | ||||
-rw-r--r-- | libnetdata/libnetdata.c | 133 | ||||
-rw-r--r-- | libnetdata/libnetdata.h | 9 | ||||
-rw-r--r-- | libnetdata/popen/popen.c | 437 | ||||
-rw-r--r-- | libnetdata/popen/popen.h | 26 | ||||
-rw-r--r-- | libnetdata/required_dummies.h | 1 | ||||
-rw-r--r-- | libnetdata/socket/security.c | 45 | ||||
-rw-r--r-- | libnetdata/socket/security.h | 14 | ||||
-rw-r--r-- | libnetdata/socket/socket.c | 101 | ||||
-rw-r--r-- | libnetdata/socket/socket.h | 28 | ||||
-rw-r--r-- | libnetdata/threads/threads.c | 2 |
18 files changed, 633 insertions, 300 deletions
diff --git a/libnetdata/buffer/buffer.c b/libnetdata/buffer/buffer.c index 3d29c30255..d0940588fc 100644 --- a/libnetdata/buffer/buffer.c +++ b/libnetdata/buffer/buffer.c @@ -148,6 +148,54 @@ void buffer_print_ll(BUFFER *wb, long long value) buffer_print_llu(wb, value); } +static unsigned char bits03_to_hex[16] = { + [0] = '0', + [1] = '1', + [2] = '2', + [3] = '3', + [4] = '4', + [5] = '5', + [6] = '6', + [7] = '7', + [8] = '8', + [9] = '9', + [10] = 'A', + [11] = 'B', + [12] = 'C', + [13] = 'D', + [14] = 'E', + [15] = 'F' +}; + +void buffer_print_llu_hex(BUFFER *wb, unsigned long long value) +{ + unsigned char buffer[sizeof(unsigned long long) * 2 + 2 + 1]; // 8 bytes * 2 + '0x' + '\0' + unsigned char *e = &buffer[sizeof(unsigned long long) * 2 + 2]; + unsigned char *p = e; + + *p-- = '\0'; + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + if(value) { + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + + while(value) { + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + + if(value) { + *p-- = bits03_to_hex[value & 0xF]; + value >>= 4; + } + } + } + *p-- = 'x'; + *p = '0'; + + buffer_fast_strcat(wb, (char *)p, e - p); +} + void buffer_fast_strcat(BUFFER *wb, const char *txt, size_t len) { if(unlikely(!txt || !*txt)) return; diff --git a/libnetdata/buffer/buffer.h b/libnetdata/buffer/buffer.h index c134cc9aca..fe45b1c6d6 100644 --- a/libnetdata/buffer/buffer.h +++ b/libnetdata/buffer/buffer.h @@ -79,6 +79,7 @@ extern char *print_number_llu_r_smart(char *str, unsigned long long uvalue); extern void buffer_print_llu(BUFFER *wb, unsigned long long uvalue); extern void buffer_print_ll(BUFFER *wb, long long value); +extern void buffer_print_llu_hex(BUFFER *wb, unsigned long long value); static inline void buffer_need_bytes(BUFFER *buffer, size_t needed_free_size) { if(unlikely(buffer->size - buffer->len < needed_free_size)) diff --git a/libnetdata/circular_buffer/circular_buffer.c b/libnetdata/circular_buffer/circular_buffer.c index f074996d9d..c791b420b0 100644 --- a/libnetdata/circular_buffer/circular_buffer.c +++ b/libnetdata/circular_buffer/circular_buffer.c @@ -89,3 +89,8 @@ size_t cbuffer_next_unsafe(struct circular_buffer *buf, char **start) { } return buf->size - buf->read; } + +void cbuffer_flush(struct circular_buffer*buf) { + buf->write = 0; + buf->read = 0; +}
\ No newline at end of file diff --git a/libnetdata/circular_buffer/circular_buffer.h b/libnetdata/circular_buffer/circular_buffer.h index e5addc50f4..ba3ddd9cd9 100644 --- a/libnetdata/circular_buffer/circular_buffer.h +++ b/libnetdata/circular_buffer/circular_buffer.h @@ -14,5 +14,6 @@ extern int cbuffer_add_unsafe(struct circular_buffer *buf, const char *d, size_t extern void cbuffer_remove_unsafe(struct circular_buffer *buf, size_t num); extern size_t cbuffer_next_unsafe(struct circular_buffer *buf, char **start); extern size_t cbuffer_available_size_unsafe(struct circular_buffer *buf); +extern void cbuffer_flush(struct circular_buffer*buf); #endif diff --git a/libnetdata/config/appconfig.c b/libnetdata/config/appconfig.c index 1288366da8..ba1f059d57 100644 --- a/libnetdata/config/appconfig.c +++ b/libnetdata/config/appconfig.c @@ -32,8 +32,8 @@ _CONNECTOR_INSTANCE *add_connector_instance(struct section *connector, struct se local_ci = callocz(1, sizeof(struct _connector_instance)); local_ci->instance = instance; local_ci->connector = connector; - strncpy(local_ci->instance_name, instance->name, CONFIG_MAX_NAME); - strncpy(local_ci->connector_name, connector->name, CONFIG_MAX_NAME); + strncpyz(local_ci->instance_name, instance->name, CONFIG_MAX_NAME); + strncpyz(local_ci->connector_name, connector->name, CONFIG_MAX_NAME); local_ci->next = global_connector_instance; global_connector_instance = local_ci; @@ -686,14 +686,14 @@ int appconfig_load(struct config *root, char *filename, int overwrite_used, cons int rc; rc = is_valid_connector(s, 0); if (likely(rc)) { - strncpy(working_connector, s, CONFIG_MAX_NAME); + strncpyz(working_connector, s, CONFIG_MAX_NAME); s = s + rc + 1; if (unlikely(!(*s))) { _connectors++; sprintf(buffer, "instance_%d", _connectors); s = buffer; } - strncpy(working_instance, s, CONFIG_MAX_NAME); + strncpyz(working_instance, s, CONFIG_MAX_NAME); working_connector_section = NULL; if (unlikely(appconfig_section_find(root, working_instance))) { error("Instance (%s) already exists", working_instance); diff --git a/libnetdata/dictionary/dictionary.c b/libnetdata/dictionary/dictionary.c index 88de7df3af..9d19d5965d 100644 --- a/libnetdata/dictionary/dictionary.c +++ b/libnetdata/dictionary/dictionary.c @@ -249,6 +249,9 @@ void dictionary_register_conflict_callback(DICTIONARY *dict, bool (*conflict_cal if(unlikely(is_view_dictionary(dict))) fatal("DICTIONARY: called %s() on a view.", __FUNCTION__ ); + internal_error(!(dict->options & DICT_OPTION_DONT_OVERWRITE_VALUE), "DICTIONARY: registering conflict callback without DICT_OPTION_DONT_OVERWRITE_VALUE"); + dict->options |= DICT_OPTION_DONT_OVERWRITE_VALUE; + dictionary_hooks_allocate(dict); dict->hooks->conflict_callback = conflict_callback; dict->hooks->conflict_callback_data = data; @@ -457,6 +460,10 @@ static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET(DICTIONARY *dict, DICTIONARY return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST); } +static inline REFCOUNT DICTIONARY_ITEM_REFCOUNT_GET_SOLE(DICTIONARY_ITEM *item) { + return (REFCOUNT)__atomic_load_n(&item->refcount, __ATOMIC_SEQ_CST); +} + // ---------------------------------------------------------------------------- // callbacks execution @@ -617,6 +624,12 @@ static void ll_recursive_unlock(DICTIONARY *dict, char rw) { } } +void dictionary_write_lock(DICTIONARY *dict) { + ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); +} +void dictionary_write_unlock(DICTIONARY *dict) { + ll_recursive_unlock(dict, DICTIONARY_LOCK_WRITE); +} static inline void dictionary_index_lock_rdlock(DICTIONARY *dict) { if(unlikely(is_dictionary_single_threaded(dict))) @@ -1494,7 +1507,9 @@ static bool dictionary_free_all_resources(DICTIONARY *dict, size_t *mem, bool fo #endif // destroy the index + dictionary_index_lock_wrlock(dict); index_size += hashtable_destroy_unsafe(dict); + dictionary_index_lock_unlock(dict); ll_recursive_lock(dict, DICTIONARY_LOCK_WRITE); DICTIONARY_ITEM *item = dict->items.list; @@ -1797,6 +1812,7 @@ size_t dictionary_destroy(DICTIONARY *dict) { if(!dict) return 0; + dict_flag_set(dict, DICT_FLAG_DESTROYED); DICTIONARY_STATS_DICT_DESTRUCTIONS_PLUS1(dict); size_t referenced_items = dictionary_referenced_items(dict); @@ -1936,20 +1952,23 @@ void dictionary_acquired_item_release(DICTIONARY *dict, DICT_ITEM_CONST DICTIONA // get the name/value of an item const char *dictionary_acquired_item_name(DICT_ITEM_CONST DICTIONARY_ITEM *item) { - api_internal_check(NULL, item, true, false); return item_get_name(item); } void *dictionary_acquired_item_value(DICT_ITEM_CONST DICTIONARY_ITEM *item) { - // we allow the item to be NULL here - api_internal_check(NULL, item, true, true); - if(likely(item)) return item->shared->value; return NULL; } +size_t dictionary_acquired_item_references(DICT_ITEM_CONST DICTIONARY_ITEM *item) { + if(likely(item)) + return DICTIONARY_ITEM_REFCOUNT_GET_SOLE(item); + + return 0; +} + // ---------------------------------------------------------------------------- // DEL an item diff --git a/libnetdata/dictionary/dictionary.h b/libnetdata/dictionary/dictionary.h index 3cbaa87f07..c51746dbff 100644 --- a/libnetdata/dictionary/dictionary.h +++ b/libnetdata/dictionary/dictionary.h @@ -180,7 +180,7 @@ extern DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_set_and_acquire_item_advanced #define dictionary_view_set_and_acquire_item(dict, name, master_item) dictionary_view_set_and_acquire_item_advanced(dict, name, -1, master_item) extern DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_view_set_and_acquire_item_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item); #define dictionary_view_set(dict, name, master_item) dictionary_view_set_advanced(dict, name, -1, master_item) -extern void *dictionary_view_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICTIONARY_ITEM *master_item); +extern void *dictionary_view_set_advanced(DICTIONARY *dict, const char *name, ssize_t name_len, DICT_ITEM_CONST DICTIONARY_ITEM *master_item); // ---------------------------------------------------------------------------- // Get an item from the dictionary @@ -211,6 +211,7 @@ extern DICT_ITEM_CONST DICTIONARY_ITEM *dictionary_acquired_item_dup(DICTIONARY extern const char *dictionary_acquired_item_name(DICT_ITEM_CONST DICTIONARY_ITEM *item); extern void *dictionary_acquired_item_value(DICT_ITEM_CONST DICTIONARY_ITEM *item); +extern size_t dictionary_acquired_item_references(DICT_ITEM_CONST DICTIONARY_ITEM *item); // ---------------------------------------------------------------------------- // Traverse (walk through) the items of the dictionary. @@ -252,6 +253,9 @@ int dictionary_sorted_walkthrough_rw(DICTIONARY *dict, char rw, int (*callback)( #define DICTIONARY_LOCK_WRITE 'w' #define DICTIONARY_LOCK_REENTRANT 'z' +extern void dictionary_write_lock(DICTIONARY *dict); +extern void dictionary_write_unlock(DICTIONARY *dict); + typedef DICTFE_CONST struct dictionary_foreach { DICTIONARY *dict; // the dictionary upon we work diff --git a/libnetdata/inlined.h b/libnetdata/inlined.h index 5c265fc015..788a0312b6 100644 --- a/libnetdata/inlined.h +++ b/libnetdata/inlined.h @@ -42,24 +42,12 @@ static inline uint32_t simple_uhash(const char *name) { return hval; } -static inline int simple_hash_strcmp(const char *name, const char *b, uint32_t *hash) { - unsigned char *s = (unsigned char *) name; - uint32_t hval = 0x811c9dc5; - int ret = 0; - while (*s) { - if(!ret) ret = *s - *b++; - hval *= 16777619; - hval ^= (uint32_t) *s++; - } - *hash = hval; - return ret; -} - static inline int str2i(const char *s) { int n = 0; - char c, negative = (*s == '-'); + char c, negative = (char)(*s == '-'); + const char *e = &s[30]; // max number of character to iterate - for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' ; c = *(++s)) { + for(c = (char)((negative)?*(++s):*s); c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -73,8 +61,9 @@ static inline int str2i(const char *s) { static inline long str2l(const char *s) { long n = 0; char c, negative = (*s == '-'); + const char *e = &s[30]; // max number of character to iterate - for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' ; c = *(++s)) { + for(c = (negative)?*(++s):*s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -88,7 +77,9 @@ static inline long str2l(const char *s) { static inline uint32_t str2uint32_t(const char *s) { uint32_t n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -98,7 +89,9 @@ static inline uint32_t str2uint32_t(const char *s) { static inline uint64_t str2uint64_t(const char *s) { uint64_t n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -108,7 +101,9 @@ static inline uint64_t str2uint64_t(const char *s) { static inline unsigned long str2ul(const char *s) { unsigned long n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -118,7 +113,9 @@ static inline unsigned long str2ul(const char *s) { static inline unsigned long long str2ull(const char *s) { unsigned long long n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } @@ -137,7 +134,9 @@ static inline long long str2ll(const char *s, char **endptr) { long long n = 0; char c; - for(c = *s; c >= '0' && c <= '9' ; c = *(++s)) { + const char *e = &s[30]; // max number of character to iterate + + for(c = *s; c >= '0' && c <= '9' && s < e ; c = *(++s)) { n *= 10; n += c - '0'; } diff --git a/libnetdata/libnetdata.c b/libnetdata/libnetdata.c index 5b6c541edf..ee5e4851da 100644 --- a/libnetdata/libnetdata.c +++ b/libnetdata/libnetdata.c @@ -1535,6 +1535,121 @@ char *find_and_replace(const char *src, const char *find, const char *replace, c return value; } +inline int pluginsd_space(char c) { + switch(c) { + case ' ': + case '\t': + case '\r': + case '\n': + case '=': + return 1; + + default: + return 0; + } +} + +inline int config_isspace(char c) +{ + switch (c) { + case ' ': + case '\t': + case '\r': + case '\n': + case ',': + return 1; + + default: + return 0; + } +} + +// split a text into words, respecting quotes +inline int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover) +{ + char *s = str, quote = 0; + int i = 0, rec = 0; + char *recover = recover_input; + + // skip all white space + while (unlikely(custom_isspace(*s))) + s++; + + // check for quote + if (unlikely(*s == '\'' || *s == '"')) { + quote = *s; // remember the quote + s++; // skip the quote + } + + // store the first word + words[i++] = s; + + // while we have something + while (likely(*s)) { + // if it is escape + if (unlikely(*s == '\\' && s[1])) { + s += 2; + continue; + } + + // if it is quote + else if (unlikely(*s == quote)) { + quote = 0; + if (recover && rec < max_recover) { + recover_location[rec++] = s; + *recover++ = *s; + } + *s = ' '; + continue; + } + + // if it is a space + else if (unlikely(quote == 0 && custom_isspace(*s))) { + // terminate the word + if (recover && rec < max_recover) { + if (!rec || recover_location[rec-1] != s) { + recover_location[rec++] = s; + *recover++ = *s; + } + } + *s++ = '\0'; + + // skip all white space + while (likely(custom_isspace(*s))) + s++; + + // check for quote + if (unlikely(*s == '\'' || *s == '"')) { + quote = *s; // remember the quote + s++; // skip the quote + } + + // if we reached the end, stop + if (unlikely(!*s)) + break; + + // store the next word + if (likely(i < max_words)) + words[i++] = s; + else + break; + } + + // anything else + else + s++; + } + + // terminate the words + memset(&words[i], 0, (max_words - i) * sizeof (char *)); + + return i; +} + +inline int pluginsd_split_words(char *str, char **words, int max_words, char *recover_input, char **recover_location, int max_recover) +{ + return quoted_strings_splitter(str, words, max_words, pluginsd_space, recover_input, recover_location, max_recover); +} bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx) { if (unlikely(!ptr)) @@ -1550,3 +1665,21 @@ void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value) { else ptr->data[idx / 64] &= ~(1ULL << (idx % 64)); } + +bool run_command_and_copy_output_to_stdout(const char *command, int max_line_length) { + pid_t pid; + FILE *fp = netdata_popen(command, &pid, NULL); + + if(fp) { + char buffer[max_line_length + 1]; + while (fgets(buffer, max_line_length, fp)) + fprintf(stdout, "%s", buffer); + } + else { + error("Failed to execute command '%s'.", command); + return false; + } + + netdata_pclose(NULL, fp, pid); + return true; +} diff --git a/libnetdata/libnetdata.h b/libnetdata/libnetdata.h index 7c84359d82..2b60b5366b 100644 --- a/libnetdata/libnetdata.h +++ b/libnetdata/libnetdata.h @@ -388,6 +388,15 @@ typedef struct bitmap256 { extern bool bitmap256_get_bit(BITMAP256 *ptr, uint8_t idx); extern void bitmap256_set_bit(BITMAP256 *ptr, uint8_t idx, bool value); +#define COMPRESSION_MAX_MSG_SIZE 0x4000 +#define PLUGINSD_LINE_MAX (COMPRESSION_MAX_MSG_SIZE - 1024) +extern int config_isspace(char c); +extern int pluginsd_space(char c); +int quoted_strings_splitter(char *str, char **words, int max_words, int (*custom_isspace)(char), char *recover_input, char **recover_location, int max_recover); +extern int pluginsd_split_words(char *str, char **words, int max_words, char *recover_string, char **recover_location, int max_recover); + +extern bool run_command_and_copy_output_to_stdout(const char *command, int max_line_length); + extern void netdata_cleanup_and_exit(int ret) NORETURN; extern void send_statistics(const char *action, const char *action_result, const char *action_data); extern char *netdata_configured_host_prefix; diff --git a/libnetdata/popen/popen.c b/libnetdata/popen/popen.c index eaeffd32dd..b67d6b096b 100644 --- a/libnetdata/popen/popen.c +++ b/libnetdata/popen/popen.c @@ -2,81 +2,129 @@ #include "../libnetdata.h" -static pthread_mutex_t myp_lock; -static int myp_tracking = 0; +// ---------------------------------------------------------------------------- +// popen with tracking -struct mypopen { +static pthread_mutex_t netdata_popen_tracking_mutex; +static bool netdata_popen_tracking_enabled = false; + +struct netdata_popen { pid_t pid; - struct mypopen *next; - struct mypopen *prev; + struct netdata_popen *next; + struct netdata_popen *prev; }; -static struct mypopen *mypopen_root = NULL; +static struct netdata_popen *netdata_popen_root = NULL; // myp_add_lock takes the lock if we're tracking. -static void myp_add_lock(void) { - if (myp_tracking == 0) +static void netdata_popen_tracking_lock(void) { + if(!netdata_popen_tracking_enabled) return; - netdata_mutex_lock(&myp_lock); + netdata_mutex_lock(&netdata_popen_tracking_mutex); } // myp_add_unlock release the lock if we're tracking. -static void myp_add_unlock(void) { - if (myp_tracking == 0) +static void netdata_popen_tracking_unlock(void) { + if(!netdata_popen_tracking_enabled) return; - netdata_mutex_unlock(&myp_lock); + netdata_mutex_unlock(&netdata_popen_tracking_mutex); } // myp_add_locked adds pid if we're tracking. // myp_add_lock must have been called previously. -static void myp_add_locked(pid_t pid) { - struct mypopen *mp; - - if (myp_tracking == 0) +static void netdata_popen_tracking_add_pid_unsafe(pid_t pid) { + if(!netdata_popen_tracking_enabled) return; - mp = mallocz(sizeof(struct mypopen)); + struct netdata_popen *mp; + + mp = mallocz(sizeof(struct netdata_popen)); mp->pid = pid; - mp->next = mypopen_root; - mp->prev = NULL; - if (mypopen_root != NULL) - mypopen_root->prev = mp; - mypopen_root = mp; - netdata_mutex_unlock(&myp_lock); + DOUBLE_LINKED_LIST_PREPEND_UNSAFE(netdata_popen_root, mp, prev, next); } // myp_del deletes pid if we're tracking. -static void myp_del(pid_t pid) { - struct mypopen *mp; - - if (myp_tracking == 0) +static void netdata_popen_tracking_del_pid(pid_t pid) { + if(!netdata_popen_tracking_enabled) return; - netdata_mutex_lock(&myp_lock); - for (mp = mypopen_root; mp != NULL; mp = mp->next) { - if (mp->pid == pid) { - if (mp->next != NULL) - mp->next->prev = mp->prev; - if (mp->prev != NULL) - mp->prev->next = mp->next; - if (mypopen_root == mp) - mypopen_root = mp->next; - freez(mp); + struct netdata_popen *mp; + + netdata_mutex_lock(&netdata_popen_tracking_mutex); + + DOUBLE_LINKED_LIST_FOREACH_FORWARD(netdata_popen_root, mp, prev, next) { + if(unlikely(mp->pid == pid)) break; - } } - if (mp == NULL) + if(mp) { + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(netdata_popen_root, mp, prev, next); + freez(mp); + } + else error("Cannot find pid %d.", pid); - netdata_mutex_unlock(&myp_lock); + netdata_mutex_unlock(&netdata_popen_tracking_mutex); } -#define PIPE_READ 0 -#define PIPE_WRITE 1 +// netdata_popen_tracking_init() should be called by apps which act as init +// (pid 1) so that processes created by mypopen and mypopene +// are tracked. This enables the reaper to ignore processes +// which will be handled internally, by calling myp_reap, to +// avoid issues with already reaped processes during wait calls. +// +// Callers should call myp_free() to clean up resources. +void netdata_popen_tracking_init(void) { + info("process tracking enabled."); + netdata_popen_tracking_enabled = true; + + if (netdata_mutex_init(&netdata_popen_tracking_mutex) != 0) + fatal("netdata_popen_tracking_init() mutex init failed."); +} + +// myp_free cleans up any resources allocated for process +// tracking. +void netdata_popen_tracking_cleanup(void) { + if(!netdata_popen_tracking_enabled) + return; + + netdata_mutex_lock(&netdata_popen_tracking_mutex); + netdata_popen_tracking_enabled = false; + + while(netdata_popen_root) { + struct netdata_popen *mp = netdata_popen_root; + DOUBLE_LINKED_LIST_REMOVE_UNSAFE(netdata_popen_root, mp, prev, next); + freez(mp); + } + + netdata_mutex_unlock(&netdata_popen_tracking_mutex); +} + +// myp_reap returns 1 if pid should be reaped, 0 otherwise. +int netdata_popen_tracking_pid_shoud_be_reaped(pid_t pid) { + if(!netdata_popen_tracking_enabled) + return 0; + + netdata_mutex_lock(&netdata_popen_tracking_mutex); + + int ret = 1; + struct netdata_popen *mp; + DOUBLE_LINKED_LIST_FOREACH_FORWARD(netdata_popen_root, mp, prev, next) { + if(unlikely(mp->pid == pid)) { + ret = 0; + break; + } + } + + netdata_mutex_unlock(&netdata_popen_tracking_mutex); + return ret; +} + +// ---------------------------------------------------------------------------- +// helpers static inline void convert_argv_to_string(char *dst, size_t size, const char *spawn_argv[]) { int i; @@ -89,118 +137,185 @@ static inline void convert_argv_to_string(char *dst, size_t size, const char *sp } } +// ---------------------------------------------------------------------------- +// the core of netdata popen + /* * Returns -1 on failure, 0 on success. When POPEN_FLAG_CREATE_PIPE is set, on success set the FILE *fp pointer. */ -static int custom_popene(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp, const char *command, const char *spawn_argv[]) { +#define PIPE_READ 0 +#define PIPE_WRITE 1 + +static int popene_internal(volatile pid_t *pidptr, char **env, uint8_t flags, FILE **fpp_child_stdin, FILE **fpp_child_stdout, const char *command, const char *spawn_argv[]) { // create a string to be logged about the command we are running char command_to_be_logged[2048]; convert_argv_to_string(command_to_be_logged, sizeof(command_to_be_logged), spawn_argv); // info("custom_popene() running command: %s", command_to_be_logged); - FILE *fp = NULL; - int ret = 0; // success by default - int pipefd[2], error; + int ret = 0; // success by default + int attr_rc = 1; // failure by default + + FILE *fp_child_stdin = NULL, *fp_child_stdout = NULL; + int pipefd_stdin[2] = { -1, -1 }; + int pipefd_stdout[2] = { -1, -1 }; + pid_t pid; posix_spawnattr_t attr; posix_spawn_file_actions_t fa; - if (flags & POPEN_FLAG_CREATE_PIPE) { - if (pipe(pipefd) == -1) - return -1; - if ((fp = fdopen(pipefd[PIPE_READ], "r")) == NULL) { - goto error_after_pipe; + int stdin_fd_to_exclude_from_closing = -1; + int stdout_fd_to_exclude_from_closing = -1; + + if(posix_spawn_file_actions_init(&fa)) { + error("POPEN: posix_spawn_file_actions_init() failed."); + return -1; + } + + if(fpp_child_stdin) { + if (pipe(pipefd_stdin) == -1) { + error("POPEN: stdin pipe() failed"); + ret = -1; + goto cleanup_and_return; + } + + if ((fp_child_stdin = fdopen(pipefd_stdin[PIPE_WRITE], "w")) == NULL) { + error("POPEN: fdopen() stdin failed"); + ret = -1; + goto cleanup_and_return; + } + + if(posix_spawn_file_actions_adddup2(&fa, pipefd_stdin[PIPE_READ], STDIN_FILENO)) { + error("POPEN: posix_spawn_file_actions_adddup2() on stdin failed."); + ret = -1; + goto cleanup_and_return; + } + } + else { + if (posix_spawn_file_actions_addopen(&fa, STDIN_FILENO, "/dev/null", O_RDONLY, 0)) { + error("POPEN: posix_spawn_file_actions_addopen() on stdin to /dev/null failed."); + // this is not a fatal error + stdin_fd_to_exclude_from_closing = STDIN_FILENO; } } - if (flags & POPEN_FLAG_CLOSE_FD) { - // Mark all files to be closed by the exec() stage of posix_spawn() - int i; - for (i = (int) (sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--) { - if (i != STDIN_FILENO && i != STDERR_FILENO) - (void) fcntl(i, F_SETFD, FD_CLOEXEC); + if (fpp_child_stdout) { + if (pipe(pipefd_stdout) == -1) { + error("POPEN: stdout pipe() failed"); + ret = -1; + goto cleanup_and_return; + } + + if ((fp_child_stdout = fdopen(pipefd_stdout[PIPE_READ], "r")) == NULL) { + error("POPEN: fdopen() stdout failed"); + ret = -1; + goto cleanup_and_return; + } + + if(posix_spawn_file_actions_adddup2(&fa, pipefd_stdout[PIPE_WRITE], STDOUT_FILENO)) { + error("POPEN: posix_spawn_file_actions_adddup2() on stdout failed."); + ret = -1; + goto cleanup_and_return; + } + } + else { + if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0)) { + error("POPEN: posix_spawn_file_actions_addopen() on stdout to /dev/null failed."); + // this is not a fatal error + stdout_fd_to_exclude_from_closing = STDOUT_FILENO; } } - if (!posix_spawn_file_actions_init(&fa)) { - if (flags & POPEN_FLAG_CREATE_PIPE) { - // move the pipe to stdout in the child - if (posix_spawn_file_actions_adddup2(&fa, pipefd[PIPE_WRITE], STDOUT_FILENO)) { - error("posix_spawn_file_actions_adddup2() failed"); - goto error_after_posix_spawn_file_actions_init; - } - } else { - // set stdout to /dev/null - if (posix_spawn_file_actions_addopen(&fa, STDOUT_FILENO, "/dev/null", O_WRONLY, 0)) { - error("posix_spawn_file_actions_addopen() failed"); - // this is not a fatal error - } + if(flags & POPEN_FLAG_CLOSE_FD) { + // Mark all files to be closed by the exec() stage of posix_spawn() + for(int i = (int)(sysconf(_SC_OPEN_MAX) - 1); i >= 0; i--) { + if(likely(i != STDERR_FILENO && i != stdin_fd_to_exclude_from_closing && i != stdout_fd_to_exclude_from_closing)) + (void)fcntl(i, F_SETFD, FD_CLOEXEC); } - } else { - error("posix_spawn_file_actions_init() failed."); - goto error_after_pipe; } - if (!(error = posix_spawnattr_init(&attr))) { + + attr_rc = posix_spawnattr_init(&attr); + if(attr_rc) { + // failed + error("POPEN: posix_spawnattr_init() failed."); + } + else { + // success // reset all signals in the child - sigset_t mask; if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF)) - error("posix_spawnattr_setflags() failed."); + error("POPEN: posix_spawnattr_setflags() fai |