/*
 * lkcd_config.c -- dump configuration utility
 * Created by: Matt D. Robinson (yakker@aparity.com)
 * Copyright 2001 Matt D. Robinson (yakker@aparity.com), all rights reserved.
 *
 * This source code is released under the terms of version 2 of the GNU GPL.
 */
#ifndef __KERNEL__
#define __KERNEL__
#include <lcrash.h>
#include <stdio.h>
#include <linux/module.h>
#undef __KERNEL__
#else   /* __KERNEL__ */
#include <lcrash.h>
#include <stdio.h>
#include <linux/module.h>
#endif  /* __KERNEL__ */
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>

#define DUMP_DEVICE	"/dev/dump"
#define DUMP_TRUE	1
#define DUMP_FALSE	0

/*
 * Name: main()
 * Func: A quick-and-dirty dump configuration tool.  Should suffice
 *       for the time being.
 */
int
main(int argc, char **argv)
{
	dev_t dnum = 0;
	int dfd, fd, err, compress, level, flags, device_num, c;
	int device_name_set = DUMP_FALSE, level_set = DUMP_FALSE;
	int flags_set = DUMP_FALSE, compress_set = DUMP_FALSE;
	int device_num_set = DUMP_FALSE, query_set = DUMP_FALSE;
	int test_set = DUMP_FALSE;
	char *test = NULL;
	char *device_name, tbuf[1024];
	struct stat stbuf;

#if !DUMP_DEBUG
	/* check for root if not debugging */
	if (geteuid() != 0) {
		fprintf(stderr, "Error: You must run this program as root!\n");
		return (-1);
	}
#endif

	/* walk through the options */
	compress = level = flags = device_num = 0;
	while ((c = getopt(argc, argv, "qc:d:D:f:l:t:")) > 0) {
		switch (c) {
			case 'c':
				if (compress_set == DUMP_TRUE) {
					goto usage;
				}
				compress_set = DUMP_TRUE;
				compress = atoi(optarg);
				break;

			case 'd':
				if ((device_name_set == DUMP_TRUE) ||
					(device_num_set == DUMP_TRUE)) {
						goto usage;
				}
				device_name_set = DUMP_TRUE;
				device_name = optarg;
				break;

			case 'D':
				if ((device_name_set == DUMP_TRUE) ||
					(device_num_set == DUMP_TRUE)) {
						goto usage;
				}
				device_num_set = DUMP_TRUE;
				device_num = atoi(optarg);
				break;

			case 'f':
				if (flags_set == DUMP_TRUE) {
					goto usage;
				}
				flags_set = DUMP_TRUE;
				flags = atoi(optarg);
				break;

			case 'l':
				if (level_set == DUMP_TRUE) {
					goto usage;
				}
				level_set = DUMP_TRUE;
				level = atoi(optarg);
				break;

			case 'q':
				if (query_set == DUMP_TRUE) {
					goto usage;
				}
				query_set = DUMP_TRUE;
				break;
#if DUMP_DEBUG
			case 't':
				if (test_set  == DUMP_TRUE) {
					goto usage;
				}
				test = optarg;
				test_set = DUMP_TRUE;
				break;
#endif

			default:
				goto usage;
		}
	}

	/* make sure -q isn't specified with anything else */
	if (query_set == DUMP_TRUE) {
		if ((device_name_set == DUMP_TRUE) ||
			(device_num_set == DUMP_TRUE) ||
			(level_set == DUMP_TRUE) ||
			(flags_set == DUMP_TRUE) ||
			(compress_set == DUMP_TRUE) ||
			(test_set == DUMP_TRUE)) {
				goto usage;
		}

		/* open the dump device for queries */
		if ((dfd = open(DUMP_DEVICE, O_RDONLY)) < 0) {
			perror("open of dump device");
			return (dfd);
		}

		/* get dump compression */
		if ((err = ioctl(dfd, DIOGDUMPCOMPRESS, (caddr_t)&compress)) < 0) {
			perror("ioctl() query for dump compression failed");
			close(dfd);
			return (err);
		}

		/* get dump flags */
		if ((err = ioctl(dfd, DIOGDUMPFLAGS, (caddr_t)&flags)) < 0) {
			perror("ioctl() query for dump flags failed");
			close(dfd);
			return (err);
		}

		/* get dump level */
		if ((err = ioctl(dfd, DIOGDUMPLEVEL, (caddr_t)&level)) < 0) {
			perror("ioctl() query for dump level failed");
			close(dfd);
			return (err);
		}

		/* get device to dump to (if specified) */
		if ((err = ioctl(dfd, DIOGDUMPDEV, (caddr_t)&dnum)) < 0) {
			perror("ioctl() for dump device failed");
			close(dfd);
			return (err);
		}

		printf("            Configured dump device: 0x%x\n", (int)dnum);

		memset(tbuf, 0, 1024);
		if (flags == DUMP_FLAGS_NONE) {
			strcat(tbuf, "DUMP_FLAGS_NONE|");
		} else if (flags & DUMP_FLAGS_NONDISRUPT) {
			strcat(tbuf, "DUMP_FLAGS_NONDISRUPT|");
		}
		if (tbuf[strlen(tbuf)-1] == '|') {
			tbuf[strlen(tbuf)-1] = 0;
		}
		printf("             Configured dump flags: %s\n", tbuf);

		memset(tbuf, 0, 1024);
		if (level == DUMP_LEVEL_NONE) {
			strcat(tbuf, "DUMP_LEVEL_NONE|");
		}
		if (level & DUMP_LEVEL_HEADER) {
			strcat(tbuf, "DUMP_LEVEL_HEADER|");
		}
		if (level & DUMP_LEVEL_KERN) {
			strcat(tbuf, "DUMP_LEVEL_KERN|");
		}
		if (level & DUMP_LEVEL_USED) {
			strcat(tbuf, "DUMP_LEVEL_USED|");
		}
		if (level & DUMP_LEVEL_ALL_RAM) {
			strcat(tbuf, "DUMP_LEVEL_ALL_RAM|");
		}
		if (level & DUMP_LEVEL_ALL) {
			strcat(tbuf, "DUMP_LEVEL_ALL|");
		}
		if (tbuf[strlen(tbuf)-1] == '|') {
			tbuf[strlen(tbuf)-1] = 0;
		}
		printf("             Configured dump level: %s\n", tbuf);

		memset(tbuf, 0, 1024);
		if (compress == DUMP_COMPRESS_NONE) {
			strcat(tbuf, "DUMP_COMPRESS_NONE|");
		}
		if (compress & DUMP_COMPRESS_RLE) {
			strcat(tbuf, "DUMP_COMPRESS_RLE|");
		}
		if (compress & DUMP_COMPRESS_GZIP) {
			strcat(tbuf, "DUMP_COMPRESS_GZIP|");
		}
		if (tbuf[strlen(tbuf)-1] == '|') {
			tbuf[strlen(tbuf)-1] = 0;
		}
		printf("Configured dump compression method: %s\n", tbuf);

		/* bail out */
		close(dfd);
		return (0);
	} else if ((device_name_set != DUMP_TRUE) && (device_num_set != DUMP_TRUE) && 
		   (level_set != DUMP_TRUE) && (flags_set != DUMP_TRUE) && 
		   (compress_set != DUMP_TRUE) && (test_set != DUMP_TRUE)) {
			/* make sure _something_ is set */
			goto usage;
	}


	/* open the dump device for setting dump fields */
	if ((dfd = open(DUMP_DEVICE, O_RDWR)) < 0) {
		perror("open of dump device");
		return (dfd);
	}

	/* set the dnum -- dnum will != 0 after */
	if ((device_name_set == DUMP_TRUE) || (device_num_set == DUMP_TRUE)) {
		if (device_name_set == DUMP_TRUE) {
			if ((fd = open(device_name, O_RDONLY)) < 0) {
				perror("open of device name");
				close(dfd);
				return (fd);
			}

			if ((err = fstat(fd, &stbuf)) < 0) {
				perror("fstat of device name");
				close(dfd);
				close(fd);
				return (err);
			}
			dnum = stbuf.st_rdev;
			close(fd);
		} else {
			if (device_num <= 0) {
				fprintf(stderr, "Error: -d value must be > 0!\n");
				close(dfd);
				return (-1);
			}
			dnum = (dev_t)device_num;
		}
	}

	/* set dump compression */
	if (compress_set == DUMP_TRUE) {
		if ((err = ioctl(dfd, DIOSDUMPCOMPRESS, (caddr_t)compress)) < 0) {
			perror("ioctl() for dump compression failed");
			close(dfd);
			return (err);
		}
	}

	/* set dump flags */
	if (flags_set == DUMP_TRUE) {
		if ((err = ioctl(dfd, DIOSDUMPFLAGS, (caddr_t)flags)) < 0) {
			perror("ioctl() for dump flags failed");
			close(dfd);
			return (err);
		}
	}

	/* set dump level */
	if (level_set == DUMP_TRUE) {
		if ((err = ioctl(dfd, DIOSDUMPLEVEL, (caddr_t)level)) < 0) {
			perror("ioctl() for dump level failed");
			close(dfd);
			return (err);
		}
	}
	
	/* test dump - panic/die/dump/forever */
	if (test_set  == DUMP_TRUE) {
		printf("%s: test '%s'\n", argv[0], test);
		if ((err = ioctl(dfd, DIODUMPTEST, test)) < 0) {
                        perror("ioctl() for dump test failed");
                        close(dfd);
                        return (err);
                }
	}

	/* set device to dump to (if specified) */
	if (dnum != (dev_t)0) {
		if ((err = ioctl(dfd, DIOSDUMPDEV, dnum)) < 0) {
			perror("ioctl() for dump device failed");
			close(dfd);
			return (err);
		}
	}

	return (0);

usage:
	printf("Usage: %s [-d device_name | -D device_num] "
		"[-c compression_type]\n\t\t\t[-f flags] [-l level]\n"
#if DUMP_DEBUG
		"\t\t\t[-t (panic || die || dump || forever) ]\n"
#endif
		"\nUsage: %s [-q]\n", argv[0], argv[0]);
	return (1);
}
