/* Main body of the oops "daemon".
   (C) Copyright 2002 Rusty Russell IBM Corporation.
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include "oopsd.h"

static void usage(const char *msg)
{
	if (msg) fprintf(stderr, "%s\n", msg);
	fprintf(stderr,
		"Usage:\n"
		"  oopsd --arm <filename>\n"
		"  oopsd --disarm\n"
		"  oopsd --add-symbols <prefix> [<filename>]\n"
		"  oopsd --test\n"
		"  oopsd -r <filename>\n");
	exit(1);
}

static int open_oopser(void)
{
	int fd;

	fd = open("/dev/oopser", O_WRONLY);
#if 1
	if (fd < 0) {
		perror("opening /dev/oopser");
		exit(1);
	}
#endif
	return fd;
}

static void disarm(void)
{
	int oopser_fd;
	unsigned int disarm;

	oopser_fd = open_oopser();

	disarm = LINUX_OOPSER_DISARM;
	if (write(oopser_fd, &disarm, sizeof(disarm)) != sizeof(disarm)) {
		perror("writing disarming information to /dev/oopser");
		exit(1);
	}
	printf("Oopser: DISARMED\n");
	close(oopser_fd);
}

/* FIXME: Use O_DIRECT? */
static void initialize(const char *name)
{
	int fd, i;
	char buffer[512] = LINUX_OOPSER_SIGNATURE "--arm\n";

	fd = open(name, O_WRONLY|O_CREAT|O_TRUNC);
	if (fd < 0) {
		fprintf(stderr, "Error opening/creating %s: %s\n", name,
			strerror(errno));
		exit(1);
	}

	for (i = 0; i < LINUX_OOPSER_BLOCKS; i++) {
		if (write(fd, buffer, sizeof(buffer)) != sizeof(buffer)) {
			fprintf(stderr, "Initializing %s: %s\n",
				name, strerror(errno));
			exit(1);
		}
	}

	if (fsync(fd) != 0)
		fprintf(stderr, "Completing initialization of %s: %s\n",
			name, strerror(errno));
	close(fd);
}

static void arm(const char *name)
{
	int oopser_fd;
	void *info;
	size_t len;
	unsigned int arm;
	int r;

	oopser_fd = open_oopser();

	info = create_set_command(name, &len);
	if ((r = write(oopser_fd, info, len)) != len) {
		perror("writing setup information to /dev/oopser");
		if (errno == EBUSY)
			fprintf(stderr, "Maybe it is already activated?\n");
		exit(1);
	}
	free(info);

	arm = LINUX_OOPSER_ARM;
	if (write(oopser_fd, &arm, sizeof(arm)) != sizeof(arm)) {
		perror("writing arming information to /dev/oopser");
		exit(1);
	}
	printf("Oopser: ARMED on %s\n", name);
	close(oopser_fd);
}

static void test_oopser(void)
{
	int oopser_fd;
	unsigned int test = LINUX_OOPSER_TEST;

	/* This test will oops us if successful.  Beware. */
	oopser_fd = open_oopser();
	sync();

	if (write(oopser_fd, &test, sizeof(test)) != sizeof(test)) {
		perror("writing test code /dev/oopser");
		exit(1);
	}
}

static void add_symbols(const char *prefix, const char *filename)
{
	int oopser_fd;
	FILE *file;

	oopser_fd = open_oopser();

	if (filename) file = fopen(filename, "r");
	else file = stdin;

	if (!file) {
		fprintf(stderr, "Could not open %s: %s\n",
			filename, strerror(errno));
		exit(1);
	}

	do_add_symbols(file, prefix, oopser_fd);
}

int main(int argc, char *argv[])
{
	if (argc < 2) usage(NULL);
	if (strcmp(argv[1], "--arm") == 0) {
		if (argc != 3) usage("--arm requires one argument");
		initialize(argv[2]);
		arm(argv[2]);
	} else if (strcmp(argv[1], "--disarm") == 0) {
		if (argc != 2) usage("--disarm requires no arguments");
		disarm();
	} else if (strcmp(argv[1], "--add-symbols") == 0) {
		if (argc < 3 || argc > 4)
			usage("--add-symbols only takes one of two arguments");
		add_symbols(argv[2], argv[3]);
	} else if (strcmp(argv[1], "--initialize") == 0) {
		if (argc != 3) usage("--initialize requires one argument");
		initialize(argv[2]);
	} else if (strcmp(argv[1], "-r") == 0) {
		/* We have an oops: mail and clean */
		if (argc != 3) usage("-r requires one argument");
		mail_oops(argv[2]);
		/* Do the reinitialize automatically */
		initialize(argv[2]);
		arm(argv[2]);
	} else if (strcmp(argv[1], "--test") == 0) {
		if (argc != 2) usage("--test requires no arguments");
		test_oopser();
	} else
		usage("Unknown option");
	return 0;
}
