
/*
	TODO:
	get rid of the compiler warnings:

fl_wrapper.c: In function '_Exit':
fl_wrapper.c:1100: warning: 'noreturn' function does return
fl_wrapper.c: In function '_exit':
fl_wrapper.c:1075: warning: 'noreturn' function does return
fl_wrapper.c: In function 'exit':
fl_wrapper.c:1050: warning: 'noreturn' function does return

	maybe TODO:
	wrape clone(); catch termination of child process created with clone()
*/

#  if FD_TRACKER == 1

/*
	
	Whenever a new file descriptor is opened, it will/should be added to the
	fl_wrapper-internal register, together with its current file status.
	Accordingly, when a file descriptor is closed, it will be removed from this
	register, and handle_file_access_after() will be called with the stored
	filename and status.
*/

struct pid_reg
{
	pid_t id;
	int executed;
	struct fd_reg *fd_head;
	struct pid_reg *next;
};

struct pid_reg *pid_head = 0;

struct fd_reg
{
	int fd;
	int closed;
	struct status_t status;
	char *filename;
	struct fd_reg *next;
};

void add_pid(pid_t pid)
{
	struct pid_reg *newpid = malloc(sizeof(struct pid_reg));

	newpid->id = pid;
	newpid->fd_head = 0;
	newpid->executed = 0;

	newpid->next = pid_head;
	pid_head = newpid;

#  if DEBUG == 1
	if (debug) fprintf(stderr, "fl_wrapper.so debug [%d]: PID %d added.\n",
		getpid(), pid);
#  endif
}

void add_fd(struct pid_reg *pid, int fd, const char *filename, struct status_t *status)
{
	struct fd_reg *newfd = malloc(sizeof(struct fd_reg));

	newfd->fd = fd;
	newfd->closed = 0;
	newfd->status = *status;
	newfd->filename = strdup(filename);

	newfd->next = pid->fd_head;
	pid->fd_head = newfd;

#  if DEBUG == 1
	if (debug) fprintf(stderr, "fl_wrapper.so debug [%d]: fd %d (%s) added.\n",
		getpid(), fd, filename);
#  endif
}

void remove_fd(struct fd_reg **fd);

void remove_pid(struct pid_reg **pid)
{
	struct pid_reg *tmp = *pid;
	struct fd_reg **fd_iter = &(*pid)->fd_head; 

#  if DEBUG == 1
	if (debug) fprintf(stderr, "fl_wrapper.so debug [%d]: PID %d removed.\n",
		getpid(), (*pid)->id);
#  endif

	while (*fd_iter) remove_fd(fd_iter);
	*pid = (*pid)->next;
	free(tmp);
}

void remove_fd(struct fd_reg **fd)
{
	struct fd_reg *tmp = *fd;

#  if DEBUG == 1
	if (debug) fprintf(stderr, "fl_wrapper.so debug [%d]: fd %d (%s) removed.\n",
		getpid(), (*fd)->fd, (*fd)->filename);
#  endif

	free((*fd)->filename);
	*fd = (*fd)->next;
	free(tmp);
}

void deregister_fd(int fd);

struct pid_reg **find_pid(pid_t pid)
{
	struct pid_reg **pid_iter = &pid_head;

	while (*pid_iter)
	{
		if ((*pid_iter)->executed)
		{
			struct fd_reg **fd_iter = &(*pid_iter)->fd_head;
			while (*fd_iter)
			{
				if ((*fd_iter)->closed)
				{
					handle_file_access_after("exec*", (*fd_iter)->filename, &(*fd_iter)->status);
					deregister_fd((*fd_iter)->fd);

				}
				else fd_iter = &(*fd_iter)->next;
			}
		}

		if ((*pid_iter)->fd_head == 0) remove_pid(pid_iter);
		else pid_iter = &(*pid_iter)->next;
	}

	pid_iter = &pid_head;
	while (*pid_iter)
	{
		if ((*pid_iter)->id == pid) break;
		pid_iter = &(*pid_iter)->next;
	}

	return pid_iter;
}

struct fd_reg **find_fd(struct pid_reg *pid, int fd)
{
	struct fd_reg **fd_iter = &pid->fd_head;
	while (*fd_iter)
	{
		if ((*fd_iter)->fd == fd) break;
		fd_iter = &(*fd_iter)->next;
	}

	return fd_iter;
}

void register_fd(int fd, const char *filename, struct status_t *status)
{
	struct pid_reg *pid_iter = *find_pid(getpid());
	if (pid_iter == 0)
	{
		add_pid(getpid());
		pid_iter = pid_head;
	}

	struct fd_reg *fd_iter = *find_fd(pid_iter, fd);
	if (fd_iter == 0)
	{
		add_fd(pid_iter, fd, filename, status);
	} else {
#  if DEBUG == 1
		if (debug) fprintf(stderr, "fl_wrapper.so debug [%d]: fd %d already registered (is %s, was %s).\n",
			getpid(), fd, filename, fd_iter->filename);
#  endif
		free(fd_iter->filename);
		fd_iter->filename = strdup(filename);
		fd_iter->status = *status;
	}
}

/* removes fd from register, returning its filename and status via filename
   and status arguments.
*/
void deregister_fd(int fd)
{
	struct pid_reg **pid_iter = find_pid(getpid());
	if (*pid_iter)
	{
		struct fd_reg **fd_iter = find_fd(*pid_iter, fd);
		if (*fd_iter)
		{
			remove_fd(fd_iter);
			if ((*pid_iter)->fd_head == 0) remove_pid(pid_iter);
		}
	}
}

void copy_fds(pid_t parent, pid_t child)
{
	struct pid_reg *pid_iter = *find_pid(parent);
	if (pid_iter)
	{
		struct fd_reg *fd_iter = pid_iter->fd_head;

		add_pid(child);
		struct fd_reg **nextfd = &pid_head->fd_head;

		while (fd_iter)
		{
			*nextfd = malloc(sizeof(struct fd_reg));
			**nextfd = *fd_iter;
			nextfd = &(*nextfd)->next;
			fd_iter = fd_iter->next;
		}
	}
}

#  endif
