Chris PeBenito 473ea7
/*
Chris PeBenito 473ea7
 * avcstat - Display SELinux avc statistics.
Chris PeBenito 473ea7
 *
Chris PeBenito 473ea7
 * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
Chris PeBenito 473ea7
 *
Chris PeBenito 473ea7
 * This program is free software; you can redistribute it and/or modify
Chris PeBenito 473ea7
 * it under the terms of the GNU General Public License version 2,
Chris PeBenito 473ea7
 * as published by the Free Software Foundation.
Chris PeBenito 473ea7
 *
Chris PeBenito 473ea7
 */
Chris PeBenito 473ea7
#include <stdio.h>
Chris PeBenito 473ea7
#include <stdlib.h>
Chris PeBenito 473ea7
#include <libgen.h>
Chris PeBenito 473ea7
#include <stdarg.h>
Chris PeBenito 473ea7
#include <errno.h>
Chris PeBenito 473ea7
#include <string.h>
Chris PeBenito 473ea7
#include <fcntl.h>
Chris PeBenito 473ea7
#include <unistd.h>
Chris PeBenito 473ea7
#include <signal.h>
Chris PeBenito 473ea7
#include <sys/types.h>
Chris PeBenito 473ea7
#include <sys/stat.h>
Chris PeBenito 473ea7
#include <sys/ioctl.h>
Chris PeBenito 473ea7
#include <linux/limits.h>
Chris PeBenito 473ea7
Chris PeBenito 473ea7
#define DEF_STAT_FILE	"/avc/cache_stats"
Chris PeBenito 473ea7
#define DEF_BUF_SIZE	8192
Chris PeBenito 473ea7
#define HEADERS		"lookups hits misses allocations reclaims frees"
Chris PeBenito 473ea7
Chris PeBenito 473ea7
struct avc_cache_stats {
Chris PeBenito 473ea7
	unsigned int lookups;
Chris PeBenito 473ea7
	unsigned int hits;
Chris PeBenito 473ea7
	unsigned int misses;
Chris PeBenito 473ea7
	unsigned int allocations;
Chris PeBenito 473ea7
	unsigned int reclaims;
Chris PeBenito 473ea7
	unsigned int frees;
Chris PeBenito 473ea7
};
Chris PeBenito 473ea7
Chris PeBenito 473ea7
static int interval;
Chris PeBenito 473ea7
static int rows;
Chris PeBenito 473ea7
static char *progname;
Chris PeBenito 473ea7
static char buf[DEF_BUF_SIZE];
Chris PeBenito 473ea7
Chris PeBenito 473ea7
/* selinuxfs mount point */
Chris PeBenito 473ea7
extern char *selinux_mnt;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
Chris PeBenito 473ea7
static void die(const char *msg, ...)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	va_list args;
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	fputs("ERROR: ", stderr);
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	va_start(args, msg);
Chris PeBenito 473ea7
	vfprintf(stderr, msg, args);
Chris PeBenito 473ea7
	va_end(args);
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	if (errno)
Chris PeBenito 473ea7
		fprintf(stderr, ": %s", strerror(errno));
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
	fputc('\n', stderr);
Chris PeBenito 473ea7
	exit(1);
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
static void usage(void)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	printf("\nUsage: %s [-c] [-f status_file] [interval]\n\n", progname);
Chris PeBenito 473ea7
	printf("Display SELinux AVC statistics.  If the interval parameter is specified, the\n");
Chris PeBenito 473ea7
	printf("program will loop, displaying updated statistics every \'interval\' seconds.\n");
Chris PeBenito 473ea7
	printf("Relative values are displayed by default. Use the -c option to specify the\n");
Chris PeBenito 473ea7
	printf("display of cumulative values.  The -f option specifies the location of the\n");
Chris PeBenito 473ea7
	printf("AVC statistics file, defaulting to \'%s%s\'.\n\n", selinux_mnt, DEF_STAT_FILE);
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
static void set_window_rows(void)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	int ret;
Chris PeBenito 473ea7
	struct winsize ws;
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	ret = ioctl(fileno(stdout), TIOCGWINSZ, &ws);
Chris PeBenito 473ea7
	if (ret < 0 || ws.ws_row < 3)
Chris PeBenito 473ea7
		ws.ws_row = 24;
Chris PeBenito 473ea7
	rows = ws.ws_row;
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
static void sighandler(int num)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	if (num == SIGWINCH)
Chris PeBenito 473ea7
		set_window_rows();
Chris PeBenito 473ea7
}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
int main(int argc, char **argv)
Chris PeBenito 473ea7
{
Chris PeBenito 473ea7
	struct avc_cache_stats tot, rel, last;
Chris PeBenito 473ea7
	int fd, i, cumulative = 0;
Chris PeBenito 473ea7
	struct sigaction sa;
Chris PeBenito 473ea7
	char avcstatfile[PATH_MAX];
Chris PeBenito 473ea7
	snprintf(avcstatfile, sizeof avcstatfile, "%s%s", selinux_mnt, DEF_STAT_FILE);
Chris PeBenito 473ea7
	progname = basename(argv[0]);
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	memset(&last, 0, sizeof(last));
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
	while((i = getopt(argc, argv, "cf:h?-")) != -1) {
Chris PeBenito 473ea7
		switch (i) {
Chris PeBenito 473ea7
		case 'c':
Chris PeBenito 473ea7
			cumulative = 1;
Chris PeBenito 473ea7
			break;
Chris PeBenito 473ea7
		case 'f':
Chris PeBenito 473ea7
			strncpy(avcstatfile, optarg, sizeof avcstatfile);
Chris PeBenito 473ea7
			break;
Chris PeBenito 473ea7
		case 'h':
Chris PeBenito 473ea7
		case '-':
Chris PeBenito 473ea7
			usage();
Chris PeBenito 473ea7
			exit(0);
Chris PeBenito 473ea7
		default:
Chris PeBenito 473ea7
			usage();
Chris PeBenito 473ea7
			die("unrecognized parameter", i);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	if (optind < argc) {
Chris PeBenito 473ea7
		char *arg = argv[optind];
Chris PeBenito 473ea7
		unsigned int n = strtoul(arg, NULL, 10);
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
		if (errno == ERANGE) {
Chris PeBenito 473ea7
			usage();
Chris PeBenito 473ea7
			die("invalid interval \'%s\'", arg);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
		if (n == 0) {
Chris PeBenito 473ea7
			usage();
Chris PeBenito 473ea7
			exit (0);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
		interval = n;
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	sa.sa_handler = sighandler;
Chris PeBenito 473ea7
	sa.sa_flags = SA_RESTART;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	i = sigaction(SIGWINCH, &sa, NULL);
Chris PeBenito 473ea7
	if (i < 0)
Chris PeBenito 473ea7
		die("sigaction");
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	set_window_rows();
Chris PeBenito 473ea7
	fd = open(avcstatfile, O_RDONLY);
Chris PeBenito 473ea7
	if (fd < 0)
Chris PeBenito 473ea7
		die("open: \'%s\'", avcstatfile);
Chris PeBenito 473ea7
	
Chris PeBenito 473ea7
	for (i = 0;; i++) {
Chris PeBenito 473ea7
		char *line;
Chris PeBenito 473ea7
		ssize_t ret, parsed = 0;
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
		memset(buf, 0, DEF_BUF_SIZE);
Chris PeBenito 473ea7
		ret = read(fd, buf, DEF_BUF_SIZE);
Chris PeBenito 473ea7
		if (ret < 0)
Chris PeBenito 473ea7
			die("read");
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
		if (ret == 0)
Chris PeBenito 473ea7
			die("read: \'%s\': unexpected end of file", avcstatfile);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		line = strtok(buf, "\n");
Chris PeBenito 473ea7
		if (!line)
Chris PeBenito 473ea7
			die("unable to parse \'%s\': end of line not found", avcstatfile); 
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		if (strcmp(line, HEADERS))
Chris PeBenito 473ea7
			die("unable to parse \'%s\': invalid headers", avcstatfile);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		if (!i || !(i % (rows - 2)))
Chris PeBenito 473ea7
			printf("%10s %10s %10s %10s %10s %10s\n", "lookups",
Chris PeBenito 473ea7
			       "hits", "misses", "allocs", "reclaims", "frees");
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		memset(&tot, 0, sizeof(tot));
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
		while ((line = strtok(NULL, "\n"))) {
Chris PeBenito 473ea7
			struct avc_cache_stats tmp;
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
			ret = sscanf(line, "%u %u %u %u %u %u",
Chris PeBenito 473ea7
				     &tmp.lookups,
Chris PeBenito 473ea7
				     &tmp.hits,
Chris PeBenito 473ea7
				     &tmp.misses,
Chris PeBenito 473ea7
				     &tmp.allocations,
Chris PeBenito 473ea7
				     &tmp.reclaims,
Chris PeBenito 473ea7
				     &tmp.frees);
Chris PeBenito 473ea7
			if (ret != 6)
Chris PeBenito 473ea7
				die("unable to parse \'%s\': scan error", avcstatfile);
Chris PeBenito 473ea7
			
Chris PeBenito 473ea7
			tot.lookups += tmp.lookups;
Chris PeBenito 473ea7
			tot.hits += tmp.hits;
Chris PeBenito 473ea7
			tot.misses += tmp.misses;
Chris PeBenito 473ea7
			tot.allocations += tmp.allocations;
Chris PeBenito 473ea7
			tot.reclaims += tmp.reclaims;
Chris PeBenito 473ea7
			tot.frees += tmp.frees;
Chris PeBenito 473ea7
			parsed = 1;
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		if (!parsed)
Chris PeBenito 473ea7
			die("unable to parse \'%s\': no data", avcstatfile);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		if (cumulative || (!cumulative && !i))
Chris PeBenito 473ea7
			printf("%10u %10u %10u %10u %10u %10u\n",
Chris PeBenito 473ea7
			       tot.lookups, tot.hits, tot.misses,
Chris PeBenito 473ea7
			       tot.allocations, tot.reclaims, tot.frees);
Chris PeBenito 473ea7
		else {
Chris PeBenito 473ea7
			rel.lookups = tot.lookups - last.lookups;
Chris PeBenito 473ea7
			rel.hits = tot.hits - last.hits;
Chris PeBenito 473ea7
			rel.misses = tot.misses - last.misses;
Chris PeBenito 473ea7
			rel.allocations = tot.allocations - last.allocations;
Chris PeBenito 473ea7
			rel.reclaims = tot.reclaims - last.reclaims;
Chris PeBenito 473ea7
			rel.frees = tot.frees - last.frees;
Chris PeBenito 473ea7
			printf("%10u %10u %10u %10u %10u %10u\n",
Chris PeBenito 473ea7
			       rel.lookups, rel.hits, rel.misses,
Chris PeBenito 473ea7
			       rel.allocations, rel.reclaims, rel.frees);
Chris PeBenito 473ea7
		}
Chris PeBenito 473ea7
		
Chris PeBenito 473ea7
		if (!interval)
Chris PeBenito 473ea7
			break;
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		memcpy(&last, &tot, sizeof(last));
Chris PeBenito 473ea7
		sleep(interval);
Chris PeBenito 473ea7
Chris PeBenito 473ea7
		ret = lseek(fd, 0, 0);
Chris PeBenito 473ea7
		if (ret < 0)
Chris PeBenito 473ea7
			die("lseek");
Chris PeBenito 473ea7
	}
Chris PeBenito 473ea7
Chris PeBenito 473ea7
	close(fd);
Chris PeBenito 473ea7
	return 0;
Chris PeBenito 473ea7
}