Home | History | Annotate | Download | only in utils
      1 /*
      2  * avcstat - Display SELinux avc statistics.
      3  *
      4  * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris (at) redhat.com>
      5  *
      6  * This program is free software; you can redistribute it and/or modify
      7  * it under the terms of the GNU General Public License version 2,
      8  * as published by the Free Software Foundation.
      9  *
     10  */
     11 #include <stdio.h>
     12 #include <stdlib.h>
     13 #include <libgen.h>
     14 #include <stdarg.h>
     15 #include <errno.h>
     16 #include <string.h>
     17 #include <fcntl.h>
     18 #include <unistd.h>
     19 #include <signal.h>
     20 #include <sys/types.h>
     21 #include <sys/stat.h>
     22 #include <sys/ioctl.h>
     23 #include <linux/limits.h>
     24 
     25 #define DEF_STAT_FILE	"/avc/cache_stats"
     26 #define DEF_BUF_SIZE	8192
     27 #define HEADERS		"lookups hits misses allocations reclaims frees"
     28 
     29 struct avc_cache_stats {
     30 	unsigned long long lookups;
     31 	unsigned long long hits;
     32 	unsigned long long misses;
     33 	unsigned long long allocations;
     34 	unsigned long long reclaims;
     35 	unsigned long long frees;
     36 };
     37 
     38 static int interval;
     39 static int rows;
     40 static char *progname;
     41 static char buf[DEF_BUF_SIZE];
     42 
     43 /* selinuxfs mount point */
     44 extern char *selinux_mnt;
     45 
     46 static __attribute__((__format__(printf,1,2),__noreturn__)) void die(const char *msg, ...)
     47 {
     48 	va_list args;
     49 
     50 	fputs("ERROR: ", stderr);
     51 
     52 	va_start(args, msg);
     53 	vfprintf(stderr, msg, args);
     54 	va_end(args);
     55 
     56 	if (errno)
     57 		fprintf(stderr, ": %s", strerror(errno));
     58 
     59 	fputc('\n', stderr);
     60 	exit(1);
     61 }
     62 
     63 static void usage(void)
     64 {
     65 	printf("\nUsage: %s [-c] [-f status_file] [interval]\n\n", progname);
     66 	printf
     67 	    ("Display SELinux AVC statistics.  If the interval parameter is specified, the\n");
     68 	printf
     69 	    ("program will loop, displaying updated statistics every \'interval\' seconds.\n");
     70 	printf
     71 	    ("Relative values are displayed by default. Use the -c option to specify the\n");
     72 	printf
     73 	    ("display of cumulative values.  The -f option specifies the location of the\n");
     74 	printf("AVC statistics file, defaulting to \'%s%s\'.\n\n", selinux_mnt,
     75 	       DEF_STAT_FILE);
     76 }
     77 
     78 static void set_window_rows(void)
     79 {
     80 	int ret;
     81 	struct winsize ws;
     82 
     83 	ret = ioctl(fileno(stdout), TIOCGWINSZ, &ws);
     84 	if (ret < 0 || ws.ws_row < 3)
     85 		ws.ws_row = 24;
     86 	rows = ws.ws_row;
     87 }
     88 
     89 static void sighandler(int num)
     90 {
     91 	if (num == SIGWINCH)
     92 		set_window_rows();
     93 }
     94 
     95 int main(int argc, char **argv)
     96 {
     97 	struct avc_cache_stats tot, rel, last;
     98 	int fd, i, cumulative = 0;
     99 	struct sigaction sa;
    100 	char avcstatfile[PATH_MAX];
    101 	snprintf(avcstatfile, sizeof avcstatfile, "%s%s", selinux_mnt,
    102 		 DEF_STAT_FILE);
    103 	progname = basename(argv[0]);
    104 
    105 	memset(&last, 0, sizeof(last));
    106 
    107 	while ((i = getopt(argc, argv, "cf:h?-")) != -1) {
    108 		switch (i) {
    109 		case 'c':
    110 			cumulative = 1;
    111 			break;
    112 		case 'f':
    113 			strncpy(avcstatfile, optarg, sizeof avcstatfile);
    114 			break;
    115 		case 'h':
    116 		case '-':
    117 			usage();
    118 			exit(0);
    119 		default:
    120 			usage();
    121 			die("unrecognized parameter '%c'", i);
    122 		}
    123 	}
    124 
    125 	if (optind < argc) {
    126 		char *arg = argv[optind];
    127 		unsigned int n = strtoul(arg, NULL, 10);
    128 
    129 		if (errno == ERANGE) {
    130 			usage();
    131 			die("invalid interval \'%s\'", arg);
    132 		}
    133 		if (n == 0) {
    134 			usage();
    135 			exit(0);
    136 		}
    137 		interval = n;
    138 	}
    139 
    140 	sa.sa_handler = sighandler;
    141 	sa.sa_flags = SA_RESTART;
    142 	sigemptyset(&sa.sa_mask);
    143 
    144 	i = sigaction(SIGWINCH, &sa, NULL);
    145 	if (i < 0)
    146 		die("sigaction");
    147 
    148 	set_window_rows();
    149 	fd = open(avcstatfile, O_RDONLY);
    150 	if (fd < 0)
    151 		die("open: \'%s\'", avcstatfile);
    152 
    153 	for (i = 0;; i++) {
    154 		char *line;
    155 		ssize_t ret, parsed = 0;
    156 
    157 		memset(buf, 0, DEF_BUF_SIZE);
    158 		ret = read(fd, buf, DEF_BUF_SIZE-1);
    159 		if (ret < 0)
    160 			die("read");
    161 
    162 		if (ret == 0)
    163 			die("read: \'%s\': unexpected end of file",
    164 			    avcstatfile);
    165 
    166 		line = strtok(buf, "\n");
    167 		if (!line)
    168 			die("unable to parse \'%s\': end of line not found",
    169 			    avcstatfile);
    170 
    171 		if (strcmp(line, HEADERS))
    172 			die("unable to parse \'%s\': invalid headers",
    173 			    avcstatfile);
    174 
    175 		if (!i || !(i % (rows - 2)))
    176 			printf("%10s %10s %10s %10s %10s %10s\n", "lookups",
    177 			       "hits", "misses", "allocs", "reclaims", "frees");
    178 
    179 		memset(&tot, 0, sizeof(tot));
    180 
    181 		while ((line = strtok(NULL, "\n"))) {
    182 			struct avc_cache_stats tmp;
    183 
    184 			ret = sscanf(line, "%llu %llu %llu %llu %llu %llu",
    185 				     &tmp.lookups,
    186 				     &tmp.hits,
    187 				     &tmp.misses,
    188 				     &tmp.allocations,
    189 				     &tmp.reclaims, &tmp.frees);
    190 			if (ret != 6)
    191 				die("unable to parse \'%s\': scan error",
    192 				    avcstatfile);
    193 
    194 			tot.lookups += tmp.lookups;
    195 			tot.hits += tmp.hits;
    196 			tot.misses += tmp.misses;
    197 			tot.allocations += tmp.allocations;
    198 			tot.reclaims += tmp.reclaims;
    199 			tot.frees += tmp.frees;
    200 			parsed = 1;
    201 		}
    202 
    203 		if (!parsed)
    204 			die("unable to parse \'%s\': no data", avcstatfile);
    205 
    206 		if (cumulative || !i)
    207 			printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
    208 			       tot.lookups, tot.hits, tot.misses,
    209 			       tot.allocations, tot.reclaims, tot.frees);
    210 		else {
    211 			rel.lookups = tot.lookups - last.lookups;
    212 			rel.hits = tot.hits - last.hits;
    213 			rel.misses = tot.misses - last.misses;
    214 			rel.allocations = tot.allocations - last.allocations;
    215 			rel.reclaims = tot.reclaims - last.reclaims;
    216 			rel.frees = tot.frees - last.frees;
    217 			printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
    218 			       rel.lookups, rel.hits, rel.misses,
    219 			       rel.allocations, rel.reclaims, rel.frees);
    220 		}
    221 
    222 		if (!interval)
    223 			break;
    224 
    225 		memcpy(&last, &tot, sizeof(last));
    226 		sleep(interval);
    227 
    228 		ret = lseek(fd, 0, 0);
    229 		if (ret < 0)
    230 			die("lseek");
    231 	}
    232 
    233 	close(fd);
    234 	return 0;
    235 }
    236