/*
 * eigther:
 * gcc checkfd.c
 * or
 * gcc checkfd.c -lcap -DUSE_CAP
 *
 * then:
 * sudo chown root.adm a.out && sudo chmod +s a.out
 *
 * you should probably limit it to only users in the adm group:
 * chmod o= a.out
 *
 * Andreas Henriksson <andreas@fatal.se>, 2007-09-06
 */

#include <stdio.h>

#include <sys/types.h>
#include <dirent.h>

#include <stdlib.h>
#include <string.h>

#ifdef USE_CAP
#include <sys/capability.h>
#include <errno.h>
#endif

int setcaps();

int main(int argc, char **argv)
{
	DIR *procdir = opendir("/proc");
	struct dirent *proc_ent;

	setcaps();

	printf("<process id>:<number of open filedescriptors>\n");
	printf("---------------------------------------------\n");
	while ((proc_ent = readdir(procdir)) != NULL) {
		char *name = proc_ent->d_name;
		int tmppid = atoi(name);

		/* not a pid proc_entectory? */
		if (proc_ent->d_type != DT_DIR || tmppid < 3)
			continue;
		
		char path[1024];
		strcpy(path, "/proc/");
		strcat(path, name);
		strcat(path, "/fd/");
		DIR *piddir = opendir(path);
		struct dirent *pid_ent;

		if (!piddir) {
			fprintf(stderr, "Error opening %s\n", path);
			exit(2);
		}

		int numfds = 0;
		//printf("%s: ", path);
		while ((pid_ent = readdir(piddir)) != NULL) {
			if (strcmp(pid_ent->d_name, ".") == 0
					|| strcmp(pid_ent->d_name, "..") == 0)
				continue;

			//printf("%s, ", pid_ent->d_name);
			numfds++;
		}
		//printf("done.\n");
		printf("%s:%d\n", name, numfds);
		
		closedir(piddir);

	}

	closedir(procdir);

	return 0;
}

#ifdef USE_CAP
int setcaps() {
	cap_flag_t cap_flag = CAP_PERMITTED;
	cap_t caps;
	const unsigned caps_size = 1;
	cap_value_t cap_list[] = { CAP_SYS_ADMIN };
	
	caps=cap_get_proc();
	
	if (!caps) {
		printf("set_capabilities(): failed to get caps: %s.", strerror(errno));
		return -1;
	}

	
	cap_set_flag(caps, cap_flag, caps_size, cap_list , CAP_SET);
	
	if (cap_set_proc(caps))  {
		printf("set_capabilities(): failed to set caps: %s.", strerror(errno));
		return -2;
	}
	return 0;
}

#else
int setcaps() {
	setuid(0);
	setgid(0);
	seteuid(0);
	setegid(0);
	return 0;
}
#endif
