blob: 7239ef2a0bb4123c262ae81c1671d2f9e88a55b8 [file] [log] [blame]
Joshua Brindle13cd4c82008-08-19 15:30:36 -04001/*
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
29struct 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
38static int interval;
39static int rows;
40static char *progname;
41static char buf[DEF_BUF_SIZE];
42
43/* selinuxfs mount point */
44extern char *selinux_mnt;
45
Daniel P. Berrange91d9fe82012-01-23 15:41:18 +000046static __attribute__((__format__(printf,1,2))) void die(const char *msg, ...)
Joshua Brindle13cd4c82008-08-19 15:30:36 -040047{
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
63static 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
78static 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
89static void sighandler(int num)
90{
91 if (num == SIGWINCH)
92 set_window_rows();
93}
94
95int 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();
Daniel P. Berrange91d9fe82012-01-23 15:41:18 +0000121 die("unrecognized parameter '%c'", i);
Joshua Brindle13cd4c82008-08-19 15:30:36 -0400122 }
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
143 i = sigaction(SIGWINCH, &sa, NULL);
144 if (i < 0)
145 die("sigaction");
146
147 set_window_rows();
148 fd = open(avcstatfile, O_RDONLY);
149 if (fd < 0)
150 die("open: \'%s\'", avcstatfile);
151
152 for (i = 0;; i++) {
153 char *line;
154 ssize_t ret, parsed = 0;
155
156 memset(buf, 0, DEF_BUF_SIZE);
157 ret = read(fd, buf, DEF_BUF_SIZE);
158 if (ret < 0)
159 die("read");
160
161 if (ret == 0)
162 die("read: \'%s\': unexpected end of file",
163 avcstatfile);
164
165 line = strtok(buf, "\n");
166 if (!line)
167 die("unable to parse \'%s\': end of line not found",
168 avcstatfile);
169
170 if (strcmp(line, HEADERS))
171 die("unable to parse \'%s\': invalid headers",
172 avcstatfile);
173
174 if (!i || !(i % (rows - 2)))
175 printf("%10s %10s %10s %10s %10s %10s\n", "lookups",
176 "hits", "misses", "allocs", "reclaims", "frees");
177
178 memset(&tot, 0, sizeof(tot));
179
180 while ((line = strtok(NULL, "\n"))) {
181 struct avc_cache_stats tmp;
182
183 ret = sscanf(line, "%llu %llu %llu %llu %llu %llu",
184 &tmp.lookups,
185 &tmp.hits,
186 &tmp.misses,
187 &tmp.allocations,
188 &tmp.reclaims, &tmp.frees);
189 if (ret != 6)
190 die("unable to parse \'%s\': scan error",
191 avcstatfile);
192
193 tot.lookups += tmp.lookups;
194 tot.hits += tmp.hits;
195 tot.misses += tmp.misses;
196 tot.allocations += tmp.allocations;
197 tot.reclaims += tmp.reclaims;
198 tot.frees += tmp.frees;
199 parsed = 1;
200 }
201
202 if (!parsed)
203 die("unable to parse \'%s\': no data", avcstatfile);
204
205 if (cumulative || (!cumulative && !i))
206 printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
207 tot.lookups, tot.hits, tot.misses,
208 tot.allocations, tot.reclaims, tot.frees);
209 else {
210 rel.lookups = tot.lookups - last.lookups;
211 rel.hits = tot.hits - last.hits;
212 rel.misses = tot.misses - last.misses;
213 rel.allocations = tot.allocations - last.allocations;
214 rel.reclaims = tot.reclaims - last.reclaims;
215 rel.frees = tot.frees - last.frees;
216 printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
217 rel.lookups, rel.hits, rel.misses,
218 rel.allocations, rel.reclaims, rel.frees);
219 }
220
221 if (!interval)
222 break;
223
224 memcpy(&last, &tot, sizeof(last));
225 sleep(interval);
226
227 ret = lseek(fd, 0, 0);
228 if (ret < 0)
229 die("lseek");
230 }
231
232 close(fd);
233 return 0;
234}