diff options
Diffstat (limited to 'agent/mibgroup/util_funcs/get_pid_from_inode.c')
-rw-r--r-- | agent/mibgroup/util_funcs/get_pid_from_inode.c | 186 |
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); +} + |