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