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