#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <pwd.h>

typedef struct waste_pid {
	pid_t pid;
	struct waste_pid *next;
} waste_pid;

typedef struct waste {
	waste_pid *pids;
	unsigned long inode;
	unsigned long size;
	char *path;
	struct waste *next;
} waste;

#define NBHASH 256
waste *wastes[NBHASH];
#define hash_waste(inode) (wastes[(inode)%NBHASH])

int main(void) {
	int fds[2];
	if (pipe(fds)<0) {
		perror("pipe");
		exit(EXIT_FAILURE);
	}
	if (!fork()) {
		close(fds[0]);
		if (dup2(fds[1],STDOUT_FILENO)<0) {
			perror("dup2(fds[1])");
			exit(EXIT_FAILURE);
		}
		if (system("grep '(deleted)' /proc/*/maps")<0)
			perror("system");
		exit(0);
	} else {
		pid_t pid;
		unsigned long start, stop, size;
		unsigned long inode;
		char write;
		char path[128];
		waste *waste;
		waste_pid *waste_pid;
		int i;
		int fd;
		ssize_t res;
		struct stat buf;
		char path2[17];
		struct passwd* passwd;

		close(fds[1]);
		dup2(fds[0],STDIN_FILENO);
		size = 0;
		while(scanf("/proc/%d/maps:%lx-%lx %*c%c%*c%*c %*x %*x:%*x %ld %128s%*[^\n]",
				&pid,&start,&stop,&write,&inode,path) == 6) {
			getchar();
			if (write!='-')
				// would be allocated anyway...
				continue;
			if (!strcmp(path,"/SYSV00000000"))
				continue;
			//printf("%d: %lx-%lx, %c, %ld, %s\n",pid,start,stop,write,inode,path);
			path[sizeof(path)-1]='\0';
			waste_pid = malloc(sizeof(*waste_pid));
			waste_pid->pid = pid;
			for (waste = hash_waste(inode);
			     waste && waste->inode != inode;
			     waste = waste->next);
			if (waste) {
				waste_pid->next = waste->pids;
				waste->pids = waste_pid;
			} else {
				waste = malloc(sizeof(*waste));
				waste->pids = waste_pid;
				waste->inode = inode;
				size += (waste->size = (stop-start)/1024);
				waste->path = strdup(path);
				waste->next = hash_waste(inode);
				hash_waste(inode) = waste;
			}
		}
		printf("%luKB total:\n",size);
		for (i=0;i<NBHASH;i++) {
			for (waste = wastes[i]; waste; waste = waste->next) {
				printf("%s: %luKB\n", waste->path, waste->size);
				for (waste_pid = waste->pids; waste_pid; waste_pid = waste_pid->next) {
					snprintf(path,sizeof(path),"/proc/%u/cmdline",waste_pid->pid);
					if ((fd = open(path,O_RDONLY))<0)
						perror("open");
					else {
						res = read(fd,path,sizeof(path)-1);
						close(fd);
						path[res]='\0';
						snprintf(path2,sizeof(path2),"/proc/%u",waste_pid->pid);
						if (stat(path2,&buf)) perror("stat");
						else if (!(passwd = getpwuid(buf.st_uid))) perror("getpwuid");
						else printf("  %u(%s): %s\n",waste_pid->pid,passwd->pw_name,path);
					}
				}
			}
		}
		return 0;
	}
}
