summaryrefslogtreecommitdiff
path: root/agent/mibgroup/util_funcs/get_pid_from_inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'agent/mibgroup/util_funcs/get_pid_from_inode.c')
-rw-r--r--agent/mibgroup/util_funcs/get_pid_from_inode.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/agent/mibgroup/util_funcs/get_pid_from_inode.c b/agent/mibgroup/util_funcs/get_pid_from_inode.c
new file mode 100644
index 0000000..8e157ae
--- /dev/null
+++ b/agent/mibgroup/util_funcs/get_pid_from_inode.c
@@ -0,0 +1,186 @@
+#include <net-snmp/net-snmp-config.h>
+
+#include "get_pid_from_inode.h"
+
+#include <net-snmp/output_api.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+# define PROC_PATH "/proc"
+# define SOCKET_TYPE_1 "socket:["
+# define SOCKET_TYPE_2 "[0000]:"
+
+/* Definition of a simple open addressing hash table.*/
+/* When inode == 0 then the entry is empty.*/
+typedef struct {
+ ino64_t inode;
+ pid_t pid;
+} inode_pid_ent_t;
+
+#define INODE_PID_TABLE_MAX_COLLISIONS 1000
+#define INODE_PID_TABLE_LENGTH 20000
+#define INODE_PID_TABLE_SIZE (INODE_PID_TABLE_LENGTH * sizeof (inode_pid_ent_t))
+static inode_pid_ent_t inode_pid_table[INODE_PID_TABLE_LENGTH];
+
+static uint32_t
+_hash(uint64_t key)
+{
+ key = (~key) + (key << 18);
+ key = key ^ (key >> 31);
+ key = key * 21;
+ key = key ^ (key >> 11);
+ key = key + (key << 6);
+ key = key ^ (key >> 22);
+ return key;
+}
+
+static void
+_clear(void)
+{
+ /* Clear the inode/pid hash table.*/
+ memset(inode_pid_table, 0, INODE_PID_TABLE_SIZE);
+}
+
+static void
+_set(ino64_t inode, pid_t pid)
+{
+ uint32_t hash = _hash(inode);
+ uint32_t i;
+ inode_pid_ent_t *entry;
+
+ /* We will try for a maximum number of collisions.*/
+ for (i = 0; i < INODE_PID_TABLE_MAX_COLLISIONS; i++) {
+ entry = &inode_pid_table[(hash + i) % INODE_PID_TABLE_LENGTH];
+
+ /* Check if this entry is empty, or the actual inode we were looking for.*/
+ /* The second part should never happen, but it is here for completeness.*/
+ if (entry->inode == 0 || entry->inode == inode) {
+ entry->inode = inode;
+ entry->pid = pid;
+ return;
+ }
+ }
+
+ /* We will silently fail to insert the inode if we get too many collisions.*/
+ /* the _get function will return a zero pid.*/
+}
+
+static pid_t _get(ino64_t inode)
+{
+ uint32_t hash = _hash(inode);
+ uint32_t i;
+ inode_pid_ent_t *entry;
+
+ /* We will try for a maximum number of collisions.*/
+ for (i = 0; i < INODE_PID_TABLE_MAX_COLLISIONS; i++) {
+ entry = &inode_pid_table[(hash + i) % INODE_PID_TABLE_LENGTH];
+
+ /* Check if this entry is empty, or the actual inode we were looking for.*/
+ /* If the entry is empty it means the inode is not in the table and we*/
+ /* should return 0, the entry will also have a zero pid.*/
+ if (entry->inode == 0 || entry->inode == inode) {
+ return entry->pid;
+ }
+ }
+
+ /* We could not find the pid.*/
+ return 0;
+}
+
+void
+netsnmp_get_pid_from_inode_init(void)
+{
+ DIR *procdirs = NULL, *piddirs = NULL;
+ char path_name[PATH_MAX + 1];
+ char socket_lnk[NAME_MAX + 1];
+ int filelen = 0, readlen = 0;
+ struct dirent *procinfo, *pidinfo;
+ pid_t pid = 0;
+ ino64_t temp_inode;
+
+ _clear();
+
+ /* walk over all directories in /proc*/
+ if (!(procdirs = opendir(PROC_PATH))) {
+ NETSNMP_LOGONCE((LOG_ERR, "snmpd: cannot open /proc\n"));
+ return;
+ }
+
+ while ((procinfo = readdir(procdirs)) != NULL) {
+ const char* name = procinfo->d_name;
+
+ /* A pid directory only contains digits, check for those.*/
+ for (; *name; name++) {
+ if (!isdigit(*name))
+ break;
+ }
+ if(*name)
+ continue;
+
+ /* Create the /proc/<pid>/fd/ path name.*/
+ memset(path_name, '\0', PATH_MAX + 1);
+ filelen = snprintf(path_name, PATH_MAX,
+ PROC_PATH "/%s/fd/", procinfo->d_name);
+ if (filelen <= 0 || PATH_MAX < filelen)
+ continue;
+
+ /* walk over all the files in /proc/<pid>/fd/*/
+ if (!(piddirs = opendir(path_name)))
+ continue;
+
+ while ((pidinfo = readdir(piddirs)) != NULL) {
+ if (filelen + strlen(pidinfo->d_name) > PATH_MAX)
+ continue;
+
+ strcpy(path_name + filelen, pidinfo->d_name);
+
+ /* The file discriptor is a symbolic link to a socket or a file.*/
+ /* Thus read the symbolic link.*/
+ memset(socket_lnk, '\0', NAME_MAX + 1);
+ readlen = readlink(path_name, socket_lnk, NAME_MAX);
+ if (readlen < 0)
+ continue;
+
+ socket_lnk[readlen] = '\0';
+
+ /* Check if to see if the file descriptor is a socket by comparing*/
+ /* the start to a string. Also extract the inode number from this*/
+ /* symbolic link.*/
+ if (!strncmp(socket_lnk, SOCKET_TYPE_1, 8)) {
+ temp_inode = strtoull(socket_lnk + 8, NULL, 0);
+ } else if (!strncmp(socket_lnk, SOCKET_TYPE_2, 7)) {
+ temp_inode = strtoull(socket_lnk + 7, NULL, 0);
+ } else {
+ temp_inode = 0;
+ }
+
+ /* Add the inode/pid combination to our hash table.*/
+ if (temp_inode != 0) {
+ pid = strtoul(procinfo->d_name, NULL, 0);
+ _set(temp_inode, pid);
+ }
+ }
+ closedir(piddirs);
+ }
+ if (procdirs)
+ closedir(procdirs);
+}
+
+pid_t
+netsnmp_get_pid_from_inode(ino64_t inode)
+{
+ return _get(inode);
+}
+