#include "common.h"

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

char *read_file(const char *path)
{
	const size_t maximum = 1U << 20;
	struct stat status;
	char *data;
	size_t capacity;
	size_t used = 0;
	int fd = open(path, O_RDONLY | O_CLOEXEC);

	if (fd < 0 || fstat(fd, &status) != 0 || status.st_size < 0 ||
	    (size_t)status.st_size > maximum) {
		if (fd >= 0)
			close(fd);
		return NULL;
	}
	capacity = status.st_size > 0 ? (size_t)status.st_size : 4096;
	data = malloc(capacity + 1);
	if (!data) {
		close(fd);
		return NULL;
	}
	for (;;) {
		ssize_t count;

		if (used == capacity) {
			size_t next = capacity < maximum / 2 ? capacity * 2 : maximum;
			char *grown;

			if (capacity == maximum)
				break;
			grown = realloc(data, next + 1);
			if (!grown) {
				free(data);
				close(fd);
				return NULL;
			}
			data = grown;
			capacity = next;
		}
		count = read(fd, data + used, capacity - used);

		if (count > 0) {
			used += (size_t)count;
			continue;
		}
		if (count < 0 && errno == EINTR)
			continue;
		if (count < 0) {
			free(data);
			close(fd);
			return NULL;
		}
		break;
	}
	close(fd);
	data[used] = '\0';
	return data;
}

int selinux_enforcing(void)
{
	char value;
	int fd = open("/sys/fs/selinux/enforce", O_RDONLY | O_CLOEXEC);

	if (fd < 0)
		return -1;
	if (read(fd, &value, 1) != 1) {
		close(fd);
		return -1;
	}
	close(fd);
	return value == '1';
}

bool process_running(const char *needle)
{
	DIR *proc = opendir("/proc");
	struct dirent *entry;

	if (!proc)
		return false;
	while ((entry = readdir(proc)) != NULL) {
		char path[128];
		char command[512];
		char *end;
		ssize_t count;
		int fd;

		if (entry->d_name[0] < '0' || entry->d_name[0] > '9')
			continue;
		(void)strtol(entry->d_name, &end, 10);
		if (*end != '\0')
			continue;
		snprintf(path, sizeof(path), "/proc/%s/cmdline", entry->d_name);
		fd = open(path, O_RDONLY | O_CLOEXEC);
		if (fd < 0)
			continue;
		count = read(fd, command, sizeof(command) - 1);
		close(fd);
		if (count <= 0)
			continue;
		command[count] = '\0';
		if (strstr(command, needle)) {
			closedir(proc);
			return true;
		}
	}
	closedir(proc);
	return false;
}

bool status_has(const char *status, const char *field, const char *value)
{
	const char *at = strstr(status, field);

	if (!at)
		return false;
	at += strlen(field);
	while (*at == ' ' || *at == '\t')
		at++;
	return strncmp(at, value, strlen(value)) == 0;
}
