Beispiel #1
0
/*
 * getoffsets - read the magic offsets from the specified file
 */
static void
getoffsets(
	off_t *tick_off,
	off_t *tickadj_off,
	off_t *dosync_off,
	off_t *noprintf_off
	)
{

#ifndef NOKMEM
# ifndef HAVE_KVM_OPEN
	const char **kname;
# endif
#endif

#ifndef NOKMEM
# ifdef NLIST_NAME_UNION
#  define NL_B {{
#  define NL_E }}
# else
#  define NL_B {
#  define NL_E }
# endif
#endif

#define K_FILLER_NAME "DavidLetterman"

#ifdef NLIST_EXTRA_INDIRECTION
	int i;
#endif

#ifndef NOKMEM
	static struct nlist nl[] =
	{
		NL_B
#ifdef K_TICKADJ_NAME
#define N_TICKADJ	0
		K_TICKADJ_NAME
#else
		K_FILLER_NAME
#endif
		NL_E,
		NL_B
#ifdef K_TICK_NAME
#define N_TICK		1
		K_TICK_NAME
#else
		K_FILLER_NAME
#endif
		NL_E,
		NL_B
#ifdef K_DOSYNCTODR_NAME
#define N_DOSYNC	2
		K_DOSYNCTODR_NAME
#else
		K_FILLER_NAME
#endif
		NL_E,
		NL_B
#ifdef K_NOPRINTF_NAME
#define N_NOPRINTF	3
		K_NOPRINTF_NAME
#else
		K_FILLER_NAME
#endif
		NL_E,
		NL_B "" NL_E,
	};

#ifndef HAVE_KVM_OPEN
	static const char *kernels[] =
	{
#ifdef HAVE_GETBOOTFILE
		NULL,			/* *** SEE BELOW! *** */
#endif
		"/kernel/unix",
		"/kernel",
		"/vmunix",
		"/unix",
		"/mach",
		"/hp-ux",
		"/386bsd",
		"/netbsd",
		"/stand/vmunix",
		"/bsd",
		NULL
	};
#endif /* not HAVE_KVM_OPEN */

#ifdef HAVE_KVM_OPEN
	/*
	 * Solaris > 2.5 doesn't have a kernel file.  Use the kvm_* interface
	 * to read the kernel name list. -- stolcke 3/4/96
	 */
	kvm_t *kvm_handle = kvm_open(NULL, NULL, NULL, O_RDONLY, progname);

	if (kvm_handle == NULL)
	{
		(void) fprintf(stderr,
			       "%s: kvm_open failed\n",
			       progname);
		exit(1);
	}
	if (kvm_nlist(kvm_handle, nl) == -1)
	{
		(void) fprintf(stderr,
			       "%s: kvm_nlist failed\n",
			       progname);
		exit(1);
	}
	kvm_close(kvm_handle);
#else /* not HAVE_KVM_OPEN */
#ifdef HAVE_GETBOOTFILE		/* *** SEE HERE! *** */
	if (kernels[0] == NULL)
	{
		char * cp = (char *)getbootfile();

		if (cp)
		{
			kernels[0] = cp;
		}
		else
		{
			kernels[0] = "/Placeholder";
		}
	}
#endif /* HAVE_GETBOOTFILE */
	for (kname = kernels; *kname != NULL; kname++)
	{
		struct stat stbuf;

		if (stat(*kname, &stbuf) == -1)
		{
			continue;
		}
		if (nlist(*kname, nl) >= 0)
		{
			break;
		}
		else
		{
			(void) fprintf(stderr,
				       "%s: nlist didn't find needed symbols from <%s>: %s\n",
				       progname, *kname, strerror(errno));
		}
	}
	if (*kname == NULL)
	{
		(void) fprintf(stderr,
			       "%s: Couldn't find the kernel\n",
			       progname);
		exit(1);
	}
#endif /* HAVE_KVM_OPEN */

	if (dokmem)
	{
		file = kmem;

		fd = openfile(file, O_RDONLY);
#ifdef NLIST_EXTRA_INDIRECTION
		/*
		 * Go one more round of indirection.
		 */
		for (i = 0; i < (sizeof(nl) / sizeof(struct nlist)); i++)
		{
			if ((nl[i].n_value) && (nl[i].n_sclass == 0x6b))
			{
				readvar(fd, nl[i].n_value, &nl[i].n_value);
			}
		}
#endif /* NLIST_EXTRA_INDIRECTION */
	}
#endif /* not NOKMEM */

	*tickadj_off  = 0;
	*tick_off     = 0;
	*dosync_off   = 0;
	*noprintf_off = 0;

#if defined(N_TICKADJ)
	*tickadj_off = nl[N_TICKADJ].n_value;
#endif

#if defined(N_TICK)
	*tick_off = nl[N_TICK].n_value;
#endif

#if defined(N_DOSYNC)
	*dosync_off = nl[N_DOSYNC].n_value;
#endif

#if defined(N_NOPRINTF)
	*noprintf_off = nl[N_NOPRINTF].n_value;
#endif
	return;
}
Beispiel #2
0
static kvm_t *
_kvm_open(kvm_t *kd, const char *uf, const char *mf, int flag, char *errout)
{
	struct stat st;

	kd->vmfd = -1;
	kd->pmfd = -1;
	kd->nlfd = -1;
	kd->vmst = 0;
	kd->procbase = 0;
	kd->argspc = 0;
	kd->argv = 0;

	if (uf == 0)
		uf = getbootfile();
	else if (strlen(uf) >= MAXPATHLEN) {
		_kvm_err(kd, kd->program, "exec file name too long");
		goto failed;
	}
	if (flag & ~O_RDWR) {
		_kvm_err(kd, kd->program, "bad flags arg");
		goto failed;
	}
	if (mf == 0)
		mf = _PATH_MEM;

	if ((kd->pmfd = open(mf, flag | O_CLOEXEC, 0)) < 0) {
		_kvm_syserr(kd, kd->program, "%s", mf);
		goto failed;
	}
	if (fstat(kd->pmfd, &st) < 0) {
		_kvm_syserr(kd, kd->program, "%s", mf);
		goto failed;
	}
	if (S_ISREG(st.st_mode) && st.st_size <= 0) {
		errno = EINVAL;
		_kvm_syserr(kd, kd->program, "empty file");
		goto failed;
	}
	if (S_ISCHR(st.st_mode)) {
		/*
		 * If this is a character special device, then check that
		 * it's /dev/mem.  If so, open kmem too.  (Maybe we should
		 * make it work for either /dev/mem or /dev/kmem -- in either
		 * case you're working with a live kernel.)
		 */
		if (strcmp(mf, _PATH_DEVNULL) == 0) {
			kd->vmfd = open(_PATH_DEVNULL, O_RDONLY | O_CLOEXEC);
			return (kd);
		} else if (strcmp(mf, _PATH_MEM) == 0) {
			if ((kd->vmfd = open(_PATH_KMEM, flag | O_CLOEXEC)) <
			    0) {
				_kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
				goto failed;
			}
			return (kd);
		}
	}
	/*
	 * This is a crash dump.
	 * Initialize the virtual address translation machinery,
	 * but first setup the namelist fd.
	 */
	if ((kd->nlfd = open(uf, O_RDONLY | O_CLOEXEC, 0)) < 0) {
		_kvm_syserr(kd, kd->program, "%s", uf);
		goto failed;
	}
	if (strncmp(mf, _PATH_FWMEM, strlen(_PATH_FWMEM)) == 0)
		kd->rawdump = 1;
	if (_kvm_initvtop(kd) < 0)
		goto failed;
	return (kd);
failed:
	/*
	 * Copy out the error if doing sane error semantics.
	 */
	if (errout != 0)
		strlcpy(errout, kd->errbuf, _POSIX2_LINE_MAX);
	(void)kvm_close(kd);
	return (0);
}
Beispiel #3
0
static void
DoFile(const char *savedir, const char *device)
{
	static char *buf = NULL;
	struct partinfo	dpart;
	struct kerneldumpheader kdhf, kdhl;
	off_t mediasize, dumpsize, firsthd, lasthd, dmpcnt;
	FILE *info, *fp, *fpkern;
	mode_t oumask;
	int fd, fdinfo, fdkernin, error, wl;
	int nr, nw, hs, he = 0;
	int bounds, status;
	u_int sectorsize;

	bounds = getbounds();
	dmpcnt = 0;
	mediasize = 0;
	status = STATUS_UNKNOWN;

	if (buf == NULL) {
		buf = malloc(BUFFERSIZE);
		if (buf == NULL) {
			syslog(LOG_ERR, "%m");
			return;
		}
	}

	if (verbose)
		printf("checking for kernel dump on device %s\n", device);

	fd = open(device, O_RDWR);
	if (fd < 0) {
		syslog(LOG_ERR, "%s: %m", device);
		return;
	}

	bzero(&dpart, sizeof(dpart));
	error = ioctl(fd, DIOCGPART, &dpart);
	if (error) {
		syslog(LOG_ERR,
		    "couldn't find media and/or sector size of %s: %m", device);
		goto closefd;
	}
	mediasize = dpart.media_size;
	sectorsize = dpart.media_blksize;

	if (verbose) {
		printf("mediasize = %lld\n", (long long)mediasize);
		printf("sectorsize = %u\n", sectorsize);
	}

	lasthd = mediasize - sectorsize;
	lseek(fd, lasthd, SEEK_SET);
	error = read(fd, &kdhl, sizeof kdhl);
	if (error != sizeof kdhl) {
		syslog(LOG_ERR,
		    "error reading last dump header at offset %lld in %s: %m",
		    (long long)lasthd, device);
		goto closefd;
	}
	if (memcmp(kdhl.magic, KERNELDUMPMAGIC, sizeof kdhl.magic)) {
		if (verbose)
			printf("magic mismatch on last dump header on %s\n",
			    device);

		status = STATUS_BAD;
		if (force == 0)
			goto closefd;

		if (memcmp(kdhl.magic, KERNELDUMPMAGIC_CLEARED,
			    sizeof kdhl.magic) == 0) {
			if (verbose)
				printf("forcing magic on %s\n", device);
			memcpy(kdhl.magic, KERNELDUMPMAGIC,
			    sizeof kdhl.magic);
		} else {
			syslog(LOG_ERR, "unable to force dump - bad magic");
			goto closefd;
		}
	}
	if (dtoh32(kdhl.version) != KERNELDUMPVERSION) {
		syslog(LOG_ERR,
		    "unknown version (%d) in last dump header on %s",
		    dtoh32(kdhl.version), device);

		status = STATUS_BAD;
		if (force == 0)
			goto closefd;
	}

	nfound++;
	if (clear)
		goto nuke;

	if (kerneldump_parity(&kdhl)) {
		syslog(LOG_ERR,
		    "parity error on last dump header on %s", device);
		nerr++;
		status = STATUS_BAD;
		if (force == 0)
			goto closefd;
	}
	dumpsize = dtoh64(kdhl.dumplength);
	firsthd = lasthd - dumpsize - sizeof kdhf;
	lseek(fd, firsthd, SEEK_SET);
	error = read(fd, &kdhf, sizeof kdhf);
	if (error != sizeof kdhf) {
		syslog(LOG_ERR,
		    "error reading first dump header at offset %lld in %s: %m",
		    (long long)firsthd, device);
		nerr++;
		goto closefd;
	}

	if (verbose >= 2) {
		printf("First dump headers:\n");
		printheader(stdout, &kdhf, device, bounds, -1);

		printf("\nLast dump headers:\n");
		printheader(stdout, &kdhl, device, bounds, -1);
		printf("\n");
	}

	if (memcmp(&kdhl, &kdhf, sizeof kdhl)) {
		syslog(LOG_ERR,
		    "first and last dump headers disagree on %s", device);
		nerr++;
		status = STATUS_BAD;
		if (force == 0)
			goto closefd;
	} else {
		status = STATUS_GOOD;
	}

	if (checkfor) {
		printf("A dump exists on %s\n", device);
		close(fd);
		exit(0);
	}

	if (kdhl.panicstring[0])
		syslog(LOG_ALERT, "reboot after panic: %s", kdhl.panicstring);
	else
		syslog(LOG_ALERT, "reboot");

	if (verbose)
		printf("Checking for available free space\n");
	if (!check_space(savedir, dumpsize)) {
		nerr++;
		goto closefd;
	}

	writebounds(bounds + 1);

	/*
	 * Write kernel file.
	 */
	fdkernin = open(getbootfile(), O_RDONLY, 0);
	if (fdkernin < 0) {
		syslog(LOG_ERR, "%s: %m", getbootfile());
	}

	if (compress) {
		sprintf(buf, "kern.%d.gz", bounds);
		fpkern = zopen(buf, "w");
	} else {
		sprintf(buf, "kern.%d", bounds);
		fpkern = fopen(buf, "w");
	}
	if (fpkern == NULL) {
		syslog(LOG_ERR, "%s: %m", buf);
		close(fdkernin);
	}

	syslog(LOG_NOTICE, "writing %skernel to %s",
	    compress ? "compressed " : "", buf);

	while ((nr = read(fdkernin, buf, sizeof(buf))) > 0) {
		nw = fwrite(buf, 1, nr, fpkern);
		if (nw != nr) {
			syslog(LOG_ERR, "kern.%d: %m", bounds);
			syslog(LOG_WARNING,
			    "WARNING: kernel may be incomplete");
			exit(1);
		}
	}
	if (nr < 0) {
		syslog(LOG_ERR, "%s: %m", getbootfile());
		syslog(LOG_WARNING,
		    "WARNING: kernel may be incomplete");
		exit(1);
	}
	fclose(fpkern);
	close(fdkernin);


	sprintf(buf, "info.%d", bounds);

	/*
	 * Create or overwrite any existing dump header files.
	 */
	fdinfo = open(buf, O_WRONLY | O_CREAT | O_TRUNC, 0600);
	if (fdinfo < 0) {
		syslog(LOG_ERR, "%s: %m", buf);
		nerr++;
		goto closefd;
	}
	oumask = umask(S_IRWXG|S_IRWXO); /* Restrict access to the core file.*/
	if (compress) {
		sprintf(buf, "vmcore.%d.gz", bounds);
		fp = zopen(buf, "w");
	} else {
		sprintf(buf, "vmcore.%d", bounds);
		fp = fopen(buf, "w");
	}
	if (fp == NULL) {
		syslog(LOG_ERR, "%s: %m", buf);
		close(fdinfo);
		nerr++;
		goto closefd;
	}
	(void)umask(oumask);

	info = fdopen(fdinfo, "w");

	if (info == NULL) {
		syslog(LOG_ERR, "fdopen failed: %m");
		nerr++;
		goto closefd;
	}

	if (verbose)
		printheader(stdout, &kdhl, device, bounds, status);

	printheader(info, &kdhl, device, bounds, status);
	fclose(info);

	syslog(LOG_NOTICE, "writing %score to %s",
	    compress ? "compressed " : "", buf);

	while (dumpsize > 0) {
		wl = BUFFERSIZE;
		if (wl > dumpsize)
			wl = dumpsize;
		nr = read(fd, buf, wl);
		if (nr != wl) {
			if (nr == 0)
				syslog(LOG_WARNING,
				    "WARNING: EOF on dump device");
			else
				syslog(LOG_ERR, "read error on %s: %m", device);
			nerr++;
			goto closeall;
		}
		if (compress) {
			nw = fwrite(buf, 1, wl, fp);
		} else {
			for (nw = 0; nw < nr; nw = he) {
				/* find a contiguous block of zeroes */
				for (hs = nw; hs < nr; hs += BLOCKSIZE) {
					for (he = hs; he < nr && buf[he] == 0;
					    ++he)
						/* nothing */ ;
					/* is the hole long enough to matter? */
					if (he >= hs + BLOCKSIZE)
						break;
				}

				/* back down to a block boundary */
				he &= BLOCKMASK;

				/*
				 * 1) Don't go beyond the end of the buffer.
				 * 2) If the end of the buffer is less than
				 *    BLOCKSIZE bytes away, we're at the end
				 *    of the file, so just grab what's left.
				 */
				if (hs + BLOCKSIZE > nr)
					hs = he = nr;

				/*
				 * At this point, we have a partial ordering:
				 *     nw <= hs <= he <= nr
				 * If hs > nw, buf[nw..hs] contains non-zero data.
				 * If he > hs, buf[hs..he] is all zeroes.
				 */
				if (hs > nw)
					if (fwrite(buf + nw, hs - nw, 1, fp)
					    != 1)
					break;
				if (he > hs)
					if (fseeko(fp, he - hs, SEEK_CUR) == -1)
						break;
			}
		}
		if (nw != wl) {
			syslog(LOG_ERR,
			    "write error on vmcore.%d file: %m", bounds);
			syslog(LOG_WARNING,
			    "WARNING: vmcore may be incomplete");
			nerr++;
			goto closeall;
		}
		if (verbose) {
			dmpcnt += wl;
			printf("%llu\r", (unsigned long long)dmpcnt);
			fflush(stdout);
		}
		dumpsize -= wl;
	}
	if (verbose)
		printf("\n");

	if (fclose(fp) < 0) {
		syslog(LOG_ERR, "error on vmcore.%d: %m", bounds);
		nerr++;
		goto closeall;
	}
	nsaved++;

	if (verbose)
		printf("dump saved\n");

nuke:
	if (clear || !keep) {
		if (verbose)
			printf("clearing dump header\n");
		memcpy(kdhl.magic, KERNELDUMPMAGIC_CLEARED, sizeof kdhl.magic);
		lseek(fd, lasthd, SEEK_SET);
		error = write(fd, &kdhl, sizeof kdhl);
		if (error != sizeof kdhl)
			syslog(LOG_ERR,
			    "error while clearing the dump header: %m");
	}
	close(fd);
	return;

closeall:
	fclose(fp);

closefd:
	close(fd);
}
Beispiel #4
0
int
main(int argc, char **argv)
{
	int ch, mode, disp, accessmode;
	struct kvmvars kvmvars;
	const char *systemname;
	char *kmemf;

	if (seteuid(getuid()) != 0) {
		err(1, "seteuid failed\n");
	}
	kmemf = NULL;
	systemname = NULL;
	while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) {
		switch((char)ch) {

		case 'M':
			kmemf = optarg;
			kflag = 1;
			break;

		case 'N':
			systemname = optarg;
			break;

		case 'B':
			Bflag = 1;
			break;

		case 'b':
			bflag = 1;
			break;

		case 'h':
			hflag = 1;
			break;

		case 'p':
			pflag = 1;
			break;

		case 'r':
			rflag = 1;
			break;

		default:
			usage();
		}
	}
	argc -= optind;
	argv += optind;

#define BACKWARD_COMPATIBILITY
#ifdef	BACKWARD_COMPATIBILITY
	if (*argv) {
		systemname = *argv;
		if (*++argv) {
			kmemf = *argv;
			++kflag;
		}
	}
#endif
	if (systemname == NULL)
		systemname = getbootfile();
	accessmode = openfiles(systemname, kmemf, &kvmvars);
	mode = getprof(&kvmvars);
	if (hflag)
		disp = GMON_PROF_OFF;
	else if (Bflag)
		disp = GMON_PROF_HIRES;
	else if (bflag)
		disp = GMON_PROF_ON;
	else
		disp = mode;
	if (pflag)
		dumpstate(&kvmvars);
	if (rflag)
		reset(&kvmvars);
	if (accessmode == O_RDWR)
		setprof(&kvmvars, disp);
	(void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
		      disp == GMON_PROF_OFF ? "off" :
		      disp == GMON_PROF_HIRES ? "running (high resolution)" :
		      disp == GMON_PROF_ON ? "running" :
		      disp == GMON_PROF_BUSY ? "busy" :
		      disp == GMON_PROF_ERROR ? "off (error)" :
		      "in an unknown state");
	return (0);
}
Beispiel #5
0
int
main(int argc, char **argv)
{
	int ch, i, jflag, npcbs;
	const char *core, *syst;

	nl[0].n_name = strdup("_tcp_debug");
	nl[1].n_name = strdup("_tcp_debx");

	jflag = npcbs = 0;
	while ((ch = getopt(argc, argv, "afjp:st")) != -1)
		switch (ch) {
		case 'a':
			++aflag;
			break;
		case 'f':
			++follow;
			setlinebuf(stdout);
			break;
		case 'j':
			++jflag;
			break;
		case 'p':
			if (npcbs >= TCP_NDEBUG)
				errx(1, "too many pcb's specified");
			(void)sscanf(optarg, "%x", (int *)&tcp_pcbs[npcbs++]);
			break;
		case 's':
			++sflag;
			break;
		case 't':
			++tflag;
			break;
		case '?':
		default:
			usage();
		}
	argc -= optind;
	argv += optind;

	core = _PATH_KMEM;
	if (argc > 0) {
		syst = *argv;
		argc--, argv++;
		if (argc > 0) {
			core = *argv;
			argc--, argv++;
			++kflag;
		}
		/*
		 * Discard setgid privileges if not the running kernel so that
		 * bad guys can't print interesting stuff from kernel memory.
		 */
		if (setgid(getgid()) != 0)
			err(1, "setgid");
	}
	else
		syst = getbootfile();

	if (nlist(syst, nl) < 0 || !nl[0].n_value)
		errx(1, "%s: no namelist", syst);
	if ((memf = open(core, O_RDONLY)) < 0)
		err(2, "%s", core);
	if (setgid(getgid()) != 0)
		err(1, "setgid");
	if (kflag)
		errx(1, "can't do core files yet");
	(void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
	if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
	    sizeof(tcp_debx))
		err(3, "tcp_debx");
	(void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
	if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
	    sizeof(tcp_debug))
		err(3, "tcp_debug");
	/*
	 * If no control blocks have been specified, figure
	 * out how many distinct one we have and summarize
	 * them in tcp_pcbs for sorting the trace records
	 * below.
	 */
	if (!npcbs) {
		for (i = 0; i < TCP_NDEBUG; i++) {
			register struct tcp_debug *td = &tcp_debug[i];
			register int j;

			if (td->td_tcb == 0)
				continue;
			for (j = 0; j < npcbs; j++)
				if (tcp_pcbs[j] == td->td_tcb)
					break;
			if (j >= npcbs)
				tcp_pcbs[npcbs++] = td->td_tcb;
		}
		if (!npcbs)
			exit(0);
	}
	qsort(tcp_pcbs, npcbs, sizeof(caddr_t), numeric);
	if (jflag) {
		for (i = 0;;) {
			printf("%p", (void *)tcp_pcbs[i]);
			if (++i == npcbs)
				break;
			fputs(", ", stdout);
		}
		putchar('\n');
	}
	else for (i = 0; i < npcbs; i++) {
		printf("\n%p:\n", tcp_pcbs[i]);
		dotrace(tcp_pcbs[i]);
	}
	exit(0);
}
Beispiel #6
0
int
main(int argc, char *argv[])
{
	char path[PATH_MAX];
	struct stat st;
	struct captured_main_args args;
	char *s;
	int a, ch;

	dumpnr = NULL;

	strlcpy(crashdir, "/var/crash", sizeof(crashdir));
	s = getenv("KGDB_CRASH_DIR");
	if (s != NULL)
		strlcpy(crashdir, s, sizeof(crashdir));

	/* Convert long options into short options. */
	for (a = 1; a < argc; a++) {
		s = argv[a];
		if (s[0] == '-') {
			s++;
			/* Long options take either 1 or 2 dashes. */
			if (s[0] == '-')
				s++;
			if (strcmp(s, "quiet") == 0)
				argv[a] = "-q";
			else if (strcmp(s, "fullname") == 0)
				argv[a] = "-f";
		}
	}

	kgdb_quiet = 0;
	memset (&args, 0, sizeof args);
	args.interpreter_p = INTERP_CONSOLE;
	args.argv = malloc(sizeof(char *));
	args.argv[0] = argv[0];

	while ((ch = getopt(argc, argv, "ab:c:d:fn:qr:vw")) != -1) {
		switch (ch) {
		case 'a':
			annotation_level++;
			break;
		case 'b': {
			int i;
			char *p;

			i = strtol(optarg, &p, 0);
			if (*p != '\0' || p == optarg)
				warnx("warning: could not set baud rate to `%s'.\n",
				    optarg);
			else
				baud_rate = i;
			break;
		}
		case 'c':	/* use given core file. */
			if (vmcore != NULL) {
				warnx("option %c: can only be specified once",
				    optopt);
				usage();
				/* NOTREACHED */
			}
			vmcore = strdup(optarg);
			break;
		case 'd':	/* lookup dumps in given directory. */
			strlcpy(crashdir, optarg, sizeof(crashdir));
			break;
		case 'f':
			annotation_level = 1;
			break;
		case 'n':	/* use dump with given number. */
			dumpnr = optarg;
			break;
		case 'q':
			kgdb_quiet = 1;
			add_arg(&args, "-q");
			break;
		case 'r':	/* use given device for remote session. */
			if (remote != NULL) {
				warnx("option %c: can only be specified once",
				    optopt);
				usage();
				/* NOTREACHED */
			}
			remote = strdup(optarg);
			break;
		case 'v':	/* increase verbosity. */
			verbose++;
			break;
		case 'w':	/* core file is writeable. */
			add_arg(&args, "--write");
			break;
		case '?':
		default:
			usage();
		}
	}

	if (((vmcore != NULL) ? 1 : 0) + ((dumpnr != NULL) ? 1 : 0) +
	    ((remote != NULL) ? 1 : 0) > 1) {
		warnx("options -c, -n and -r are mutually exclusive");
		usage();
		/* NOTREACHED */
	}

	if (verbose > 1)
		warnx("using %s as the crash directory", crashdir);

	if (argc > optind)
		kernel = strdup(argv[optind++]);

	if (argc > optind && (dumpnr != NULL || remote != NULL)) {
		warnx("options -n and -r do not take a core file. Ignored");
		optind = argc;
	}

	if (dumpnr != NULL) {
		snprintf(path, sizeof(path), "%s/vmcore.%s", crashdir, dumpnr);
		if (stat(path, &st) == -1)
			err(1, "%s", path);
		if (!S_ISREG(st.st_mode))
			errx(1, "%s: not a regular file", path);
		vmcore = strdup(path);
	} else if (remote != NULL) {
		verify_remote();
	} else if (argc > optind) {
		if (vmcore == NULL)
			vmcore = strdup(argv[optind++]);
		if (argc > optind)
			warnx("multiple core files specified. Ignored");
	} else if (vmcore == NULL && kernel == NULL) {
		vmcore = strdup(_PATH_MEM);
		kernel = strdup(getbootfile());
	}

	if (verbose) {
		if (vmcore != NULL)
			warnx("core file: %s", vmcore);
		if (remote != NULL)
			warnx("device file: %s", remote);
		if (kernel != NULL)
			warnx("kernel image: %s", kernel);
	}

	/* A remote target requires an explicit kernel argument. */
	if (remote != NULL && kernel == NULL) {
		warnx("remote debugging requires a kernel");
		usage();
		/* NOTREACHED */
	}

	/* If we don't have a kernel image yet, try to find one. */
	if (kernel == NULL) {
		if (dumpnr != NULL)
			kernel_from_dumpnr(dumpnr);

		if (kernel == NULL)
			errx(1, "couldn't find a suitable kernel image");
		if (verbose)
			warnx("kernel image: %s", kernel);
	}

	/* Set an alternate prompt. */
	add_arg(&args, "-iex");
	add_arg(&args, "set prompt (kgdb) ");

	/* Open the vmcore if requested. */
	if (vmcore != NULL) {
		add_arg(&args, "-ex");
		if (asprintf(&s, "target vmcore %s", vmcore) < 0)
			err(1, "couldn't build command line");
		add_arg(&args, s);
	}

	/* Open the remote target if requested. */
	if (remote != NULL) {
		add_arg(&args, "-ex");
		if (asprintf(&s, "target remote %s", remote) < 0)
			err(1, "couldn't build command line");
		add_arg(&args, s);
	}

	add_arg(&args, kernel);

	/* The libgdb code uses optind too. Reset it... */
	optind = 0;

	/* Terminate argv list. */
	add_arg(&args, NULL);

	return (gdb_main(&args));
}