/*
 * fspy - Linux filesystem activity monitor
 *
 * Copyright (C) 2007  Richard Sammet (e-axe) <richard.sammet@gmail.com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <dirent.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <limits.h>

#include "fspy.h"
#include "output.h"
#include "fsevents.h"
#include "stating.h"
#include "diff.h"

/* Get process info from PID (used by fanotify) */
static int get_process_info_from_pid(pid_t pid, process_info_t *info) {
	char path[256];
	struct stat st;

	if (pid <= 0)
		return -1;

	info->pid = pid;

	/* Get command line */
	snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
	int fd = open(path, O_RDONLY);
	if (fd >= 0) {
		ssize_t len = read(fd, info->cmdline, sizeof(info->cmdline) - 1);
		if (len > 0) {
			info->cmdline[len] = '\0';
			/* Replace nulls with spaces for multi-arg commands */
			for (int i = 0; i < len - 1; i++)
				if (info->cmdline[i] == '\0')
					info->cmdline[i] = ' ';
		} else {
			/* If cmdline is empty, try comm */
			close(fd);
			snprintf(path, sizeof(path), "/proc/%d/comm", pid);
			fd = open(path, O_RDONLY);
			if (fd >= 0) {
				len = read(fd, info->cmdline, sizeof(info->cmdline) - 1);
				if (len > 0) {
					info->cmdline[len] = '\0';
					/* Remove trailing newline */
					if (info->cmdline[len-1] == '\n')
						info->cmdline[len-1] = '\0';
				}
			}
		}
		close(fd);
	}

	/* Get UID */
	snprintf(path, sizeof(path), "/proc/%d", pid);
	if (stat(path, &st) == 0)
		info->uid = st.st_uid;

	return 0;
}

void print_data(char *mystrin, struct inotify_event *event,
                const char *event_fpath, struct stat *statdat, struct diffprint *dprint)
{

	char *mystrin_tmp;
	char *mystr;
	char mychar;
	char desc_ptr[128] = {0};
	char *ctp;
	char fpath[ELEMENT_SIZE * 2] = {0};
	char *freeme;

	const char *const normal = "\033[0m";
	const char *const marked = "\033[1;31m";

	time_t currtime;

	snprintf(fpath, (ELEMENT_SIZE * 2), "%s%s", event_fpath, event->name);

	freeme = mystrin_tmp = strdup(mystrin);

	while((mystr = strtok(mystrin_tmp, DELIM))) {
		if(strlen(mystr) == 1) {
			mychar = mystr[0];

			switch(mychar) {
			case  'f':
				printf("%s", event->name);
				break;
			case  'p':
				printf("%s", event_fpath);
				break;
			case  'd':
				memset(desc_ptr, 0, 128 * sizeof(char));
				printf("%s", get_event_desc(event->mask, desc_ptr));
				break;
			case  'w':
				printf("%i", event->wd);
				break;
			case  'c':
				printf("%u", event->cookie);
				break;
			case  'm':
				printf("0x%08x", event->mask);
				break;
			case  'l':
				printf("%u", event->len);
				break;
			case  't':
				memset(desc_ptr, 0, 128 * sizeof(char));
				printf("%s", gettype(fpath, desc_ptr, statdat));
				break;
			/* yeah, i know, thats some kind of dirty style ;)
			   but i dont like the additional newline at EOL... */
			case  'A':
				ctp = ctime(&statdat->st_atime);
				ctp[strlen(ctp) - 1] = '\0';
				(dprint->A == 1)?printf("%s%s%s", marked, ctp, normal):printf("%s", ctp);
				break;
			case  'M':
				ctp = ctime(&statdat->st_mtime);
				ctp[strlen(ctp) - 1] = '\0';
				(dprint->M == 1)?printf("%s%s%s", marked, ctp, normal):printf("%s", ctp);
				break;
			case  'S':
				ctp = ctime(&statdat->st_ctime);
				ctp[strlen(ctp) - 1] = '\0';
				(dprint->S == 1)?printf("%s%s%s", marked, ctp, normal):printf("%s", ctp);
				break;
			case  'T':
				currtime = time(NULL);
				ctp = ctime(&currtime);
				ctp[strlen(ctp) - 1] = '\0';
				printf("%s", ctp);
				break;
			case  's':
				(dprint->s == 1)?printf("%s%lld%s", marked, (long long int) statdat->st_size,
				                        normal):printf("%lld", (long long int) statdat->st_size);
				break;
			case  'U':
				(dprint->U == 1)?printf("%s%ld%s", marked, (long int) statdat->st_uid,
				                        normal):printf("%ld", (long int) statdat->st_uid);
				break;
			case  'G':
				(dprint->G == 1)?printf("%s%ld%s", marked, (long int) statdat->st_gid,
				                        normal):printf("%ld", (long int) statdat->st_gid);
				break;
			case  'O':
				(dprint->O == 1)?printf("%s%lo%s", marked, (unsigned long int) statdat->st_mode,
				                        normal):printf("%lo", (unsigned long int) statdat->st_mode);
				break;
			case  'I':
				(dprint->I == 1)?printf("%s%ld%s", marked, (long int) statdat->st_ino,
				                        normal):printf("%ld", (long int) statdat->st_ino);
				break;
			case  'D':
				(dprint->D == 1)?printf("%s%ld%s", marked, (long int) statdat->st_dev,
				                        normal):printf("%ld", (long int) statdat->st_dev);
				break;
			default  :
				printf("%c", mychar);
				break;
			}
		} else {
			printf("%s", mystr);
		}

		freeme = mystrin_tmp;
		mystrin_tmp = NULL;
	}

	/* Show process info if enabled - PID will be set by fanotify */
	if (show_process && event->wd > 0) {
		/* For fanotify, event->wd contains the PID */
		process_info_t pinfo = {0};

		if (get_process_info_from_pid(event->wd, &pinfo) == 0) {
			printf(" [PID:%d UID:%d CMD:%s]", pinfo.pid, pinfo.uid, pinfo.cmdline);
		} else {
			printf(" [PID:unknown]");
		}
	}

	printf("\n");

	free(freeme);
}
