113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle/*
213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * avcstat - Display SELinux avc statistics.
313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *
413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * Copyright (C) 2004 Red Hat, Inc., James Morris <jmorris@redhat.com>
513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *
613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * This program is free software; you can redistribute it and/or modify
713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * it under the terms of the GNU General Public License version 2,
813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle * as published by the Free Software Foundation.
913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle *
1013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle */
1113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <stdio.h>
1213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <stdlib.h>
1313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <libgen.h>
1413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <stdarg.h>
1513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <errno.h>
1613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <string.h>
1713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <fcntl.h>
1813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <unistd.h>
1913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <signal.h>
2013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/types.h>
2113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/stat.h>
2213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <sys/ioctl.h>
2313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#include <linux/limits.h>
2413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
2513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#define DEF_STAT_FILE	"/avc/cache_stats"
2613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#define DEF_BUF_SIZE	8192
2713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle#define HEADERS		"lookups hits misses allocations reclaims frees"
2813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
2913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestruct avc_cache_stats {
3013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	unsigned long long lookups;
3113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	unsigned long long hits;
3213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	unsigned long long misses;
3313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	unsigned long long allocations;
3413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	unsigned long long reclaims;
3513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	unsigned long long frees;
3613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle};
3713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
3813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic int interval;
3913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic int rows;
4013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic char *progname;
4113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic char buf[DEF_BUF_SIZE];
4213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
4313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle/* selinuxfs mount point */
4413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindleextern char *selinux_mnt;
4513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
4691d9fe8af05a9a9ded5d02bcd8c1c5a1e1ef670eDaniel P. Berrangestatic __attribute__((__format__(printf,1,2))) void die(const char *msg, ...)
4713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{
4813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	va_list args;
4913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
5013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	fputs("ERROR: ", stderr);
5113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
5213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	va_start(args, msg);
5313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	vfprintf(stderr, msg, args);
5413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	va_end(args);
5513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
5613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	if (errno)
5713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		fprintf(stderr, ": %s", strerror(errno));
5813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
5913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	fputc('\n', stderr);
6013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	exit(1);
6113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle}
6213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
6313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic void usage(void)
6413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{
6513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	printf("\nUsage: %s [-c] [-f status_file] [interval]\n\n", progname);
6613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	printf
6713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	    ("Display SELinux AVC statistics.  If the interval parameter is specified, the\n");
6813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	printf
6913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	    ("program will loop, displaying updated statistics every \'interval\' seconds.\n");
7013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	printf
7113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	    ("Relative values are displayed by default. Use the -c option to specify the\n");
7213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	printf
7313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	    ("display of cumulative values.  The -f option specifies the location of the\n");
7413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	printf("AVC statistics file, defaulting to \'%s%s\'.\n\n", selinux_mnt,
7513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	       DEF_STAT_FILE);
7613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle}
7713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
7813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic void set_window_rows(void)
7913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{
8013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	int ret;
8113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	struct winsize ws;
8213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
8313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	ret = ioctl(fileno(stdout), TIOCGWINSZ, &ws);
8413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	if (ret < 0 || ws.ws_row < 3)
8513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		ws.ws_row = 24;
8613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	rows = ws.ws_row;
8713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle}
8813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
8913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindlestatic void sighandler(int num)
9013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{
9113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	if (num == SIGWINCH)
9213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		set_window_rows();
9313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle}
9413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
9513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindleint main(int argc, char **argv)
9613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle{
9713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	struct avc_cache_stats tot, rel, last;
9813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	int fd, i, cumulative = 0;
9913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	struct sigaction sa;
10013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	char avcstatfile[PATH_MAX];
10113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	snprintf(avcstatfile, sizeof avcstatfile, "%s%s", selinux_mnt,
10213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		 DEF_STAT_FILE);
10313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	progname = basename(argv[0]);
10413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
10513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	memset(&last, 0, sizeof(last));
10613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
10713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	while ((i = getopt(argc, argv, "cf:h?-")) != -1) {
10813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		switch (i) {
10913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		case 'c':
11013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			cumulative = 1;
11113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			break;
11213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		case 'f':
11313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			strncpy(avcstatfile, optarg, sizeof avcstatfile);
11413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			break;
11513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		case 'h':
11613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		case '-':
11713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			usage();
11813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			exit(0);
11913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		default:
12013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			usage();
12191d9fe8af05a9a9ded5d02bcd8c1c5a1e1ef670eDaniel P. Berrange			die("unrecognized parameter '%c'", i);
12213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		}
12313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	}
12413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
12513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	if (optind < argc) {
12613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		char *arg = argv[optind];
12713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		unsigned int n = strtoul(arg, NULL, 10);
12813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
12913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (errno == ERANGE) {
13013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			usage();
13113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("invalid interval \'%s\'", arg);
13213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		}
13313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (n == 0) {
13413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			usage();
13513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			exit(0);
13613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		}
13713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		interval = n;
13813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	}
13913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
14013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	sa.sa_handler = sighandler;
14113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	sa.sa_flags = SA_RESTART;
142fa7a9a604ee9f12dbfa63950adc0122880c092b0Dan Walsh	sigemptyset(&sa.sa_mask);
14313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
14413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	i = sigaction(SIGWINCH, &sa, NULL);
14513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	if (i < 0)
14613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		die("sigaction");
14713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
14813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	set_window_rows();
14913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	fd = open(avcstatfile, O_RDONLY);
15013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	if (fd < 0)
15113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		die("open: \'%s\'", avcstatfile);
15213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
15313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	for (i = 0;; i++) {
15413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		char *line;
15513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		ssize_t ret, parsed = 0;
15613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
15713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		memset(buf, 0, DEF_BUF_SIZE);
158aa62cd60f7192123b509c2518e7a2083e34a65a2Eric Paris		ret = read(fd, buf, DEF_BUF_SIZE-1);
15913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (ret < 0)
16013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("read");
16113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
16213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (ret == 0)
16313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("read: \'%s\': unexpected end of file",
16413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			    avcstatfile);
16513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
16613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		line = strtok(buf, "\n");
16713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (!line)
16813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("unable to parse \'%s\': end of line not found",
16913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			    avcstatfile);
17013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
17113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (strcmp(line, HEADERS))
17213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("unable to parse \'%s\': invalid headers",
17313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			    avcstatfile);
17413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
17513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (!i || !(i % (rows - 2)))
17613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			printf("%10s %10s %10s %10s %10s %10s\n", "lookups",
17713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			       "hits", "misses", "allocs", "reclaims", "frees");
17813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
17913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		memset(&tot, 0, sizeof(tot));
18013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
18113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		while ((line = strtok(NULL, "\n"))) {
18213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			struct avc_cache_stats tmp;
18313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
18413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			ret = sscanf(line, "%llu %llu %llu %llu %llu %llu",
18513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				     &tmp.lookups,
18613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				     &tmp.hits,
18713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				     &tmp.misses,
18813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				     &tmp.allocations,
18913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				     &tmp.reclaims, &tmp.frees);
19013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			if (ret != 6)
19113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				die("unable to parse \'%s\': scan error",
19213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle				    avcstatfile);
19313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
19413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			tot.lookups += tmp.lookups;
19513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			tot.hits += tmp.hits;
19613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			tot.misses += tmp.misses;
19713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			tot.allocations += tmp.allocations;
19813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			tot.reclaims += tmp.reclaims;
19913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			tot.frees += tmp.frees;
20013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			parsed = 1;
20113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		}
20213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
20313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (!parsed)
20413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("unable to parse \'%s\': no data", avcstatfile);
20513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
20613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (cumulative || (!cumulative && !i))
20713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
20813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			       tot.lookups, tot.hits, tot.misses,
20913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			       tot.allocations, tot.reclaims, tot.frees);
21013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		else {
21113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			rel.lookups = tot.lookups - last.lookups;
21213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			rel.hits = tot.hits - last.hits;
21313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			rel.misses = tot.misses - last.misses;
21413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			rel.allocations = tot.allocations - last.allocations;
21513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			rel.reclaims = tot.reclaims - last.reclaims;
21613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			rel.frees = tot.frees - last.frees;
21713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			printf("%10Lu %10Lu %10Lu %10Lu %10Lu %10Lu\n",
21813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			       rel.lookups, rel.hits, rel.misses,
21913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			       rel.allocations, rel.reclaims, rel.frees);
22013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		}
22113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
22213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (!interval)
22313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			break;
22413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
22513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		memcpy(&last, &tot, sizeof(last));
22613cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		sleep(interval);
22713cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
22813cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		ret = lseek(fd, 0, 0);
22913cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle		if (ret < 0)
23013cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle			die("lseek");
23113cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	}
23213cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle
23313cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	close(fd);
23413cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle	return 0;
23513cd4c8960688af11ad23b4c946149015c80d54Joshua Brindle}
236