• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * avcstat - Display SELinux avc statistics.
3  *
4  * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@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 
die(const char * msg,...)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 
usage(void)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 
set_window_rows(void)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 
sighandler(int num)89 static void sighandler(int num)
90 {
91 	if (num == SIGWINCH)
92 		set_window_rows();
93 }
94 
main(int argc,char ** argv)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) - 1);
114 			avcstatfile[sizeof(avcstatfile)-1] = '\0';
115 			break;
116 		case 'h':
117 		case '-':
118 			usage();
119 			exit(0);
120 		default:
121 			usage();
122 			die("unrecognized parameter '%c'", i);
123 		}
124 	}
125 
126 	if (optind < argc) {
127 		char *arg = argv[optind];
128 		unsigned int n = strtoul(arg, NULL, 10);
129 
130 		if (errno == ERANGE) {
131 			usage();
132 			die("invalid interval \'%s\'", arg);
133 		}
134 		if (n == 0) {
135 			usage();
136 			exit(0);
137 		}
138 		interval = n;
139 	}
140 
141 	sa.sa_handler = sighandler;
142 	sa.sa_flags = SA_RESTART;
143 	sigemptyset(&sa.sa_mask);
144 
145 	i = sigaction(SIGWINCH, &sa, NULL);
146 	if (i < 0)
147 		die("sigaction");
148 
149 	set_window_rows();
150 	fd = open(avcstatfile, O_RDONLY);
151 	if (fd < 0)
152 		die("open: \'%s\'", avcstatfile);
153 
154 	for (i = 0;; i++) {
155 		char *line;
156 		ssize_t ret, parsed = 0;
157 
158 		memset(buf, 0, DEF_BUF_SIZE);
159 		ret = read(fd, buf, DEF_BUF_SIZE-1);
160 		if (ret < 0)
161 			die("read");
162 
163 		if (ret == 0)
164 			die("read: \'%s\': unexpected end of file",
165 			    avcstatfile);
166 
167 		line = strtok(buf, "\n");
168 		if (!line)
169 			die("unable to parse \'%s\': end of line not found",
170 			    avcstatfile);
171 
172 		if (strcmp(line, HEADERS))
173 			die("unable to parse \'%s\': invalid headers",
174 			    avcstatfile);
175 
176 		if (!i || !(i % (rows - 2)))
177 			printf("%10s %10s %10s %10s %10s %10s\n", "lookups",
178 			       "hits", "misses", "allocs", "reclaims", "frees");
179 
180 		memset(&tot, 0, sizeof(tot));
181 
182 		while ((line = strtok(NULL, "\n"))) {
183 			struct avc_cache_stats tmp;
184 
185 			ret = sscanf(line, "%llu %llu %llu %llu %llu %llu",
186 				     &tmp.lookups,
187 				     &tmp.hits,
188 				     &tmp.misses,
189 				     &tmp.allocations,
190 				     &tmp.reclaims, &tmp.frees);
191 			if (ret != 6)
192 				die("unable to parse \'%s\': scan error",
193 				    avcstatfile);
194 
195 			tot.lookups += tmp.lookups;
196 			tot.hits += tmp.hits;
197 			tot.misses += tmp.misses;
198 			tot.allocations += tmp.allocations;
199 			tot.reclaims += tmp.reclaims;
200 			tot.frees += tmp.frees;
201 			parsed = 1;
202 		}
203 
204 		if (!parsed)
205 			die("unable to parse \'%s\': no data", avcstatfile);
206 
207 		if (cumulative || !i)
208 			printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
209 			       tot.lookups, tot.hits, tot.misses,
210 			       tot.allocations, tot.reclaims, tot.frees);
211 		else {
212 			rel.lookups = tot.lookups - last.lookups;
213 			rel.hits = tot.hits - last.hits;
214 			rel.misses = tot.misses - last.misses;
215 			rel.allocations = tot.allocations - last.allocations;
216 			rel.reclaims = tot.reclaims - last.reclaims;
217 			rel.frees = tot.frees - last.frees;
218 			printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
219 			       rel.lookups, rel.hits, rel.misses,
220 			       rel.allocations, rel.reclaims, rel.frees);
221 		}
222 
223 		if (!interval)
224 			break;
225 
226 		memcpy(&last, &tot, sizeof(last));
227 		sleep(interval);
228 
229 		ret = lseek(fd, 0, 0);
230 		if (ret < 0)
231 			die("lseek");
232 	}
233 
234 	close(fd);
235 	return 0;
236 }
237