#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <selinux/selinux.h>
#include <selinux/label.h>

static __attribute__ ((__noreturn__)) void usage(const char *progname)
{
	fprintf(stderr,
		"usage: %s -b backend [-v] [-r] -k key [-t type] [-f file]\n\n"
		"Where:\n\t"
		"-b  The backend - \"file\", \"media\", \"x\", \"db\" or "
			"\"prop\"\n\t"
		"-v  Validate entries against loaded policy.\n\t"
		"-r  Use \"raw\" function.\n\t"
		"-k  Lookup key - Depends on backend.\n\t"
		"-t  Lookup type - Optional as depends on backend.\n\t"
		"-f  Optional file containing the specs (defaults to\n\t"
		"    those used by loaded policy).\n\n"
		"Examples:\n\t"
		"%s -v -b file -k /run -t 0\n\t"
		"   lookup with validation against the loaded policy, the\n\t"
		"   \"file\" backend for path \"/run\" with mode = 0\n\t"
		"%s -r -b x -t 4 -k X11:ButtonPress\n\t"
		"   lookup_raw the \"X\" backend for type SELABEL_X_EVENT\n\t"
		"   using key \"X11:ButtonPress\"\n\n",
		progname, progname, progname);
	exit(1);
}

int main(int argc, char **argv)
{
	int raw = 0, type = 0, backend = 0, rc, opt;
	char *validate = NULL, *key = NULL, *context = NULL, *file = NULL;

	struct selabel_handle *hnd;
	struct selinux_opt selabel_option[] = {
		{ SELABEL_OPT_PATH, file },
		{ SELABEL_OPT_VALIDATE, validate }
	};

	if (argc < 3)
		usage(argv[0]);

	while ((opt = getopt(argc, argv, "b:f:vrk:t:")) > 0) {
		switch (opt) {
		case 'b':
			if (!strcasecmp(optarg, "file")) {
				backend = SELABEL_CTX_FILE;
			} else if (!strcmp(optarg, "media")) {
				backend = SELABEL_CTX_MEDIA;
			} else if (!strcmp(optarg, "x")) {
				backend = SELABEL_CTX_X;
			} else if (!strcmp(optarg, "db")) {
				backend = SELABEL_CTX_DB;
			} else if (!strcmp(optarg, "prop")) {
				backend = SELABEL_CTX_ANDROID_PROP;
			} else if (!strcmp(optarg, "service")) {
				backend = SELABEL_CTX_ANDROID_SERVICE;
			} else {
				fprintf(stderr, "Unknown backend: %s\n",
								    optarg);
				usage(argv[0]);
			}
			break;
		case 'f':
			file = optarg;
			break;
		case 'v':
			validate = (char *)1;
			break;
		case 'r':
			raw = 1;
			break;
		case 'k':
			key = optarg;
			break;
		case 't':
			type = atoi(optarg);
			break;
		default:
			usage(argv[0]);
		}
	}

	selabel_option[0].value = file;
	selabel_option[1].value = validate;

	hnd = selabel_open(backend, selabel_option, 2);
	if (!hnd) {
		fprintf(stderr, "ERROR: selabel_open - Could not obtain "
							     "handle.\n");
		return -1;
	}

	switch (raw) {
	case 1:
		rc = selabel_lookup_raw(hnd, &context, key, type);
		break;
	default:
		rc = selabel_lookup(hnd, &context, key, type);
	}
	selabel_close(hnd);

	if (rc) {
		switch (errno) {
		case ENOENT:
			fprintf(stderr, "ERROR: selabel_lookup failed to "
					    "find a valid context.\n");
			break;
		case EINVAL:
			fprintf(stderr, "ERROR: selabel_lookup failed to "
				    "validate context, or key / type are "
				    "invalid.\n");
			break;
		default:
			fprintf(stderr, "selabel_lookup ERROR: %s\n",
						    strerror(errno));
		}
	} else {
		printf("Default context: %s\n", context);
		freecon(context);
	}

	return rc;
}