/* main.c - core analysis suite
 *
 * Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
 * Copyright (C) 2002-2017 David Anderson
 * Copyright (C) 2002-2017 Red Hat, Inc. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include "defs.h"
#include "xen_hyper_defs.h"
#include <curses.h>
#include <getopt.h>
#include <sys/prctl.h>

static void setup_environment(int, char **);
static int is_external_command(void);
static int is_builtin_command(void);
static int is_input_file(void);
static void check_xen_hyper(void);
static void show_untrusted_files(void);
static void get_osrelease(char *);
static void get_log(char *);
static char *no_vmcoreinfo(const char *);

static struct option long_options[] = {
        {"memory_module", required_argument, 0, 0},
        {"memory_device", required_argument, 0, 0},
        {"no_kallsyms", 0, 0, 0},
        {"no_modules", 0, 0, 0},
        {"help", optional_argument, 0, 'h'},
	{"no_data_debug", 0, 0, 0},
	{"no_crashrc", 0, 0, 0},
	{"no_kmem_cache", 0, 0, 0},
	{"kmem_cache_delay", 0, 0, 0},
	{"readnow", 0, 0, 0},
	{"smp", 0, 0, 0},
	{"machdep", required_argument, 0, 0},
	{"version", 0, 0, 0},
	{"buildinfo", 0, 0, 0},
        {"cpus", required_argument, 0, 0},
        {"no_ikconfig", 0, 0, 0},
        {"hyper", 0, 0, 0},
	{"p2m_mfn", required_argument, 0, 0},
	{"xen_phys_start", required_argument, 0, 0},
	{"zero_excluded", 0, 0, 0},
	{"no_panic", 0, 0, 0},
        {"more", 0, 0, 0},
        {"less", 0, 0, 0},
        {"CRASHPAGER", 0, 0, 0},
        {"no_scroll", 0, 0, 0},
        {"reloc", required_argument, 0, 0},
	{"kaslr", required_argument, 0, 0},
	{"active", 0, 0, 0},
	{"minimal", 0, 0, 0},
	{"mod", required_argument, 0, 0},
	{"kvmhost", required_argument, 0, 0},
	{"kvmio", required_argument, 0, 0},
	{"no_elf_notes", 0, 0, 0},
	{"osrelease", required_argument, 0, 0},
	{"log", required_argument, 0, 0},
	{"hex", 0, 0, 0},
	{"dec", 0, 0, 0},
	{"no_strip", 0, 0, 0},
	{"hash", required_argument, 0, 0},
	{"offline", required_argument, 0, 0},
	{"src", required_argument, 0, 0},
        {0, 0, 0, 0}
};

int
main(int argc, char **argv)
{
	int i, c, option_index;
	char *tmpname;

	setup_environment(argc, argv);

	/* 
	 *  Get and verify command line options.
	 */
	opterr = 0;
	optind = 0;
	while((c = getopt_long(argc, argv, "Lkgh::e:i:sSvc:d:tfp:m:xo:",
       		long_options, &option_index)) != -1) {
		switch (c)
		{
		case 0:
		        if (STREQ(long_options[option_index].name, 
			    "memory_module")) 
				pc->memory_module = optarg;

		        else if (STREQ(long_options[option_index].name, 
			    "memory_device")) 
				pc->memory_device = optarg;

		        else if (STREQ(long_options[option_index].name, 
			    "no_kallsyms")) 
				kt->flags |= NO_KALLSYMS;

		        else if (STREQ(long_options[option_index].name, 
			    "no_modules")) 
				kt->flags |= NO_MODULE_ACCESS;

		        else if (STREQ(long_options[option_index].name, 
			    "no_ikconfig")) 
				kt->flags |= NO_IKCONFIG;

		        else if (STREQ(long_options[option_index].name, 
			    "no_data_debug")) 
				pc->flags &= ~DATADEBUG;

		        else if (STREQ(long_options[option_index].name, 
			    "no_kmem_cache")) 
				vt->flags |= KMEM_CACHE_UNAVAIL;

		        else if (STREQ(long_options[option_index].name, 
			    "kmem_cache_delay")) 
				vt->flags |= KMEM_CACHE_DELAY;

		        else if (STREQ(long_options[option_index].name, 
			    "readnow")) 
				pc->flags |= READNOW;

		        else if (STREQ(long_options[option_index].name, 
			    "smp")) 
				kt->flags |= SMP;

		        else if (STREQ(long_options[option_index].name, 
			    "machdep")) {
				for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
					if (machdep->cmdline_args[i])
						continue;
					machdep->cmdline_args[i] = optarg;
					break;
				}
				if (i == MAX_MACHDEP_ARGS)
					error(INFO, "option ignored: %s\n",
						optarg);
			}

		        else if (STREQ(long_options[option_index].name, 
			    "version")) { 
				pc->flags |= VERSION_QUERY;
                        	display_version();
                        	display_gdb_banner();
                        	clean_exit(0);
			}

		        else if (STREQ(long_options[option_index].name, 
			    "buildinfo")) {
				dump_build_data();
				clean_exit(0);
			}

		        else if (STREQ(long_options[option_index].name, "cpus")) 
				kt->cpus_override = optarg;

			else if (STREQ(long_options[option_index].name, "hyper"))
				pc->flags |= XEN_HYPER;

		        else if (STREQ(long_options[option_index].name, "p2m_mfn")) 
				xen_kdump_p2m_mfn(optarg);

		        else if (STREQ(long_options[option_index].name, "xen_phys_start")) 
				set_xen_phys_start(optarg);

		        else if (STREQ(long_options[option_index].name, "zero_excluded")) 
				*diskdump_flags |= ZERO_EXCLUDED;

			else if (STREQ(long_options[option_index].name, "no_elf_notes")) {
				if (machine_type("X86") || machine_type("X86_64"))
					*diskdump_flags |= NO_ELF_NOTES;
				else
					error(INFO,
					      "--no_elf_notes is only applicable to "
					      "the X86 and X86_64 architectures.\n");
			}

		        else if (STREQ(long_options[option_index].name, "no_panic")) 
				tt->flags |= PANIC_TASK_NOT_FOUND;

		        else if (STREQ(long_options[option_index].name, "no_strip")) 
				st->flags |= NO_STRIP;

		        else if (STREQ(long_options[option_index].name, "more")) {
				if ((pc->scroll_command != SCROLL_NONE) &&
				    file_exists("/bin/more", NULL))
					pc->scroll_command = SCROLL_MORE;
			}

		        else if (STREQ(long_options[option_index].name, "less")) {
				if ((pc->scroll_command != SCROLL_NONE) &&
				    file_exists("/usr/bin/less", NULL))
					pc->scroll_command = SCROLL_LESS;
			}

		        else if (STREQ(long_options[option_index].name, "CRASHPAGER")) {
				if ((pc->scroll_command != SCROLL_NONE) && 
				    CRASHPAGER_valid())
					pc->scroll_command = SCROLL_CRASHPAGER;
			}

		        else if (STREQ(long_options[option_index].name, "no_scroll"))
				 pc->flags &= ~SCROLL;

		        else if (STREQ(long_options[option_index].name, "no_crashrc"))
				pc->flags |= NOCRASHRC;

		        else if (STREQ(long_options[option_index].name, "active"))
				tt->flags |= ACTIVE_ONLY;

		        else if (STREQ(long_options[option_index].name, "mod"))
				kt->module_tree = optarg;

		        else if (STREQ(long_options[option_index].name, "hash")) {
				if (!calculate(optarg, &pc->nr_hash_queues, NULL, 0)) {
					error(INFO, "invalid --hash argument: %s\n",
						optarg);
				}
			} else if (STREQ(long_options[option_index].name, "kaslr")) {
				if (!machine_type("X86_64") &&
				    !machine_type("ARM64") && !machine_type("X86"))
					error(INFO, "--kaslr not valid "
						"with this machine type.\n");
				else if (STREQ(optarg, "auto"))
					kt->flags2 |= (RELOC_AUTO|KASLR);
				else {
					if (!calculate(optarg, &kt->relocate,
							NULL, 0)) {
						error(INFO,
						    "invalid --kaslr argument: %s\n",
						    optarg);
						program_usage(SHORT_FORM);
					}
					kt->relocate *= -1;
					kt->flags |= RELOC_SET;
					kt->flags2 |= KASLR;
				}

			} else if (STREQ(long_options[option_index].name, "reloc")) {
				if (!calculate(optarg, &kt->relocate, NULL, 0)) {
					error(INFO, "invalid --reloc argument: %s\n",
						optarg);
					program_usage(SHORT_FORM);
				}
				kt->flags |= RELOC_SET;
			}

			else if (STREQ(long_options[option_index].name, "minimal")) 
				pc->flags |= MINIMAL_MODE;

		        else if (STREQ(long_options[option_index].name, "kvmhost"))
				set_kvmhost_type(optarg);

		        else if (STREQ(long_options[option_index].name, "kvmio"))
				set_kvm_iohole(optarg);

		        else if (STREQ(long_options[option_index].name, "osrelease")) {
				pc->flags2 |= GET_OSRELEASE;
				get_osrelease(optarg);
			}

		        else if (STREQ(long_options[option_index].name, "log")) {
				pc->flags2 |= GET_LOG;
				get_log(optarg);
			}

			else if (STREQ(long_options[option_index].name, "hex")) {
				pc->flags2 |= RADIX_OVERRIDE;
				pc->output_radix = 16;
			}

			else if (STREQ(long_options[option_index].name, "dec")) {
				pc->flags2 |= RADIX_OVERRIDE;
				pc->output_radix = 10;
			}

			else if (STREQ(long_options[option_index].name, "offline")) {
				if (STREQ(optarg, "show"))
					pc->flags2 &= ~OFFLINE_HIDE;
				else if (STREQ(optarg, "hide"))
					pc->flags2 |= OFFLINE_HIDE;
				else {
					error(INFO, "invalid --offline argument: %s\n", optarg);
					program_usage(SHORT_FORM);
				}
			}

			else if (STREQ(long_options[option_index].name, "src"))
				kt->source_tree = optarg;

			else {
				error(INFO, "internal error: option %s unhandled\n",
					long_options[option_index].name);
				program_usage(SHORT_FORM);
			}
			break;

		case 'f':
			st->flags |= FORCE_DEBUGINFO;
			break;

		case 'g':
			pc->flags |= KERNEL_DEBUG_QUERY;
			break;

		case 'h':
			/* note: long_getopt's handling of optional arguments is weak.
			 * To it, an optional argument must be part of the same argument
			 * as the flag itself (eg. --help=commands or -hcommands).
			 * We want to accept "--help commands" or "-h commands".
			 * So we must do that part ourselves.
			 */
			if (optarg != NULL)
				cmd_usage(optarg, COMPLETE_HELP|PIPE_TO_SCROLL|MUST_HELP);
			else if (argv[optind] != NULL && argv[optind][0] != '-')
				cmd_usage(argv[optind++], COMPLETE_HELP|PIPE_TO_SCROLL|MUST_HELP);
			else
				program_usage(LONG_FORM);
			clean_exit(0);
			
		case 'k':
			pc->flags |= KERNTYPES;
			break;

		case 'e':
			if (STREQ(optarg, "vi"))
				pc->editing_mode = "vi";
			else if (STREQ(optarg, "emacs"))
				pc->editing_mode = "emacs";
			else
				fprintf(fp, "invalid edit mode: %s\n", optarg);	
			break;

		case 't':
			kt->flags2 |= GET_TIMESTAMP;
			break;

		case 'i':
			pc->input_file = optarg;
			pc->flags |= CMDLINE_IFILE;
			break;

		case 'v':
			pc->flags |= VERSION_QUERY;
			display_version();
			display_gdb_banner();
			clean_exit(0);

		case 's':
			pc->flags |= SILENT;
			pc->flags &= ~SCROLL;
//   			pc->scroll_command = SCROLL_NONE;   (why?)
			break;

		case 'L':
			if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1)
				perror("mlockall");
			break;

		case 'S':
			if (is_system_map("/boot/System.map")) {
                                pc->system_map = "/boot/System.map";
                                pc->flags |= (SYSMAP|SYSMAP_ARG);
			}
			break;	

		case 'c':
			create_console_device(optarg);
			break;

		case 'd': 
			pc->debug = atol(optarg);
			set_lkcd_debug(pc->debug);
			set_vas_debug(pc->debug);
			break;

		case 'p':
			force_page_size(optarg);
			break;

		case 'm':
			for (i = 0; i < MAX_MACHDEP_ARGS; i++) {
				if (machdep->cmdline_args[i])
					continue;
				machdep->cmdline_args[i] = optarg;
				break;
			}
			if (i == MAX_MACHDEP_ARGS)
				error(INFO, "option ignored: %s\n",
					optarg);
			break;

		case 'x':
			pc->flags |= PRELOAD_EXTENSIONS;
			break;

		case 'o':
			ramdump_elf_output_file(optarg);
			break;

		default:
			error(INFO, "invalid option: %s\n",
				argv[optind-1]);
			program_usage(SHORT_FORM);
		}
	}
	opterr = 1;

	display_version();

	/*
	 *  Take the kernel and dumpfile arguments in either order.
	 */
	while (argv[optind]) {

		if (is_ramdump(argv[optind])) {
			if (pc->flags & MEMORY_SOURCES) {
				error(INFO,
					"too many dumpfile arguments\n");
					program_usage(SHORT_FORM);
			}

			if (ACTIVE()) {
				pc->flags |= LIVE_RAMDUMP;
				pc->readmem = read_ramdump;
				pc->writemem = NULL;
				optind++;
				continue;
			}

			pc->dumpfile = ramdump_to_elf();
			if (is_kdump(pc->dumpfile, KDUMP_LOCAL)) {
				pc->flags |= KDUMP;
				if (is_ramdump_image())
					pc->readmem = read_ramdump;
				else
					pc->readmem = read_kdump;
				pc->writemem = NULL;
			} else {
				error(INFO, "malformed ELF file: %s\n",
					pc->dumpfile);
				program_usage(SHORT_FORM);
			}
			optind++;
			continue;
		}

		if (is_remote_daemon(argv[optind])) {
                	if (pc->flags & DUMPFILE_TYPES) {
				error(INFO, 
				      "too many dumpfile/memory arguments\n");
				program_usage(SHORT_FORM);
			}
			pc->flags2 |= REMOTE_DAEMON;
			optind++;
			continue;
		}

		if (STREQ(argv[optind], "/dev/crash")) {
			pc->memory_device = argv[optind];
			optind++;
			continue;
		}

		if (!file_exists(argv[optind], NULL)) {
			error(INFO, "%s: %s\n", argv[optind], strerror(ENOENT));
			program_usage(SHORT_FORM);
		} else if (is_directory(argv[optind])) {
			error(INFO, "%s: not a supported file format\n", 
				argv[optind]);
			program_usage(SHORT_FORM);
		} else if (!is_readable(argv[optind])) 
			program_usage(SHORT_FORM);

		if (is_kernel(argv[optind])) {
			if (pc->namelist || pc->server_namelist) {
				if (!select_namelist(argv[optind])) {
                               		error(INFO, 
					    "too many namelist arguments\n");
                               		program_usage(SHORT_FORM);
				}
			} else
				pc->namelist = argv[optind];

		} else if (is_compressed_kernel(argv[optind], &tmpname)) {
			if (pc->namelist) {
				if (!select_namelist(tmpname)) {
					error(INFO, 
					    "too many namelist arguments\n");
					program_usage(SHORT_FORM);
				}
				if (pc->namelist_debug == tmpname) {
					pc->namelist_debug_orig = argv[optind];
				} else {
					pc->namelist_debug_orig = pc->namelist_orig;
					pc->namelist_orig = argv[optind];
				}
			} else {
				pc->namelist = tmpname;
				pc->namelist_orig = argv[optind];
			}
			pc->cleanup = NULL;

		} else if (!(pc->flags & KERNEL_DEBUG_QUERY)) {

			if (is_flattened_format(argv[optind]))
				pc->flags2 |= FLAT;

			if (STREQ(argv[optind], "/dev/mem")) {
                        	if (pc->flags & MEMORY_SOURCES) {
                                	error(INFO, 
                                            "too many dumpfile arguments\n");
                                	program_usage(SHORT_FORM);
                        	}
				pc->flags |= DEVMEM;
				pc->dumpfile = NULL;
				pc->readmem = read_dev_mem;
				pc->writemem = write_dev_mem;
				pc->live_memsrc = argv[optind];

			} else if (is_proc_kcore(argv[optind], KCORE_LOCAL)) {
				if (pc->flags & MEMORY_SOURCES) {
					error(INFO, 
					    "too many dumpfile arguments\n");
					program_usage(SHORT_FORM);
				}
				pc->flags |= PROC_KCORE;
				pc->dumpfile = NULL;
				pc->readmem = read_proc_kcore;
				pc->writemem = write_proc_kcore;
				pc->live_memsrc = argv[optind];

			} else if (is_netdump(argv[optind], NETDUMP_LOCAL)) {
                                if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= NETDUMP;
                                pc->dumpfile = argv[optind];

				if (is_sadump_xen()) {
					pc->readmem = read_kdump;
					pc->writemem = write_kdump;
				} else {
					pc->readmem = read_netdump;
					pc->writemem = write_netdump;
				}

                        } else if (is_kdump(argv[optind], KDUMP_LOCAL)) {
                                if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= KDUMP;
                                pc->dumpfile = argv[optind];
                                pc->readmem = read_kdump;
                                pc->writemem = write_kdump;

                        } else if (is_kvmdump(argv[optind])) {
                                if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= KVMDUMP;
                                pc->dumpfile = argv[optind];
                                pc->readmem = read_kvmdump;
                                pc->writemem = write_kvmdump;

			} else if (is_kvmdump_mapfile(argv[optind])) {
				if (pc->kvmdump_mapfile) {
                                        error(INFO,
                                            "too many KVM map file arguments\n");
                                        program_usage(SHORT_FORM);
				}
				pc->kvmdump_mapfile = argv[optind];
                                
                        } else if (is_xendump(argv[optind])) {
                                if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= XENDUMP;
                                pc->dumpfile = argv[optind];
                                pc->readmem = read_xendump;
                                pc->writemem = write_xendump;

                        } else if (is_system_map(argv[optind])) {
                                pc->system_map = argv[optind];
                                pc->flags |= (SYSMAP|SYSMAP_ARG);

			} else if (is_diskdump(argv[optind])) {
                                if ((pc->flags & MEMORY_SOURCES) &&
                                    (!dumpfile_is_split())) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= DISKDUMP;
                                pc->dumpfile = argv[optind];
                                pc->readmem = read_diskdump;
                                pc->writemem = write_diskdump;

			} else if (is_lkcd_compressed_dump(argv[optind])) {
				if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= LKCD;
                                pc->dumpfile = argv[optind];
				pc->readmem = read_lkcd_dumpfile;
				pc->writemem = write_lkcd_dumpfile;

			} else if (is_mclx_compressed_dump(argv[optind])) {
				if (pc->flags & MEMORY_SOURCES) {
					error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
				pc->flags |= MCLXCD;
				pc->dumpfile = argv[optind];
				pc->readmem = read_mclx_dumpfile;
				pc->writemem = write_mclx_dumpfile;

                        } else if (is_s390_dump(argv[optind])) {
                                if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
                                pc->flags |= S390D;
                                pc->dumpfile = argv[optind];
                                pc->readmem = read_s390_dumpfile;
                                pc->writemem = write_s390_dumpfile;

			} else if (is_sadump(argv[optind])) {
				if ((pc->flags & MEMORY_SOURCES) &&
				    !sadump_is_diskset()) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
				}
				pc->flags |= SADUMP;
				pc->dumpfile = argv[optind];
				pc->readmem = read_sadump;
				pc->writemem = write_sadump;

			} else if (is_vmware_vmss(argv[optind])) {
                                if (pc->flags & MEMORY_SOURCES) {
                                        error(INFO,
                                            "too many dumpfile arguments\n");
                                        program_usage(SHORT_FORM);
                                }
				pc->flags |= VMWARE_VMSS;
				pc->dumpfile = argv[optind];
				pc->readmem = read_vmware_vmss;
				pc->writemem = write_vmware_vmss;

			} else { 
				error(INFO, 
				    "%s: not a supported file format\n",
					argv[optind]);
				program_usage(SHORT_FORM);
			}
		}
		optind++;
	}
	
	check_xen_hyper();

        if (setjmp(pc->main_loop_env))
                clean_exit(1);

	/*
	 *  Initialize various subsystems.
	 */
	fd_init();
	buf_init();
        cmdline_init();
        mem_init();
       	hq_init();
	machdep_init(PRE_SYMTAB);
        symtab_init();
	paravirt_init();
	machdep_init(PRE_GDB);
        datatype_init();

	/*
	 *  gdb_main_loop() modifies "command_loop_hook" to point to the 
         *  main_loop() function below, and then calls gdb's main() function.
         *  After gdb initializes itself, it calls back to main_loop().
	 */
	gdb_main_loop(argc, argv);   

	clean_exit(0);
	exit(0); 
}

/*
 *  This routine is called from above, but also will be re-entered
 *  as part of gdb's SIGINT handling.  Since GDB_INIT and RUNTIME 
 *  will be set on re-entrancy, the initialization routines won't 
 *  be called.  This can be avoided by always making gdb ignore SIGINT.
 */
void
main_loop(void)
{
	if (pc->flags2 & ERASEINFO_DATA)
		error(WARNING, "\n%s:\n         "
		    "Kernel data has been erased from this dumpfile.  This may "
		    "cause\n         the crash session to fail entirely, may "
                    "cause commands to fail,\n         or may result in "
		    "unpredictable\n         runtime behavior.\n",
			pc->dumpfile);

	if (pc->flags2 & INCOMPLETE_DUMP) {
		error(WARNING, "\n%s:\n         "
		    "This dumpfile is incomplete.  This may cause the crash session"
		    "\n         to fail entirely, may cause commands to fail, or may"
		    " result in\n         unpredictable runtime behavior.\n",
			pc->dumpfile);
		if (!(*diskdump_flags & ZERO_EXCLUDED))
			fprintf(fp,
			    "   NOTE: This dumpfile may be analyzed with the --zero_excluded command\n"
			    "         line option, in which case any read requests from missing pages\n"
			    "         will return zero-filled memory.\n");
	}

	if (pc->flags2 & EXCLUDED_VMEMMAP) {
		error(WARNING, "\n%s:\n         "
		    "This dumpfile is incomplete because the page structures associated\n"
                    "         with excluded pages may also be excluded.  This may cause the crash\n"
		    "         session to fail entirely, may cause commands to fail (most notably\n"
		    "         the \"kmem\" command), or may result in unpredictable runtime behavior.\n",
			pc->dumpfile);

	}

        if (!(pc->flags & GDB_INIT)) {
		gdb_session_init();
		machdep_init(POST_RELOC);
		show_untrusted_files();
		kdump_backup_region_init();
		if (XEN_HYPER_MODE()) {
#ifdef XEN_HYPERVISOR_ARCH
			machdep_init(POST_GDB);
			xen_hyper_init();
			machdep_init(POST_INIT);
#else
        		error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
#endif
		} else if (!(pc->flags & MINIMAL_MODE)) {
			read_in_kernel_config(IKCFG_INIT);
			kernel_init();
			machdep_init(POST_GDB);
        		vm_init();
			machdep_init(POST_VM);
        		module_init();
        		help_init();
        		task_init();
        		vfs_init();
			net_init();
			dev_init();
			machdep_init(POST_INIT);
		}
	} else
		SIGACTION(SIGINT, restart, &pc->sigaction, NULL);

        /*
         *  Display system statistics and current context.
         */
        if (!(pc->flags & SILENT) && !(pc->flags & RUNTIME)) {
		if (XEN_HYPER_MODE()) {
#ifdef XEN_HYPERVISOR_ARCH
			xen_hyper_display_sys_stats();
			xen_hyper_show_vcpu_context(XEN_HYPER_VCPU_LAST_CONTEXT());
                	fprintf(fp, "\n");
#else
        		error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
#endif
		} else if (!(pc->flags & MINIMAL_MODE)) {
			display_sys_stats();
			show_context(CURRENT_CONTEXT());
                	fprintf(fp, "\n");
		}
        }

	if (pc->flags & MINIMAL_MODE)
            error(NOTE, 
		"minimal mode commands: log, dis, rd, sym, eval, set, extend and exit\n\n");

        pc->flags |= RUNTIME;

	if (pc->flags & PRELOAD_EXTENSIONS)
		preload_extensions();

	/*
	 *  Return here if a non-recoverable error occurs
	 *  during command execution.
	 */
	if (setjmp(pc->main_loop_env)) {
		;
	}

	/*
	 *  process_command_line() reads, parses and stores input command lines
	 *  in the global args[] array.  exec_command() figures out what to 
         *  do with the parsed line.
	 */
	while (TRUE) {
		process_command_line();
		exec_command();
	}
}

/*
 *  Most of the time args[0] simply contains the name string of a command
 *  found in the global command_table[].  Special consideration is done for 
 *  dealing with input files, "known" external commands, and built-in commands.
 *  If none of the above apply, the args[0] string is checked against the
 *  known list of structure, union and typedef names, and if found, passed
 *  on to cmd_struct(), cmd_union() or cmd_whatis().
 */
void
exec_command(void)
{
	struct command_table_entry *ct;
	struct args_input_file args_ifile;

        if (args[0] && (args[0][0] == '\\') && args[0][1]) {
		shift_string_left(args[0], 1);
                shift_string_left(pc->orig_line, 1);
		pc->curcmd_flags |= NO_MODIFY;
	}

reattempt:
	if (!args[0])
		return;

	optind = argerrs = 0;

	if ((ct = get_command_table_entry(args[0]))) {
                if (ct->flags & REFRESH_TASK_TABLE) {
			if (XEN_HYPER_MODE()) {
#ifdef XEN_HYPERVISOR_ARCH
				xen_hyper_refresh_domain_context_space();
				xen_hyper_refresh_vcpu_context_space();
#else
        			error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
#endif
			} else if (!(pc->flags & MINIMAL_MODE)) {
				tt->refresh_task_table();
				sort_context_array();
				sort_tgid_array();	
			}
		}
                if (!STREQ(pc->curcmd, pc->program_name))
                        pc->lastcmd = pc->curcmd;
                pc->curcmd = ct->name;
		pc->cmdgencur++;

		if (is_args_input_file(ct, &args_ifile))
			exec_args_input_file(ct, &args_ifile);
		else
			(*ct->func)();

                pc->lastcmd = pc->curcmd;
                pc->curcmd = pc->program_name;
                return;
	}

	if (is_input_file())
		return;

	if (is_external_command())
		return;

	if (is_builtin_command())
		return;

        if (is_datatype_command()) 
                goto reattempt;

	if (STRNEQ(args[0], "#") || STRNEQ(args[0], "//"))
		return;

	if (!(pc->flags & MINIMAL_MODE) &&
	    is_gdb_command(TRUE, FAULT_ON_ERROR)) 
		goto reattempt;

	if (REMOTE() && remote_execute())
		return;

	pc->curcmd = pc->program_name;

	if (pc->flags & MINIMAL_MODE)
		error(INFO, 
		    "%s: command not available in minimal mode\n"
		    "NOTE: minimal mode commands: log, dis, rd, sym, eval, set, extend and exit\n",
			args[0]);
	else
		error(INFO, "command not found: %s\n", args[0]);

	if (pc->curcmd_flags & REPEAT)
		pc->curcmd_flags &= ~REPEAT;
}


/*
 *  Find the command_table structure associated with a command name.
 */
struct command_table_entry *
get_command_table_entry(char *name)
{       
	int i;
        struct command_table_entry *cp;
        struct extension_table *ext;

	if (pc->flags2 & GDB_CMD_MODE) {
		if (STREQ(name, "crash")) {
			if (argcnt == 1)
				error(FATAL, 
				    "a crash command must follow "
				    "the \"crash\" directive\n");
			for (i = 1; i <= argcnt; i++)
				args[i-1] = args[i];
			argcnt--;
			name = args[0];
		} else
			name = "gdb";
	}
	
	for (cp = pc->cmd_table; cp->name; cp++) {
                if (STREQ(cp->name, name)) {
			if (!(pc->flags & MINIMAL_MODE) || (cp->flags & MINIMAL))
				return cp;
			else
				return NULL;
		}
        }
                
        for (ext = extension_table; ext; ext = ext->next) {
                for (cp = ext->command_table; cp->name; cp++) {
                        if (STREQ(cp->name, name)) {
				if (!(pc->flags & MINIMAL_MODE) || (cp->flags & MINIMAL))
					return cp;
				else
					return NULL;
			}
                }
        }       

        return NULL;
}


static int
is_input_file(void)
{
        if (STREQ(args[0], "<")) {
                exec_input_file();
                return TRUE;
        }

	return FALSE;
}

static int
is_builtin_command(void)
{
	int i;
	struct remote_file remote_file, *rfp;

	/*
	 *  cmd_test() is used strictly for debugging -- but not advertised
	 *  in the help menu.
	 */ 
        if (STREQ(args[0], "test")) {
		pc->curcmd = "test";
                cmd_test();
                return TRUE;
        }

        if (STREQ(args[0], "save")) {
		pc->curcmd = "save";
		rfp = &remote_file;
		BZERO(rfp, sizeof(struct remote_file));
		rfp->flags |= REMOTE_VERBOSE;
		for (i = 1; i < argcnt; i++) {
			rfp->filename = args[i];
			get_remote_file(rfp); 
		}
		return TRUE;
	}

	return FALSE;
}

/*
 *  Pure laziness -- to avoid having to type the exclamation point at the
 *  beginning of the line.
 */
static int
is_external_command(void)
{
	int i;
	char *cmd;
	char command[BUFSIZE];

	cmd = args[0];

        if (STREQ(cmd, "vi") ||
            STREQ(cmd, "pwd") ||
            STREQ(cmd, "grep") ||
            STREQ(cmd, "cat") ||
            STREQ(cmd, "more") ||
            STREQ(cmd, "less") ||
	    STREQ(cmd, "echo") ||
            STREQ(cmd, "ls")) {
                sprintf(command, "%s", cmd);
                for (i = 1; i < argcnt; i++) {
                        strcat(command, " ");
			if (strstr(args[i], " ")) {
				strcat(command, "\"");
                        	strcat(command, args[i]);
				strcat(command, "\"");
			}
			else
                        	strcat(command, args[i]);
                }
                if (system(command) == -1)
			perror(command);
                return TRUE;
        }

	return FALSE;
}

void
cmd_quit(void)
{
	if (REMOTE())
		remote_exit();

	clean_exit(0);
}

void
cmd_mach(void)
{
	machdep->cmd_mach();
}


static void
setup_environment(int argc, char **argv)
{
	int i;
	char *p1;
	char buf[BUFSIZE];
	char homerc[BUFSIZE];
	char localrc[BUFSIZE];
	FILE *afp;
	char *program;

	program = argv[0];

	/*
	 *  Program output typically goes via "fprintf(fp, ...)", but the 
	 *  contents of fp are modified on the fly to handle redirection
	 *  to pipes or output files.
	 */
	fp = stdout;

	/*
	 *  Start populating the program_context structure.  It's used so
	 *  frequently that "pc" has been declared globally to point to the
	 *  "program_context" structure.
	 */
        pc->program_name = (char *)basename(program);
	pc->program_path = program;
        pc->program_version = build_version;
	pc->program_pid = (ulong)getpid();
        pc->curcmd = pc->program_name;
        pc->flags = (HASH|SCROLL);
	pc->flags |= DATADEBUG;          /* default until unnecessary */
	pc->flags2 |= REDZONE;
	pc->confd = -2;
	pc->machine_type = MACHINE_TYPE;
	pc->readmem = read_dev_mem;      /* defaults until argv[] is parsed */
	pc->writemem = write_dev_mem;
	pc->read_vmcoreinfo = no_vmcoreinfo;
	pc->memory_module = NULL;
	pc->memory_device = MEMORY_DRIVER_DEVICE;
	machdep->bits = sizeof(long) * 8;
	machdep->verify_paddr = generic_verify_paddr;
	machdep->get_kvaddr_ranges = generic_get_kvaddr_ranges;
	pc->redhat_debug_loc = DEFAULT_REDHAT_DEBUG_LOCATION;
	pc->cmdgencur = 0;
	pc->cmd_table = linux_command_table;
	kt->BUG_bytes = -1;
	kt->flags |= PRE_KERNEL_INIT;

	/*
	 *  Set up to perform a clean_exit() upon parent death.
	 */
	SIGACTION(SIGUSR2, restart, &pc->sigaction, NULL);
	prctl(PR_SET_PDEATHSIG, SIGUSR2);

	/*
	 *  Get gdb version before initializing it since this might be one 
         *  of the short-hand commands that need it without running gdb.
	 */
	get_gdb_version();

	/* 
	 *  Set up the default scrolling behavior for terminal output.
	 */
	if (isatty(fileno(stdout))) {
		if (CRASHPAGER_valid()) {
			pc->flags |= SCROLL;
			pc->scroll_command = SCROLL_CRASHPAGER;
		} else if (file_exists("/usr/bin/less", NULL)) {
			pc->flags |= SCROLL;
			pc->scroll_command = SCROLL_LESS;
		} else if (file_exists("/bin/more", NULL)) {
			pc->flags |= SCROLL;
			pc->scroll_command = SCROLL_MORE;
		} else {
                	pc->scroll_command = SCROLL_NONE;
                	pc->flags &= ~SCROLL;
        	}
	} 

	/*
	 *  Setup the readline command line editing mode based upon the 
	 *  following order:
	 *
	 *   (1) EDITOR environment variable
         *   (2) overridden by any .crashrc entry: "set vi" or "set emacs"
         *   (3) RL_VI_MODE if not set anywhere else
	 */

	pc->flags |= READLINE;
	pc->editing_mode = "no_mode";

	if ((p1 = getenv("EDITOR"))) {
		if (strstr(p1, "vi"))
			pc->editing_mode = "vi";
		if (strstr(p1, "emacs"))
			pc->editing_mode = "emacs";
	}

	/*
	 *  Resolve $HOME .rc file first, then the one in the local directory.
         *  Note that only "set" and "alias" commands are done at this time.
	 */
	for (i = 1; i < argc; i++)
		if (STREQ(argv[i], "--no_crashrc"))
			pc->flags |= NOCRASHRC; 

	alias_init(NULL);

	if ((p1 = getenv("HOME"))) {
		if ((pc->home = (char *)malloc(strlen(p1)+1)) == NULL) {
                        error(INFO, "home directory malloc: %s\n",
                                strerror(errno));
			pc->home = "(unknown)";
		} else
			strcpy(pc->home, p1);
	        sprintf(homerc, "%s/.%src", pc->home, pc->program_name);
	        if (!(pc->flags & NOCRASHRC) && file_exists(homerc, NULL)) {
	                if ((afp = fopen(homerc, "r")) == NULL)
	                        error(INFO, "cannot open %s: %s\n",
	                                homerc, strerror(errno));
			else if (untrusted_file(afp, homerc))
				fclose(afp);
	                else {
	                        while (fgets(buf, BUFSIZE, afp))
	                                resolve_rc_cmd(buf, ALIAS_RCHOME);
	                        fclose(afp);
	                }
	        }
	}

        sprintf(localrc, ".%src", pc->program_name);
	if (!same_file(homerc, localrc) && 
	    !(pc->flags & NOCRASHRC) && file_exists(localrc, NULL)) {
		if ((afp = fopen(localrc, "r")) == NULL)
                        error(INFO, "cannot open %s: %s\n",
				localrc, strerror(errno));
		else if (untrusted_file(afp, localrc))
			fclose(afp);
		else {
			while (fgets(buf, BUFSIZE, afp)) 
				resolve_rc_cmd(buf, ALIAS_RCLOCAL);
			fclose(afp);
		}
	}

	if (STREQ(pc->editing_mode, "no_mode"))
		pc->editing_mode = "vi";

	machdep_init(SETUP_ENV);
}


/*
 *  "help -p" output
 */
void
dump_program_context(void)
{
	int i;
	int others = 0;
	char *p1;
	char buf[BUFSIZE];
	char buf2[BUFSIZE];

	fprintf(fp, "     program_name: %s\n", pc->program_name);
	fprintf(fp, "     program_path: %s\n", pc->program_path);
	fprintf(fp, "  program_version: %s\n", pc->program_version);
	fprintf(fp, "      gdb_version: %s\n", pc->gdb_version);
	fprintf(fp, "      program_pid: %ld\n", pc->program_pid);
	fprintf(fp, "           prompt: \"%s\"\n", pc->prompt);
	fprintf(fp, "            flags: %llx ", pc->flags);

	if (pc->flags)
		sprintf(buf, "(");
	if (pc->flags & RUNTIME)
		sprintf(&buf[strlen(buf)], "%sRUNTIME", others++ ? "|" : "");
	if (pc->flags & LIVE_SYSTEM)
		sprintf(&buf[strlen(buf)], "%sLIVE_SYSTEM", 
			others++ ? "|" : "");
	if (pc->flags & TTY)
		sprintf(&buf[strlen(buf)], "%sTTY", others++ ? "|" : "");
        if (pc->flags & IN_FOREACH)
                sprintf(&buf[strlen(buf)], "%sIN_FOREACH", others++ ? "|" : "");
        if (pc->flags & MFD_RDWR)
                sprintf(&buf[strlen(buf)], "%sMFD_RDWR", others++ ? "|" : "");
        if (pc->flags & KVMDUMP)
                sprintf(&buf[strlen(buf)], "%sKVMDUMP", others++ ? "|" : "");
        if (pc->flags & SILENT)
                sprintf(&buf[strlen(buf)], "%sSILENT", others++ ? "|" : "");
        if (pc->flags & HASH)
                sprintf(&buf[strlen(buf)], "%sHASH", others++ ? "|" : "");
        if (pc->flags & SCROLL)
                sprintf(&buf[strlen(buf)], "%sSCROLL", others++ ? "|" : "");
        if (pc->flags & NO_CONSOLE)
                sprintf(&buf[strlen(buf)], "%sNO_CONSOLE", others++ ? "|" : "");
        if (pc->flags & MCLXCD)
                sprintf(&buf[strlen(buf)], "%sMCLXCD", others++ ? "|" : "");
        if (pc->flags & RUNTIME_IFILE)
                sprintf(&buf[strlen(buf)], "%sRUNTIME_IFILE", 
			others++ ? "|" : "");
        if (pc->flags & CMDLINE_IFILE)
                sprintf(&buf[strlen(buf)], "%sCMDLINE_IFILE", 
			others++ ? "|" : "");
        if (pc->flags & DROP_CORE)
                sprintf(&buf[strlen(buf)], "%sDROP_CORE", others++ ? "|" : "");
        if (pc->flags & LKCD)
                sprintf(&buf[strlen(buf)], "%sLKCD", others++ ? "|" : "");
        if (pc->flags & GDB_INIT)
                sprintf(&buf[strlen(buf)], "%sGDB_INIT", others++ ? "|" : "");
        if (pc->flags & IN_GDB)
                sprintf(&buf[strlen(buf)], "%sIN_GDB", others++ ? "|" : "");
	if (pc->flags & RCHOME_IFILE)
                sprintf(&buf[strlen(buf)], "%sRCHOME_IFILE", 
			others++ ? "|" : "");
	if (pc->flags & RCLOCAL_IFILE)
                sprintf(&buf[strlen(buf)], "%sRCLOCAL_IFILE", 
			others++ ? "|" : "");
	if (pc->flags & READLINE)
                sprintf(&buf[strlen(buf)], "%sREADLINE", others++ ? "|" : "");
        if (pc->flags & _SIGINT_)
                sprintf(&buf[strlen(buf)], 
			"%s_SIGINT_", others++ ? "|" : "");
        if (pc->flags & IN_RESTART)
                sprintf(&buf[strlen(buf)], "%sIN_RESTART", others++ ? "|" : "");
        if (pc->flags & KERNEL_DEBUG_QUERY)
                sprintf(&buf[strlen(buf)], 
			"%sKERNEL_DEBUG_QUERY", others++ ? "|" : "");
        if (pc->flags & DEVMEM)
                sprintf(&buf[strlen(buf)], 
			"%sDEVMEM", others++ ? "|" : "");
        if (pc->flags & MEMMOD)
                sprintf(&buf[strlen(buf)], 
			"%sMEMMOD", others++ ? "|" : "");
        if (pc->flags & MODPRELOAD)
                sprintf(&buf[strlen(buf)], 
			"%sMODPRELOAD", others++ ? "|" : "");
        if (pc->flags & REM_LIVE_SYSTEM)
                sprintf(&buf[strlen(buf)],
                        "%sREM_LIVE_SYSTEM", others++ ? "|" : "");
        if (pc->flags & NAMELIST_LOCAL)
                sprintf(&buf[strlen(buf)],
                        "%sNAMELIST_LOCAL", others++ ? "|" : "");
        if (pc->flags & DUMPFILE_SAVED)
                sprintf(&buf[strlen(buf)],
                        "%sDUMPFILE_SAVED", others++ ? "|" : "");
        if (pc->flags & NAMELIST_SAVED)
                sprintf(&buf[strlen(buf)],
                        "%sNAMELIST_SAVED", others++ ? "|" : "");
        if (pc->flags & UNLINK_NAMELIST)
                sprintf(&buf[strlen(buf)],
                        "%sUNLINK_NAMELIST", others++ ? "|" : "");
        if (pc->flags & NAMELIST_UNLINKED)
                sprintf(&buf[strlen(buf)],
                        "%sNAMELIST_UNLINKED", others++ ? "|" : "");
        if (pc->flags & REM_MCLXCD)
                sprintf(&buf[strlen(buf)],
                        "%sREM_MCLXCD", others++ ? "|" : "");
        if (pc->flags & REM_LKCD)
                sprintf(&buf[strlen(buf)],
                        "%sREM_LKCD", others++ ? "|" : "");
        if (pc->flags & NAMELIST_NO_GZIP)
                sprintf(&buf[strlen(buf)],
                        "%sNAMELIST_NO_GZIP", others++ ? "|" : "");
        if (pc->flags & UNLINK_MODULES)
                sprintf(&buf[strlen(buf)],
                        "%sUNLINK_MODULES", others++ ? "|" : "");
        if (pc->flags & S390D)
                sprintf(&buf[strlen(buf)],
                        "%sS390D", others++ ? "|" : "");
        if (pc->flags & REM_S390D)
                sprintf(&buf[strlen(buf)],
                        "%sREM_S390D", others++ ? "|" : "");
        if (pc->flags & NETDUMP)
                sprintf(&buf[strlen(buf)],
                        "%sNETDUMP", others++ ? "|" : "");
        if (pc->flags & XENDUMP)
                sprintf(&buf[strlen(buf)],
                        "%sXENDUMP", others++ ? "|" : "");
        if (pc->flags & KDUMP)
                sprintf(&buf[strlen(buf)],
                        "%sKDUMP", others++ ? "|" : "");
        if (pc->flags & SADUMP)
                sprintf(&buf[strlen(buf)],
                        "%sSADUMP", others++ ? "|" : "");
        if (pc->flags & SYSRQ)
                sprintf(&buf[strlen(buf)],
                        "%sSYSRQ", others++ ? "|" : "");
        if (pc->flags & REM_NETDUMP)
                sprintf(&buf[strlen(buf)],
                        "%sREM_NETDUMP", others++ ? "|" : "");
        if (pc->flags & DISKDUMP)
                sprintf(&buf[strlen(buf)],
                        "%sDISKDUMP", others++ ? "|" : "");
        if (pc->flags & SYSMAP)
                sprintf(&buf[strlen(buf)],
                        "%sSYSMAP", others++ ? "|" : "");
        if (pc->flags & SYSMAP_ARG)
                sprintf(&buf[strlen(buf)],
                        "%sSYSMAP_ARG", others++ ? "|" : "");
        if (pc->flags & DATADEBUG)
                sprintf(&buf[strlen(buf)],
                        "%sDATADEBUG", others++ ? "|" : "");
	if (pc->flags & FINDKERNEL)
                sprintf(&buf[strlen(buf)],
                        "%sFINDKERNEL", others++ ? "|" : "");
        if (pc->flags & VERSION_QUERY)
                sprintf(&buf[strlen(buf)],
                        "%sVERSION_QUERY", others++ ? "|" : "");
        if (pc->flags & READNOW)
                sprintf(&buf[strlen(buf)],
                        "%sREADNOW", others++ ? "|" : "");
        if (pc->flags & NOCRASHRC)
                sprintf(&buf[strlen(buf)],
                        "%sNOCRASHRC", others++ ? "|" : "");
        if (pc->flags & INIT_IFILE)
                sprintf(&buf[strlen(buf)],
                        "%sINIT_IFILE", others++ ? "|" : "");
        if (pc->flags & XEN_HYPER)
                sprintf(&buf[strlen(buf)],
                        "%sXEN_HYPER", others++ ? "|" : "");
        if (pc->flags & XEN_CORE)
                sprintf(&buf[strlen(buf)],
                        "%sXEN_CORE", others++ ? "|" : "");
        if (pc->flags & PLEASE_WAIT)
                sprintf(&buf[strlen(buf)],
                        "%sPLEASE_WAIT", others++ ? "|" : "");
        if (pc->flags & IFILE_ERROR)
                sprintf(&buf[strlen(buf)],
                        "%sIFILE_ERROR", others++ ? "|" : "");
        if (pc->flags & MINIMAL_MODE)
                sprintf(&buf[strlen(buf)],
                        "%sMINIMAL_MODE", others++ ? "|" : "");
        if (pc->flags & CRASHBUILTIN)
                sprintf(&buf[strlen(buf)], 
			"%sCRASHBUILTIN", others++ ? "|" : "");
        if (pc->flags & PRELOAD_EXTENSIONS)
                sprintf(&buf[strlen(buf)], 
			"%sPRELOAD_EXTENSIONS", others++ ? "|" : "");
        if (pc->flags & PROC_KCORE)
                sprintf(&buf[strlen(buf)], 
			"%sPROC_KCORE", others++ ? "|" : "");

	if (pc->flags)
		strcat(buf, ")");

	if (strlen(buf)) {
		if (strlen(buf) > 46) {
			sprintf(buf2, "\n%s\n", 
				mkstring(buf, 80, CENTER|LJUST, NULL));
			if (strlen(buf2) <= 82) 
				fprintf(fp, "%s", buf2);
			else {
				for (i = strlen(buf2)-1; i; i--) {
					if ((buf2[i] == '|') && (i < 80))
						break;
				}

				strcpy(buf, buf2);
				buf[i+1] = NULLCHAR;
				fprintf(fp, "%s\n %s", buf, &buf2[i+1]);
			}
		}
		else
			fprintf(fp, "%s\n", buf);
	}

	others = 0;
	fprintf(fp, "           flags2: %llx (", pc->flags2);
	if (pc->flags2 & FLAT)
		fprintf(fp, "%sFLAT", others++ ? "|" : "");
	if (pc->flags2 & ELF_NOTES)
		fprintf(fp, "%sELF_NOTES", others++ ? "|" : "");
	if (pc->flags2 & GET_OSRELEASE)
		fprintf(fp, "%sGET_OSRELEASE", others++ ? "|" : "");
	if (pc->flags2 & REMOTE_DAEMON)
		fprintf(fp, "%sREMOTE_DAEMON", others++ ? "|" : "");
	if (pc->flags2 & LIVE_DUMP)
		fprintf(fp, "%sLIVE_DUMP", others++ ? "|" : "");
	if (pc->flags2 & RADIX_OVERRIDE)
		fprintf(fp, "%sRADIX_OVERRIDE", others++ ? "|" : "");
	if (pc->flags2 & QEMU_MEM_DUMP_ELF)
		fprintf(fp, "%sQEMU_MEM_DUMP_ELF", others++ ? "|" : "");
	if (pc->flags2 & QEMU_MEM_DUMP_COMPRESSED)
		fprintf(fp, "%sQEMU_MEM_DUMP_COMPRESSED", others++ ? "|" : "");
	if (pc->flags2 & GET_LOG)
		fprintf(fp, "%sGET_LOG", others++ ? "|" : "");
	if (pc->flags2 & VMCOREINFO)
		fprintf(fp, "%sVMCOREINFO", others++ ? "|" : "");
	if (pc->flags2 & ALLOW_FP)
		fprintf(fp, "%sALLOW_FP", others++ ? "|" : "");
	if (pc->flags2 & RAMDUMP)
		fprintf(fp, "%sRAMDUMP", others++ ? "|" : "");
	if (pc->flags2 & OFFLINE_HIDE)
		fprintf(fp, "%sOFFLINE_HIDE", others++ ? "|" : "");
	if (pc->flags2 & INCOMPLETE_DUMP)
		fprintf(fp, "%sINCOMPLETE_DUMP", others++ ? "|" : "");
	if (pc->flags2 & SNAP)
		fprintf(fp, "%sSNAP", others++ ? "|" : "");
	if (pc->flags2 & EXCLUDED_VMEMMAP)
		fprintf(fp, "%sEXCLUDED_VMEMMAP", others++ ? "|" : "");
        if (pc->flags2 & MEMSRC_LOCAL)
		fprintf(fp, "%sMEMSRC_LOCAL", others++ ? "|" : "");
	if (pc->flags2 & REDZONE)
		fprintf(fp, "%sREDZONE", others++ ? "|" : "");
	fprintf(fp, ")\n");

	fprintf(fp, "         namelist: %s\n", pc->namelist);
	fprintf(fp, "         dumpfile: %s\n", pc->dumpfile);
	fprintf(fp, "      live_memsrc: %s\n", pc->live_memsrc);
	fprintf(fp, "       system_map: %s\n", pc->system_map);
	fprintf(fp, "   namelist_debug: %s\n", pc->namelist_debug);
	fprintf(fp, "   debuginfo_file: %s\n", pc->debuginfo_file);
	fprintf(fp, "    namelist_orig: %s\n", pc->namelist_orig);
	fprintf(fp, "namelist_dbg_orig: %s\n", pc->namelist_debug_orig);
	fprintf(fp, "  kvmdump_mapfile: %s\n", pc->kvmdump_mapfile);
	fprintf(fp, "    memory_module: %s\n", pc->memory_module);
	fprintf(fp, "    memory_device: %s\n", pc->memory_device);
	fprintf(fp, "     machine_type: %s\n", pc->machine_type);
	fprintf(fp, "     editing_mode: %s\n", pc->editing_mode);
	fprintf(fp, "              nfd: %d\n", pc->nfd);
	fprintf(fp, "              mfd: %d\n", pc->mfd);
	fprintf(fp, "              kfd: %d\n", pc->kfd);
	fprintf(fp, "              dfd: %d\n", pc->dfd);
	fprintf(fp, "            confd: %d\n", pc->confd);
	fprintf(fp, "             home: %s\n", pc->home);
	fprintf(fp, "     command_line: ");
	if (STRNEQ(pc->command_line, args[0]))
		fprintf(fp, "%s\n", concat_args(buf, 0, FALSE));
	else
		fprintf(fp, "%s\n", pc->command_line);
	fprintf(fp, "        orig_line: %s\n", pc->orig_line);
	fprintf(fp, "        eoc_index: %d\n", pc->eoc_index);
	fprintf(fp, "         readline: %lx\n", (ulong)pc->readline);
	fprintf(fp, "           my_tty: %s\n", pc->my_tty);
	fprintf(fp, "            debug: %ld\n", pc->debug);
	fprintf(fp, "       debug_save: %ld\n", pc->debug_save);
	fprintf(fp, "          console: %s\n", pc->console);
	fprintf(fp, " redhat_debug_loc: %s\n", pc->redhat_debug_loc);
	fprintf(fp, "        pipefd[2]: %d,%d\n", pc->pipefd[0], pc->pipefd[1]);
	fprintf(fp, "           nullfp: %lx\n", (ulong)pc->nullfp);
	fprintf(fp, "          stdpipe: %lx\n", (ulong)pc->stdpipe);
	fprintf(fp, "             pipe: %lx\n", (ulong)pc->pipe);
	fprintf(fp, "            ifile: %lx\n", (ulong)pc->ifile);
	fprintf(fp, "            ofile: %lx\n", (ulong)pc->ofile);
	fprintf(fp, "       ifile_pipe: %lx\n", (ulong)pc->ifile_pipe);
	fprintf(fp, "      ifile_ofile: %lx\n", (ulong)pc->ifile_ofile);
	fprintf(fp, "       args_ifile: %lx\n", (ulong)pc->args_ifile);
	fprintf(fp, "       input_file: %s\n", pc->input_file);
	fprintf(fp, "ifile_in_progress: %lx (", pc->ifile_in_progress);
	others = 0;
	if (pc->ifile_in_progress & RCHOME_IFILE)
		fprintf(fp, "%sRCHOME_IFILE", others++ ? "|" : "");
	if (pc->ifile_in_progress & RCLOCAL_IFILE)
		fprintf(fp, "%sRCLOCAL_IFILE", others++ ? "|" : "");
	if (pc->ifile_in_progress & CMDLINE_IFILE)
		fprintf(fp, "%sCMDLINE_IFILE", others++ ? "|" : "");
	if (pc->ifile_in_progress & RUNTIME_IFILE)
		fprintf(fp, "%sRUNTIME_IFILE", others++ ? "|" : "");
	fprintf(fp, ")\n");
	fprintf(fp, "     ifile_offset: %lld\n", (ulonglong)pc->ifile_offset);
	fprintf(fp, "runtime_ifile_cmd: %s\n", pc->runtime_ifile_cmd ?
                pc->runtime_ifile_cmd : "(unused)");
	fprintf(fp, "   scroll_command: ");
	switch (pc->scroll_command) 
	{
	case SCROLL_NONE:
		fprintf(fp, "SCROLL_NONE\n");
		break;
	case SCROLL_LESS:
		fprintf(fp, "SCROLL_LESS\n");
		break;
	case SCROLL_MORE:
		fprintf(fp, "SCROLL_MORE\n");
		break;
	case SCROLL_CRASHPAGER:
		fprintf(fp, "SCROLL_CRASHPAGER (%s)\n", getenv("CRASHPAGER"));
		break;
	}

	buf[0] = NULLCHAR;
	fprintf(fp, "         redirect: %lx ", pc->redirect);
	if (pc->redirect)
		sprintf(buf, "(");
	others = 0;
	if (pc->redirect & FROM_COMMAND_LINE)
		sprintf(&buf[strlen(buf)], 
			"%sFROM_COMMAND_LINE", others++ ? "|" : "");
	if (pc->redirect & FROM_INPUT_FILE)
		sprintf(&buf[strlen(buf)], 
			"%sFROM_INPUT_FILE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_NOT_DONE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_NOT_DONE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_TO_PIPE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_TO_PIPE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_TO_STDPIPE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_TO_STDPIPE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_TO_FILE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_TO_FILE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_FAILURE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_FAILURE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_SHELL_ESCAPE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_SHELL_ESCAPE", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_SHELL_COMMAND)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_SHELL_COMMAND", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_PID_KNOWN)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_PID_KNOWN", others++ ? "|" : "");
	if (pc->redirect & REDIRECT_MULTI_PIPE)
		sprintf(&buf[strlen(buf)], 
			"%sREDIRECT_MULTI_PIPE", others++ ? "|" : "");
	if (pc->redirect)
		strcat(buf, ")");

        if (strlen(buf)) {
                if (strlen(buf) > 54)
                        fprintf(fp, "\n%s\n",
                                mkstring(buf, 80, CENTER|LJUST, NULL));
                else
                        fprintf(fp, "%s\n", buf);
        }

	if (!pc->redirect)
		fprintf(fp, "\n");

	fprintf(fp, "      stdpipe_pid: %d\n", pc->stdpipe_pid);
	fprintf(fp, "         pipe_pid: %d\n", pc->pipe_pid);
	fprintf(fp, "   pipe_shell_pid: %d\n", pc->pipe_shell_pid);
	fprintf(fp, "     pipe_command: %s\n", pc->pipe_command);
	if (pc->symfile && pc->symfile2) {
		fprintf(fp, "          symfile: %lx  (%ld)\n", 
			(ulong)pc->symfile, (ulong)ftell(pc->symfile));
		fprintf(fp, "         symfile2: %lx  (%ld)\n", 
			(ulong)pc->symfile2, (ulong)ftell(pc->symfile2));
	} else {
		fprintf(fp, "          symfile: %lx \n", (ulong)pc->symfile);
		fprintf(fp, "         symfile2: %lx \n", (ulong)pc->symfile2);
	}
	fprintf(fp, "          tmpfile: %lx\n", (ulong)pc->tmpfile);
	fprintf(fp, "         saved_fp: %lx\n", (ulong)pc->saved_fp);
	fprintf(fp, "           tmp_fp: %lx\n", (ulong)pc->tmp_fp);
	fprintf(fp, "         tmpfile2: %lx\n", (ulong)pc->tmpfile2);

	fprintf(fp, "        cmd_table: %s\n", XEN_HYPER_MODE() ?
		"xen_hyper_command_table" : "linux_command_table");
	fprintf(fp, "           curcmd: %s\n", pc->curcmd);
	fprintf(fp, "          lastcmd: %s\n", pc->lastcmd);
	fprintf(fp, "      cur_gdb_cmd: %d  %s\n", pc->cur_gdb_cmd,
		gdb_command_string(pc->cur_gdb_cmd, buf, FALSE));
	fprintf(fp, "     last_gdb_cmd: %d  %s\n", pc->last_gdb_cmd,
		gdb_command_string(pc->last_gdb_cmd, buf, FALSE));
	fprintf(fp, "          cur_req: %lx\n", (ulong)pc->cur_req);
	fprintf(fp, "        cmdgencur: %ld\n", pc->cmdgencur); 
	fprintf(fp, "     curcmd_flags: %lx (", pc->curcmd_flags);
	others = 0;
        if (pc->curcmd_flags & XEN_MACHINE_ADDR)
		fprintf(fp, "%sXEN_MACHINE_ADDR", others ? "|" : "");
        if (pc->curcmd_flags & REPEAT)
		fprintf(fp, "%sREPEAT", others ? "|" : "");
        if (pc->curcmd_flags & IDLE_TASK_SHOWN)
		fprintf(fp, "%sIDLE_TASK_SHOWN", others ? "|" : "");
        if (pc->curcmd_flags & TASK_SPECIFIED)
		fprintf(fp, "%sTASK_SPECIFIED", others ? "|" : "");
        if (pc->curcmd_flags & MEMTYPE_UVADDR)
		fprintf(fp, "%sMEMTYPE_UVADDR", others ? "|" : "");
        if (pc->curcmd_flags & MEMTYPE_FILEADDR)
		fprintf(fp, "%sMEMTYPE_FILEADDR", others ? "|" : "");
        if (pc->curcmd_flags & HEADER_PRINTED)
		fprintf(fp, "%sHEADER_PRINTED", others ? "|" : "");
        if (pc->curcmd_flags & BAD_INSTRUCTION)
		fprintf(fp, "%sBAD_INSTRUCTION", others ? "|" : "");
        if (pc->curcmd_flags & UD2A_INSTRUCTION)
		fprintf(fp, "%sUD2A_INSTRUCTION", others ? "|" : "");
        if (pc->curcmd_flags & IRQ_IN_USE)
		fprintf(fp, "%sIRQ_IN_USE", others ? "|" : "");
        if (pc->curcmd_flags & IGNORE_ERRORS)
		fprintf(fp, "%sIGNORE_ERRORS", others ? "|" : "");
        if (pc->curcmd_flags & FROM_RCFILE)
		fprintf(fp, "%sFROM_RCFILE", others ? "|" : "");
        if (pc->curcmd_flags & MEMTYPE_KVADDR)
		fprintf(fp, "%sMEMTYPE_KVADDR", others ? "|" : "");
        if (pc->curcmd_flags & NO_MODIFY)
		fprintf(fp, "%sNO_MODIFY", others ? "|" : "");
        if (pc->curcmd_flags & MOD_SECTIONS)
		fprintf(fp, "%sMOD_SECTIONS", others ? "|" : "");
        if (pc->curcmd_flags & MOD_READNOW)
		fprintf(fp, "%sMOD_READNOW", others ? "|" : "");
        if (pc->curcmd_flags & MM_STRUCT_FORCE)
		fprintf(fp, "%sMM_STRUCT_FORCE", others ? "|" : "");
        if (pc->curcmd_flags & CPUMASK)
		fprintf(fp, "%sCPUMASK", others ? "|" : "");
        if (pc->curcmd_flags & PARTIAL_READ_OK)
		fprintf(fp, "%sPARTIAL_READ_OK", others ? "|" : "");
	fprintf(fp, ")\n");
	fprintf(fp, "   curcmd_private: %llx\n", pc->curcmd_private); 
	fprintf(fp, "      cmd_cleanup: %lx\n", (ulong)pc->cmd_cleanup);
	fprintf(fp, "  cmd_cleanup_arg: %lx\n", (ulong)pc->cmd_cleanup_arg);
	fprintf(fp, "       sigint_cnt: %d\n", pc->sigint_cnt);
	fprintf(fp, "        sigaction: %lx\n", (ulong)&pc->sigaction);
	fprintf(fp, "    gdb_sigaction: %lx\n", (ulong)&pc->gdb_sigaction);
	fprintf(fp, "    main_loop_env: %lx\n", (ulong)&pc->main_loop_env);
	fprintf(fp, " foreach_loop_env: %lx\n", (ulong)&pc->foreach_loop_env);
	fprintf(fp, "gdb_interface_env: %lx\n", (ulong)&pc->gdb_interface_env);
	fprintf(fp, "     termios_orig: %lx\n", (ulong)&pc->termios_orig);
	fprintf(fp, "      termios_raw: %lx\n", (ulong)&pc->termios_raw);
	fprintf(fp, "            ncmds: %d\n", pc->ncmds);
	fprintf(fp, "          cmdlist: %lx\n", (ulong)pc->cmdlist);
	fprintf(fp, "        cmdlistsz: %d\n", pc->cmdlistsz);
	fprintf(fp, "     output_radix: %d (%s)\n", pc->output_radix,
		pc->output_radix == 16 ? 
		"hex" : ((pc->output_radix == 10) ? "decimal" : "???"));

	fprintf(fp, "           server: %s\n", pc->server);
	fprintf(fp, "       server_pid: %ld\n", pc->server_pid);
	fprintf(fp, "             port: %d\n", pc->port);
	fprintf(fp, "           sockfd: %d\n", pc->sockfd);
	fprintf(fp, "    server_memsrc: %s\n", pc->server_memsrc);
	fprintf(fp, "  server_namelist: %s\n", pc->server_namelist);
	fprintf(fp, "             rmfd: %d\n", pc->rmfd);
	fprintf(fp, "             rkfd: %d\n", pc->rkfd);
	fprintf(fp, "       rcvbufsize: %ld\n", pc->rcvbufsize);

	fprintf(fp, "          readmem: ");
	if ((p1 = readmem_function_name()))
		fprintf(fp, "%s()\n", p1);
	else
		fprintf(fp, "%lx\n", (ulong)pc->readmem);

	fprintf(fp, "         writemem: ");
	if ((p1 = writemem_function_name()))
		fprintf(fp, "%s()\n", p1);
	else
		fprintf(fp, "%lx\n", (ulong)pc->writemem);

	fprintf(fp, "  dumpfile memory: %d\n", 
		dumpfile_memory(DUMPFILE_MEM_USED)); 
	fprintf(fp, "           curext: %lx\n", (ulong)pc->curext); 
	fprintf(fp, "             sbrk: %lx\n", (ulong)pc->sbrk); 
	fprintf(fp, "          cleanup: %s\n", pc->cleanup);
	fprintf(fp, "            scope: %lx %s\n", pc->scope,
		pc->scope ? "" : "(not set)");
	fprintf(fp, "   nr_hash_queues: %ld\n", pc->nr_hash_queues);
	fprintf(fp, "  read_vmcoreinfo: %lx\n", (ulong)pc->read_vmcoreinfo);
}

char *
readmem_function_name(void)
{
	if (pc->readmem == read_dev_mem)
		return("read_dev_mem");
	else if (pc->readmem == read_mclx_dumpfile)
		return("read_mclx_dumpfile");
	else if (pc->readmem == read_lkcd_dumpfile)
		return("read_lkcd_dumpfile");
	else if (pc->readmem == read_daemon)
		return("read_daemon");
	else if (pc->readmem == read_netdump)
		return("read_netdump");
	else if (pc->readmem == read_xendump)
		return("read_xendump");
	else if (pc->readmem == read_kdump)
		return("read_kdump");
	else if (pc->readmem == read_memory_device)
		return("read_memory_device");
	else if (pc->readmem == read_xendump_hyper)
		return("read_xendump_hyper");
	else if (pc->readmem == read_diskdump)
		return("read_diskdump");
	else if (pc->readmem == read_proc_kcore)
		return("read_proc_kcore");
	else if (pc->readmem == read_sadump)
		return("read_sadump");
	else if (pc->readmem == read_s390_dumpfile)
		return("read_s390_dumpfile");
	else if (pc->readmem == read_ramdump)
		return("read_ramdump");
	else if (pc->readmem == read_vmware_vmss)
		return("read_vmware_vmss");
	else
		return NULL;
}

char *
writemem_function_name(void)
{
	if (pc->writemem == write_dev_mem)
		return("write_dev_mem");
	else if (pc->writemem == write_mclx_dumpfile)
		return("write_mclx_dumpfile");
	else if (pc->writemem == write_lkcd_dumpfile)
		return("write_lkcd_dumpfile");
	else if (pc->writemem == write_daemon)
		return("write_daemon");
	else if (pc->writemem == write_netdump)
		return("write_netdump");
	else if (pc->writemem == write_xendump)
		return("write_xendump");
	else if (pc->writemem == write_kdump)
		return("write_kdump");
	else if (pc->writemem == write_memory_device)
		return("write_memory_device");
//	else if (pc->writemem == write_xendump_hyper)
//		return("write_xendump_hyper");
	else if (pc->writemem == write_diskdump)
		return("write_diskdump");
	else if (pc->writemem == write_proc_kcore)
		return("write_proc_kcore");
	else if (pc->writemem == write_sadump)
		return("write_sadump");
	else if (pc->writemem == write_s390_dumpfile)
		return("write_s390_dumpfile");
	else if (pc->writemem == write_vmware_vmss)
		return("write_vmware_vmss");
	else
		return NULL;
}

/*
 *  "help -B" output
 */
void
dump_build_data(void)
{
        fprintf(fp, "   build_command: %s\n", build_command);
        fprintf(fp, "      build_data: %s\n", build_data);
        fprintf(fp, "    build_target: %s\n", build_target);
        fprintf(fp, "   build_version: %s\n", build_version);
        fprintf(fp, "compiler version: %s\n", compiler_version);
}

/*
 *  Perform any cleanup activity here.
 */
int 
clean_exit(int status)
{
	if (pc->flags & MEMMOD)
		cleanup_memory_driver();

	if ((pc->namelist_orig) && file_exists(pc->namelist, NULL))
		unlink(pc->namelist);
	if ((pc->namelist_debug_orig) && file_exists(pc->namelist_debug, NULL))
		unlink(pc->namelist_debug);
	if (pc->cleanup && file_exists(pc->cleanup, NULL))
		unlink(pc->cleanup);

	ramdump_cleanup();
	exit(status);
}

/*
 *  Check whether this session is for xen hypervisor analysis.
 */
static void
check_xen_hyper(void)
{
	if (!pc->namelist)
		return;

	if (!XEN_HYPER_MODE()) {
		if (STRNEQ(basename(pc->namelist), "xen-syms"))
			pc->flags |= XEN_HYPER;
		else
			return;
	}

#ifdef XEN_HYPERVISOR_ARCH
	pc->cmd_table = xen_hyper_command_table;
	if (pc->flags & XENDUMP)
		pc->readmem = read_xendump_hyper;
#else
	error(FATAL, XEN_HYPERVISOR_NOT_SUPPORTED);
#endif
}

/*
 *  Reject untrusted .crashrc, $HOME/.crashrc, 
 *  .gdbinit, and $HOME/.gdbinit files.
 */
static char *untrusted_file_list[4] = { 0 };

int
untrusted_file(FILE *filep, char *filename)
{
	struct stat sbuf;
	int i;

	if (filep && (fstat(fileno(filep), &sbuf) == 0) &&
	    (sbuf.st_uid == getuid()) && !(sbuf.st_mode & S_IWOTH))
		return FALSE;
	
	for (i = 0; i < 4; i++) {
		if (!untrusted_file_list[i]) {
			untrusted_file_list[i] = strdup(filename);
			break;
		}
	}

	return TRUE;
}

static void
show_untrusted_files(void)
{
	int i, cnt;

	for (i = cnt = 0; i < 4; i++) {
		if (untrusted_file_list[i]) {
			error(WARNING, "not using untrusted file: \"%s\"\n", 
				untrusted_file_list[i]);
			free(untrusted_file_list[i]);
			cnt++;
		}
	}
	if (cnt)
		fprintf(fp, "\n");
}

/*
 *  If GET_OSRELEASE is still set, the OS release has been
 *  found and displayed.
 */
static void
get_osrelease(char *dumpfile)
{
	int retval = 1;

	if (is_flattened_format(dumpfile)) {
		if (pc->flags2 & GET_OSRELEASE)
			retval = 0;
	} else if (is_diskdump(dumpfile)) {
		if (pc->flags2 & GET_OSRELEASE)
			retval = 0;
	} else if (is_kdump(dumpfile, KDUMP_LOCAL)) {
		if (pc->flags2 & GET_OSRELEASE)
			retval = 0;
	}
	
	if (retval)
		fprintf(fp, "unknown\n");

	clean_exit(retval);
}

static void
get_log(char *dumpfile)
{

	int retval = 1;

	if (is_flattened_format(dumpfile))
		pc->flags2 |= FLAT;
	
	if (is_diskdump(dumpfile)) {
		if (pc->flags2 & GET_LOG)
			retval = 0;
	} else if (is_kdump(dumpfile, KDUMP_LOCAL)) {
		if (pc->flags2 & GET_LOG)
			retval = 0;
	}

	if (retval)
		fprintf(fp, "%s: no VMCOREINFO data\n", dumpfile);

	clean_exit(retval);
}


static char *
no_vmcoreinfo(const char *unused)
{
	return NULL;
}
