local-listener using libnetdata (#15319)
Costa Tsaousis committed
Jul 6, 2023 at 17:19 UTC
9af8a73e255ee66c89175844eb2f3759672e5cc2
3 files changed
+109
-114
Makefile.am
+2
@@ -283,6 +283,7 @@ CGROUP_NETWORK_FILES = \
283
284
LOCAL_LISTENERS_FILES = \
285
collectors/plugins.d/local_listeners.c \
286
+ $(LIBNETDATA_FILES) \
287
$(NULL)
288
289
DISKSPACE_PLUGIN_FILES = \
@@ -1213,6 +1214,7 @@ if ENABLE_PLUGIN_LOCAL_LISTENERS
1214
plugins_PROGRAMS += local-listeners
1215
local_listeners_SOURCES = $(LOCAL_LISTENERS_FILES)
1216
local_listeners_LDADD = \
1217
+ $(NETDATA_COMMON_LIBS) \
1218
$(NULL)
1219
endif
1220
collectors/plugins.d/local_listeners.c
+105
-112
@@ -1,10 +1,12 @@
1
+#include "libnetdata/libnetdata.h"
2
+#include "libnetdata/required_dummies.h"
3
+
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <stdbool.h>
7
#include <dirent.h>
8
#include <string.h>
9
#include <unistd.h>
7
-#include <errno.h>
10
#include <ctype.h>
11
#include <arpa/inet.h>
12
@@ -15,6 +17,12 @@ typedef enum {
17
PROC_NET_PROTOCOL_UDP6,
18
} PROC_NET_PROTOCOLS;
19
20
+#define MAX_ERROR_LOGS 10
21
+
22
+static size_t pid_fds_processed = 0;
23
+static size_t pid_fds_failed = 0;
24
+static size_t errors_encountered = 0;
25
+
26
static inline const char *protocol_name(PROC_NET_PROTOCOLS protocol) {
27
switch(protocol) {
28
default:
@@ -32,26 +40,70 @@ static inline const char *protocol_name(PROC_NET_PROTOCOLS protocol) {
40
}
41
}
42
43
+static inline int read_cmdline(pid_t pid, char* buffer, size_t bufferSize) {
44
+ char path[FILENAME_MAX + 1];
45
+ snprintfz(path, FILENAME_MAX, "/proc/%d/cmdline", pid);
46
+
47
+ FILE* file = fopen(path, "r");
48
+ if (!file) {
49
+ if(++errors_encountered < MAX_ERROR_LOGS)
50
+ collector_error("LOCAL-LISTENERS: error opening file: %s\n", path);
51
+
52
+ return -1;
53
+ }
54
+
55
+ size_t bytesRead = fread(buffer, 1, bufferSize - 1, file);
56
+ buffer[bytesRead] = '\0'; // Ensure null-terminated
57
+
58
+ // Replace null characters in cmdline with spaces
59
+ for (size_t i = 0; i < bytesRead; i++) {
60
+ if (buffer[i] == '\0') {
61
+ buffer[i] = ' ';
62
+ }
63
+ }
64
+
65
+ fclose(file);
66
+ return 0;
67
+}
68
+
69
+static inline void fix_cmdline(char* str) {
70
+ if (str == NULL)
71
+ return;
72
+
73
+ char *s = str;
74
+
75
+ do {
76
+ if(*s == '|' || iscntrl(*s))
77
+ *s = '_';
78
+
79
+ } while(*++s);
80
+
81
+
82
+ while(s > str && *(s-1) == ' ')
83
+ *--s = '\0';
84
+}
85
+
86
+// ----------------------------------------------------------------------------
87
+
88
#define HASH_TABLE_SIZE 100000
36
-#define MAX_ERROR_LOGS 10
89
90
typedef struct Node {
39
- unsigned int inode;
91
+ unsigned int inode; // key
92
+
93
+ // values
94
unsigned int port;
95
char local_address[INET6_ADDRSTRLEN];
96
PROC_NET_PROTOCOLS protocol;
97
bool processed;
44
- struct Node *next;
98
+
99
+ // linking
100
+ struct Node *prev, *next;
101
} Node;
102
103
typedef struct HashTable {
104
Node *table[HASH_TABLE_SIZE];
105
} HashTable;
106
51
-static size_t pid_fds_processed = 0;
52
-static size_t pid_fds_failed = 0;
53
-static size_t errors_encountered = 0;
54
-
107
static HashTable *hashTable_key_inode_port_value = NULL;
108
109
static inline void generate_output(const char *protocol, const char *address, unsigned int port, const char *cmdline) {
@@ -59,7 +111,7 @@ static inline void generate_output(const char *protocol, const char *address, un
111
}
112
113
HashTable* createHashTable() {
62
- HashTable *hashTable = (HashTable*)malloc(sizeof(HashTable));
114
+ HashTable *hashTable = (HashTable*)mallocz(sizeof(HashTable));
115
memset(hashTable, 0, sizeof(HashTable));
116
return hashTable;
117
}
@@ -70,100 +122,49 @@ static inline unsigned int hashFunction(unsigned int inode) {
122
123
static inline void insertHashTable(HashTable *hashTable, unsigned int inode, unsigned int port, PROC_NET_PROTOCOLS protocol, char *local_address) {
124
unsigned int index = hashFunction(inode);
73
- Node *newNode = (Node*)malloc(sizeof(Node));
125
+ Node *newNode = (Node*)mallocz(sizeof(Node));
126
newNode->inode = inode;
127
newNode->port = port;
128
newNode->protocol = protocol;
77
- strncpy(newNode->local_address, local_address, INET6_ADDRSTRLEN);
78
- newNode->local_address[INET6_ADDRSTRLEN - 1] = '\0';
79
- newNode->next = hashTable->table[index];
80
- hashTable->table[index] = newNode;
129
+ strncpyz(newNode->local_address, local_address, INET6_ADDRSTRLEN - 1);
130
+ DOUBLE_LINKED_LIST_APPEND_ITEM_UNSAFE(hashTable->table[index], newNode, prev, next);
131
}
132
83
-static inline unsigned int lookupHashTable(HashTable *hashTable, unsigned int inode, PROC_NET_PROTOCOLS *protocol, char **local_address) {
133
+static inline bool lookupHashTable_and_execute(HashTable *hashTable, unsigned int inode, pid_t pid) {
134
unsigned int index = hashFunction(inode);
85
- Node *node = hashTable->table[index];
86
- while (node) {
87
- if (node->inode == inode) {
88
- *protocol = node->protocol;
89
- *local_address = node->local_address;
90
- node->processed = true;
91
- return node->port;
135
+ for(Node *node = hashTable->table[index], *next = NULL ; node ; node = next) {
136
+ next = node->next;
137
+
138
+ if(node->inode == inode && node->port) {
139
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(hashTable->table[index], node, prev, next);
140
+ char cmdline[8192] = "";
141
+ read_cmdline(pid, cmdline, sizeof(cmdline));
142
+ fix_cmdline(cmdline);
143
+ generate_output(protocol_name(node->protocol), node->local_address, node->port, cmdline);
144
+ freez(node);
145
+ return true;
146
}
93
- node = node->next;
147
}
95
- return 0; // Not found
148
+
149
+ return false;
150
}
151
152
void freeHashTable(HashTable *hashTable) {
153
for (unsigned int i = 0; i < HASH_TABLE_SIZE; i++) {
100
- Node *node = hashTable->table[i];
101
- while (node) {
102
- Node *tmp = node;
103
- if(!tmp->processed)
104
- generate_output(protocol_name(tmp->protocol), tmp->local_address, tmp->port, "");
105
- node = node->next;
106
- free(tmp);
107
- }
108
- }
109
- free(hashTable);
110
-}
111
-
112
-static inline int read_cmdline(pid_t pid, char* buffer, size_t bufferSize) {
113
- char path[FILENAME_MAX + 1];
114
- snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
115
- path[FILENAME_MAX] = '\0';
116
-
117
- FILE* file = fopen(path, "r");
118
- if (!file) {
119
- if(++errors_encountered < MAX_ERROR_LOGS)
120
- fprintf(stderr, "local-listeners: error opening file: %s\n", path);
121
-
122
- return -1;
123
- }
124
-
125
- size_t bytesRead = fread(buffer, 1, bufferSize - 1, file);
126
- buffer[bytesRead] = '\0'; // Ensure null-terminated
127
-
128
- // Replace null characters in cmdline with spaces
129
- for (size_t i = 0; i < bytesRead; i++) {
130
- if (buffer[i] == '\0') {
131
- buffer[i] = ' ';
154
+ while(hashTable->table[i]) {
155
+ Node *tmp = hashTable->table[i];
156
+ DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(hashTable->table[i], tmp, prev, next);
157
+ generate_output(protocol_name(tmp->protocol), tmp->local_address, tmp->port, "");
158
+ freez(tmp);
159
}
160
}
134
-
135
- fclose(file);
136
- return 0;
161
+ freez(hashTable);
162
}
163
139
-static inline void fix_cmdline(char* str) {
140
- if (str == NULL)
141
- return;
142
-
143
- char *s = str;
144
-
145
- do {
146
- if(*s == '|' || iscntrl(*s))
147
- *s = '_';
148
-
149
- } while(*++s);
150
-
151
-
152
- while(s > str && *(s-1) == ' ')
153
- *--s = '\0';
154
-}
164
+// ----------------------------------------------------------------------------
165
166
static inline void found_this_socket_inode(pid_t pid, unsigned int inode) {
157
- PROC_NET_PROTOCOLS protocol = 0;
158
- char *address = NULL;
159
- unsigned int port = lookupHashTable(hashTable_key_inode_port_value, inode, &protocol, &address);
160
-
161
- if(port) {
162
- char cmdline[8192] = "";
163
- read_cmdline(pid, cmdline, sizeof(cmdline));
164
- fix_cmdline(cmdline);
165
- generate_output(protocol_name(protocol), address, port, cmdline);
166
- }
167
+ lookupHashTable_and_execute(hashTable_key_inode_port_value, inode, pid);
168
}
169
170
bool find_all_sockets_in_proc(const char *proc_filename) {
@@ -174,7 +175,7 @@ bool find_all_sockets_in_proc(const char *proc_filename) {
175
proc_dir = opendir(proc_filename);
176
if (proc_dir == NULL) {
177
if(++errors_encountered < MAX_ERROR_LOGS)
177
- fprintf(stderr, "local-listeners: cannot opendir() '%s' (error: %s)\n", proc_filename, strerror(errno));
178
+ collector_error("LOCAL-LISTENERS: cannot opendir() '%s'", proc_filename);
179
180
pid_fds_failed++;
181
return false;
@@ -194,13 +195,12 @@ bool find_all_sockets_in_proc(const char *proc_filename) {
195
continue;
196
197
// Build the path to the fd directory of the process
197
- snprintf(path_buffer, FILENAME_MAX, "%s/%s/fd/", proc_filename, proc_entry->d_name);
198
- path_buffer[FILENAME_MAX] = '\0';
198
+ snprintfz(path_buffer, FILENAME_MAX, "%s/%s/fd/", proc_filename, proc_entry->d_name);
199
200
fd_dir = opendir(path_buffer);
201
if (fd_dir == NULL) {
202
if(++errors_encountered < MAX_ERROR_LOGS)
203
- fprintf(stderr, "local-listeners: cannot opendir() '%s' (error: %s)\n", path_buffer, strerror(errno));
203
+ collector_error("LOCAL-LISTENERS: cannot opendir() '%s'", path_buffer);
204
205
pid_fds_failed++;
206
continue;
@@ -210,18 +210,17 @@ bool find_all_sockets_in_proc(const char *proc_filename) {
210
if(!strcmp(fd_entry->d_name, ".") || !strcmp(fd_entry->d_name, ".."))
211
continue;
212
213
- char link_path[512];
214
- char link_target[512];
213
+ char link_path[FILENAME_MAX + 1];
214
+ char link_target[FILENAME_MAX + 1];
215
int inode;
216
217
// Build the path to the file descriptor link
218
- strncpy(link_path, path_buffer, sizeof(link_path));
219
- strncat(link_path, fd_entry->d_name, sizeof(link_path) - strlen(link_path) - 1);
218
+ snprintfz(link_path, FILENAME_MAX, "%s/%s", path_buffer, fd_entry->d_name);
219
220
ssize_t len = readlink(link_path, link_target, sizeof(link_target) - 1);
221
if (len == -1) {
222
if(++errors_encountered < MAX_ERROR_LOGS)
224
- fprintf(stderr, "local-listeners: cannot read link '%s' (error: %s)\n", link_path, strerror(errno));
223
+ collector_error("LOCAL-LISTENERS: cannot read link '%s'", link_path);
224
225
pid_fds_failed++;
226
continue;
@@ -242,6 +241,8 @@ bool find_all_sockets_in_proc(const char *proc_filename) {
241
return true;
242
}
243
244
+// ----------------------------------------------------------------------------
245
+
246
static inline void add_port_and_inode(PROC_NET_PROTOCOLS protocol, unsigned int port, unsigned int inode, char *local_address) {
247
insertHashTable(hashTable_key_inode_port_value, inode, port, protocol, local_address);
248
}
@@ -336,38 +337,30 @@ bool read_proc_net_x(const char *filename, PROC_NET_PROTOCOLS protocol) {
337
return true;
338
}
339
339
-int main(int argc, char **argv) {
340
- (void)argc;
341
- (void)argv;
342
-
343
- char *netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
344
- if(!netdata_configured_host_prefix) netdata_configured_host_prefix = "";
340
+// ----------------------------------------------------------------------------
341
342
+int main(int argc __maybe_unused, char **argv __maybe_unused) {
343
char path[FILENAME_MAX + 1];
347
-
344
hashTable_key_inode_port_value = createHashTable();
345
350
- snprintf(path, FILENAME_MAX, "%s/proc/net/tcp", netdata_configured_host_prefix);
351
- path[FILENAME_MAX] = '\0';
346
+ netdata_configured_host_prefix = getenv("NETDATA_HOST_PREFIX");
347
+ if(!netdata_configured_host_prefix) netdata_configured_host_prefix = "";
348
+
349
+ snprintfz(path, FILENAME_MAX, "%s/proc/net/tcp", netdata_configured_host_prefix);
350
read_proc_net_x(path, PROC_NET_PROTOCOL_TCP);
351
354
- snprintf(path, FILENAME_MAX, "%s/proc/net/udp", netdata_configured_host_prefix);
355
- path[FILENAME_MAX] = '\0';
352
+ snprintfz(path, FILENAME_MAX, "%s/proc/net/udp", netdata_configured_host_prefix);
353
read_proc_net_x(path, PROC_NET_PROTOCOL_UDP);
354
358
- snprintf(path, FILENAME_MAX, "%s/proc/net/tcp6", netdata_configured_host_prefix);
359
- path[FILENAME_MAX] = '\0';
355
+ snprintfz(path, FILENAME_MAX, "%s/proc/net/tcp6", netdata_configured_host_prefix);
356
read_proc_net_x(path, PROC_NET_PROTOCOL_TCP6);
357
362
- snprintf(path, FILENAME_MAX, "%s/proc/net/udp6", netdata_configured_host_prefix);
363
- path[FILENAME_MAX] = '\0';
358
+ snprintfz(path, FILENAME_MAX, "%s/proc/net/udp6", netdata_configured_host_prefix);
359
read_proc_net_x(path, PROC_NET_PROTOCOL_UDP6);
360
366
- snprintf(path, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
367
- path[FILENAME_MAX] = '\0';
361
+ snprintfz(path, FILENAME_MAX, "%s/proc", netdata_configured_host_prefix);
362
find_all_sockets_in_proc(path);
363
364
freeHashTable(hashTable_key_inode_port_value);
371
-
365
return 0;
366
}
libnetdata/log/log.c
+2
-2
@@ -781,7 +781,7 @@ void debug_int( const char *file, const char *function, const unsigned long line
781
void info_int( int is_collector, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... )
782
{
783
va_list args;
784
- FILE *fp = (is_collector) ? stderr : stderror;
784
+ FILE *fp = (is_collector || !stderror) ? stderr : stderror;
785
786
log_lock();
787
@@ -910,7 +910,7 @@ void error_limit_int(ERROR_LIMIT *erl, const char *prefix, const char *file __ma
910
void error_int(int is_collector, const char *prefix, const char *file __maybe_unused, const char *function __maybe_unused, const unsigned long line __maybe_unused, const char *fmt, ... ) {
911
// save a copy of errno - just in case this function generates a new error
912
int __errno = errno;
913
- FILE *fp = (is_collector) ? stderr : stderror;
913
+ FILE *fp = (is_collector || !stderror) ? stderr : stderror;
914
915
va_list args;
916