#include #include "get_pid_from_inode.h" #include /* strlcpy() */ #include #include #include #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #else #include #endif #if HAVE_UNISTD_H #include #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//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//fd/*/ if (!(piddirs = opendir(path_name))) continue; while ((pidinfo = readdir(piddirs)) != NULL) { if (filelen + strlen(pidinfo->d_name) > PATH_MAX) continue; strlcpy(path_name + filelen, pidinfo->d_name, sizeof(path_name) - filelen); /* 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); }