/* $Id$ */

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

static char *get_proc_path(pid_t pid, int fd, int fdinfo)
{
	static char buf[64];

	snprintf(buf, sizeof(buf), "/proc/%d/fd%s", pid, fdinfo ? "info" : "");
	if (fd != -1)
		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "/%d", fd);

	return buf;
}

static int dump_proc(pid_t pid)
{
	DIR *d;
	struct dirent *de;
	char *path;

	path = get_proc_path(pid, -1, 0);
	d = opendir(path);
	if (!d)
	{
		fprintf(stderr, "Nie można otworzyć katalogu %s: %s\n", path, strerror(errno));
		return EXIT_FAILURE;
	}

	printf("Otwarte deskryptory procesu %d:\n", pid);

	while ((de = readdir(d)))
	{
		char buf[PATH_MAX];
		int fd;

		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
			continue;

		fd = atoi(de->d_name);
		memset(buf, 0, sizeof(buf));
		path = get_proc_path(pid, fd, 0);
		if (readlink(path, buf, sizeof(buf) - 1) == -1)
		{
			fprintf(stderr, "readlink(%s): %s\n", path, strerror(errno));
			closedir(d);
			return EXIT_FAILURE;
		}

		printf("%5d: %s\n", fd, buf);
	}

	closedir(d);
	return EXIT_SUCCESS;
}

static int do_fd(pid_t pid, int fd)
{
	for (;;)
	{
		struct stat st;
		char *path;
		FILE *fp;
		char buf[64];
		off_t pos = 0;

		path = get_proc_path(pid, fd, 0);
		if (stat(path, &st) == -1)
		{
			if (errno == ENOENT)
				break;

			fprintf(stderr, "stat(%s): %s\n", path, strerror(errno));
			return EXIT_FAILURE;
		}

		path = get_proc_path(pid, fd, 1);
		fp = fopen(path, "r");
		if (!fp)
		{
			if (errno == ENOENT)
			{
				if (!(fp = fopen(get_proc_path(getpid(), 0, 1), "r")) && errno == ENOENT)
				{
					printf("Masz za stare jądro, musisz uaktualnić.\n");
					return EXIT_FAILURE;
				}

				fclose(fp);
				break;
			}

			fprintf(stderr, "fopen(%s): %s\n", path, strerror(errno));
			return EXIT_FAILURE;
		}

		for (;;)
		{
			fgets(buf, sizeof(buf), fp);
			if (feof(fp))
			{
				printf("Masz uszkodzone jądro.\n");
				fclose(fp);
				return EXIT_FAILURE;
			}

			if (!strncmp(buf, "pos:\t", 5))
			{
				if (sscanf(buf + 5, "%lu", &pos) != 1)
				{
					printf("Masz uszkodzone jądro - linia z pozycją: %s", buf);
					fclose(fp);
					return EXIT_FAILURE;
				}

				break;
			}
		}

		if (!st.st_size)
			printf("pos=%lu, size=0\n", pos);
		else
			printf("pos=%lu, size=%lu, progress=%f%%\n", pos, st.st_size, (float) pos * 100 / st.st_size);

		sleep(1);
	}

	return EXIT_SUCCESS;
}

int main(int ac, char * const av[])
{
	pid_t pid;
	int fd;

	if (ac < 2)
	{
		printf("fpstat (C) 2008 Adam Wysocki - http://www.chmurka.net/\n");
		printf("Program wypisuje informacje na temat postępu w czytaniu pliku \n");
		printf("przez inny proces.\n");
		printf("\n");
		printf("Użycie: fpstat <pid> [<fd>]\n");

		return EXIT_SUCCESS;
	}

	pid = atoi(av[1]);
	if (kill(pid, 0) == -1 && errno == ESRCH)
	{
		fprintf(stderr, "Proces %d nie istnieje.\n", pid);
		return EXIT_FAILURE;
	}

	if (ac < 3)
		return dump_proc(pid);

	fd = atoi(av[2]);
	return do_fd(pid, fd);
}
