sigar/exp/linux_pid_portmap.c

212 lines
4.3 KiB
C

#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
/*
* eam can provide per-process measurements, but at the moment requires
* a pid. something we cannot store in the database. however, we could
* store a service port in the database (e.g. 8009 for tomcat ajp) and
* figure out the pid given that.
*
* it is possible via /proc/net/tcp and /proc/$pid/fd/
* to map a tcp port to one (or more if forked) process id(s).
* caveats: expensive to scan /proc/$pid/fd
* must own process or be root to read /proc/$pid/fd
* brutal to provide similar functionality on other platforms,
* tho possible (see lsof).
*/
static char *sigar_skip_token(char *p)
{
while (isspace(*p)) p++;
while (*p && !isspace(*p)) p++;
return p;
}
static char *sigar_skip_multiple_token(char *p, int count)
{
int i;
for (i = 0; i < count; i++) {
p = sigar_skip_token(p);
}
return p;
}
static int get_process_ppid(char *pid, char *buf, int buflen)
{
FILE *fp;
char buffer[1024], *ptr, *ptr2;
sprintf(buffer, "/proc/%s/stat", pid);
if (!(fp = fopen(buffer, "r"))) {
return errno;
}
ptr = fgets(buffer, sizeof(buffer), fp);
fclose(fp);
ptr = sigar_skip_multiple_token(ptr, 3);
while (isspace(*ptr)) {
++ptr;
}
ptr2 = strchr(ptr, ' ');
*ptr2 = '\0';
strncpy(buf, ptr2, buflen);
buf[buflen] = '\0';
return 0;
}
typedef struct {
int port;
int inode;
int uid;
int pid;
char name[1024];
} portmap_t;
static int portmap_lookup_inode(portmap_t *map) {
FILE *fp;
char buffer[8192], *ptr;
int inode = 0;
if ((fp = fopen("/proc/net/tcp", "r")) < 0) {
return -1;
}
fgets(buffer, sizeof(buffer), fp); /* skip header */
while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
ptr = sigar_skip_token(ptr);
if ((ptr = strchr(ptr, ':'))) {
unsigned long port = strtoul(++ptr, &ptr, 16);
if (!(map->port == (port & 0xffff))) {
continue;
}
}
ptr = sigar_skip_multiple_token(ptr, 5);
map->uid = strtoul(ptr, &ptr, 10);
ptr = sigar_skip_token(ptr);
inode = map->inode = strtoul(ptr, &ptr, 10);
break;
}
fclose(fp);
return inode ? 0 : ENOENT;
}
static int portmap_lookup_pid(portmap_t *map, char *pid) {
DIR *dirp;
struct dirent *dp;
char fname[1024], dname[1024];
map->pid = -1;
sprintf(dname, "/proc/%s/fd", pid);
if (!(dirp = opendir(dname))) {
return errno;
}
while ((dp = readdir(dirp))) {
struct stat statbuf;
if (!isdigit(*dp->d_name)) {
continue;
}
sprintf(fname, "%s/%s", dname, dp->d_name);
//printf("check %s\n", fname);
if (stat(fname, &statbuf)) {
continue;
}
if (statbuf.st_ino == map->inode) {
map->pid = atoi(pid);
break;
}
}
closedir(dirp);
return map->pid == -1 ? ENOENT : 0;
}
static int portmap_lookup_pid_scan(portmap_t *map) {
DIR *dirp;
struct dirent *dp;
char dname[1024];
if (!(dirp = opendir("/proc"))) {
return -1;
}
map->pid = -1;
while ((dp = readdir(dirp))) {
struct stat statbuf;
if (!isdigit(*dp->d_name)) {
continue;
}
sprintf(dname, "/proc/%s", dp->d_name);
if (stat(dname, &statbuf)) {
continue;
}
if (!(statbuf.st_uid == map->uid)) {
continue;
}
if (portmap_lookup_pid(map, dp->d_name) == 0) {
#if 0
char *pid = dp->d_name;
char ppid[16];
get_process_ppid(pid, ppid, sizeof(ppid));
#endif
//break;
printf("%s has inode %d\n", dp->d_name, map->inode);
}
}
closedir(dirp);
return map->pid == -1 ? ENOENT : 0;
}
int main(int argc, char **argv) {
portmap_t map;
if (argc != 2) {
printf("usage: %s port\n", argv[0]);
}
map.port = atoi(argv[1]);
if (portmap_lookup_inode(&map) == 0) {
if (portmap_lookup_pid_scan(&map) == 0) {
printf("inode=%d, pid=%d\n", map.inode, map.pid);
}
}
return 1;
}