diff --git a/lib/libkvm/Makefile b/lib/libkvm/Makefile index 5aa5171bf653..db3621d66ef1 100644 --- a/lib/libkvm/Makefile +++ b/lib/libkvm/Makefile @@ -1,10 +1,14 @@ -# $Id: Makefile,v 1.3 1994/04/23 02:41:59 briggs Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 LIB= kvm -CFLAGS+=-DLIBC_SCCS -SRCS= kvm.c -.if (${MACHINE} == "amiga") || (${MACHINE} == "mac68k") -CFLAGS+=-D${MACHINE} -.endif +CFLAGS+=-DLIBC_SCCS -I/sys +SRCS= kvm.c kvm_${MACHINE}.c kvm_file.c kvm_getloadavg.c kvm_proc.c + +MAN3= kvm.0 kvm_geterr.0 kvm_getfiles.0 kvm_getloadavg.0 kvm_getprocs.0 \ + kvm_nlist.0 kvm_open.0 kvm_read.0 + +MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.3 +MLINKS+=kvm_open.3 kvm_openfiles.3 kvm_open.3 kvm_close.3 +MLINKS+=kvm_read.3 kvm_write.3 .include diff --git a/lib/libkvm/kvm.c b/lib/libkvm/kvm.c index 16192998aa64..a76f61e8d52c 100644 --- a/lib/libkvm/kvm.c +++ b/lib/libkvm/kvm.c @@ -1,8 +1,10 @@ /*- - * Copyright (c) 1994 Charles Hannum. - * Copyright (c) 1993 Christopher G. Demetriou - * Copyright (c) 1989 The Regents of the University of California. - * All rights reserved. + * Copyright (c) 1989, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software developed by the Computer Systems + * Engineering group at Lawrence Berkeley Laboratory under DARPA contract + * BG 91-66 and contributed to Berkeley. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -34,1306 +36,505 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -/*static char sccsid[] = "from: @(#)kvm.c 5.18 (Berkeley) 5/7/91";*/ -static char rcsid[] = "$Id: kvm.c,v 1.34 1994/05/05 06:03:26 cgd Exp $"; +static char sccsid[] = "@(#)kvm.c 8.2 (Berkeley) 2/13/94"; #endif /* LIBC_SCCS and not lint */ #include #include #include #include -#include -#include -#include +#include +#include + +#include +#include +#include + #include + +#include +#include #include -#include #include -#include #include +#include #include #include +#include #include +#include -#define btop(x) (((unsigned)(x)) >> PGSHIFT) /* XXX */ -#define ptob(x) ((caddr_t)((x) << PGSHIFT)) /* XXX */ -#include /* ??? kinfo_proc currently includes this*/ -#include -#include -#include -#if defined(m68k) -#include -#define btos(x) (((unsigned)(x)) >> SEGSHIFT) /* XXX */ -#endif +#include "kvm_private.h" -/* - * files - */ -static const char *unixf, *memf, *kmemf, *swapf; -static int unixx, mem, kmem, swap; -static DBM *db; -/* - * flags - */ -static int deadkernel; -static int kvminit = 0; -static int kvmfilesopen = 0; -/* - * state - */ -static struct kinfo_proc *kvmprocbase, *kvmprocptr; -static int kvmnprocs; -/* - * u. buffer - */ -static union { - struct user user; - char upages[UPAGES][NBPG]; -} user; +static int kvm_dbopen __P((kvm_t *, const char *)); -struct swapblk { - long offset; /* offset in swap device */ - long size; /* remaining size of block in swap device */ -}; - -/* - * random other stuff - */ -static int dmmin, dmmax; -static int pcbpf; -static int nswap; -static long vm_page_hash_mask; -static long vm_page_buckets; -static long page_shift; -static char *tmp; -#if defined(m68k) -#if defined(amiga) || defined(mac68k) -static int cpu040; -#endif -static int lowram; -static struct ste *Sysseg; -#endif -#if defined(i386) -static struct pde *PTD; -#endif - -#define atop(x) (((unsigned)(x)) >> page_shift) -#define vm_page_hash(object, offset) \ - (((unsigned)object+(unsigned)atop(offset))&vm_page_hash_mask) - -#define basename(cp) ((tmp=rindex((cp), '/')) ? tmp+1 : (cp)) -#define MAXSYMSIZE 256 - -static struct nlist nl[] = { - { "_Usrptmap" }, -#define X_USRPTMAP 0 - { "_usrpt" }, -#define X_USRPT 1 - { "_nswap" }, -#define X_NSWAP 2 - { "_dmmin" }, -#define X_DMMIN 3 - { "_dmmax" }, -#define X_DMMAX 4 - { "_vm_page_buckets" }, -#define X_VM_PAGE_BUCKETS 5 - { "_vm_page_hash_mask" }, -#define X_VM_PAGE_HASH_MASK 6 - { "_page_shift" }, -#define X_PAGE_SHIFT 7 - -#if defined(m68k) -#define X_DEADKERNEL 8 -#endif - -#if defined(i386) -#define X_DEADKERNEL 8 -#endif - -#if defined(ns32k) -#define X_DEADKERNEL 8 -#endif - -#if defined(sparc) - { "_pmap_dtos" }, -#define X_PMAP_DTOS 8 -#define X_DEADKERNEL 9 -#endif - - /* - * everything here and down, only if a dead kernel - */ - { "_Sysmap" }, -#define X_SYSMAP (X_DEADKERNEL + 0) - { "_Syssize" }, -#define X_SYSSIZE (X_DEADKERNEL + 1) - { "_allproc" }, -#define X_ALLPROC (X_DEADKERNEL + 2) - { "_zombproc" }, -#define X_ZOMBPROC (X_DEADKERNEL + 3) - { "_nproc" }, -#define X_NPROC (X_DEADKERNEL + 4) -#define X_LAST (X_DEADKERNEL + 5) - -#if defined(m68k) - { "_Sysseg" }, -#define X_SYSSEG (X_LAST+0) - { "_lowram" }, -#define X_LOWRAM (X_LAST+1) -#if defined(amiga) || defined(mac68k) - { "_cpu040" }, -#define X_CPU040 (X_LAST+2) -#endif -#endif - -#if defined(i386) - { "_IdlePTD" }, -#define X_IdlePTD (X_LAST+0) -#endif - - { "" }, -}; - -static off_t Vtophys(); -static void klseek(), seterr(), setsyserr(), vstodb(); -static int getkvars(), kvm_doprocs(), kvm_init(); -static int vatosw(); -static int pager_get(); -static int findpage(); -#if defined(sparc) -static vm_offset_t phys2realphys(); -#endif - -/* - * returns 0 if files were opened now, - * 1 if files were already opened, - * -1 if files could not be opened. - */ -kvm_openfiles(uf, mf, sf) - const char *uf, *mf, *sf; +char * +kvm_geterr(kd) + kvm_t *kd; { - if (kvmfilesopen) - return (1); - unixx = mem = kmem = swap = -1; - unixf = (uf == NULL) ? _PATH_UNIX : uf; - memf = (mf == NULL) ? _PATH_MEM : mf; + return (kd->errbuf); +} - if ((unixx = open(unixf, O_RDONLY, 0)) == -1) { - setsyserr("can't open %s", unixf); - goto failed; - } - if ((mem = open(memf, O_RDONLY, 0)) == -1) { - setsyserr("can't open %s", memf); - goto failed; - } - if (sf != NULL) - swapf = sf; - if (mf != NULL) { - deadkernel++; - kmemf = mf; - kmem = mem; - swap = -1; +#if __STDC__ +#include +#else +#include +#endif + +/* + * Report an error using printf style arguments. "program" is kd->program + * on hard errors, and 0 on soft errors, so that under sun error emulation, + * only hard errors are printed out (otherwise, programs like gdb will + * generate tons of error messages when trying to access bogus pointers). + */ +void +#if __STDC__ +_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...) +#else +_kvm_err(kd, program, fmt, va_alist) + kvm_t *kd; + char *program, *fmt; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (program != NULL) { + (void)fprintf(stderr, "%s: ", program); + (void)vfprintf(stderr, fmt, ap); + (void)fputc('\n', stderr); + } else + (void)vsnprintf(kd->errbuf, + sizeof(kd->errbuf), (char *)fmt, ap); + + va_end(ap); +} + +void +#if __STDC__ +_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...) +#else +_kvm_syserr(kd, program, fmt, va_alist) + kvm_t *kd; + char *program, *fmt; + va_dcl +#endif +{ + va_list ap; + register int n; + +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + if (program != NULL) { + (void)fprintf(stderr, "%s: ", program); + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": %s\n", strerror(errno)); } else { - kmemf = _PATH_KMEM; - if ((kmem = open(kmemf, O_RDONLY, 0)) == -1) { - setsyserr("can't open %s", kmemf); + register char *cp = kd->errbuf; + + (void)vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap); + n = strlen(cp); + (void)snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s", + strerror(errno)); + } + va_end(ap); +} + +void * +_kvm_malloc(kd, n) + register kvm_t *kd; + register size_t n; +{ + void *p; + + if ((p = malloc(n)) == NULL) + _kvm_err(kd, kd->program, strerror(errno)); + return (p); +} + +static kvm_t * +_kvm_open(kd, uf, mf, sf, flag, errout) + register kvm_t *kd; + const char *uf; + const char *mf; + const char *sf; + int flag; + char *errout; +{ + struct stat st; + + kd->vmfd = -1; + kd->pmfd = -1; + kd->swfd = -1; + kd->nlfd = -1; + kd->vmst = 0; + kd->db = 0; + kd->procbase = 0; + kd->argspc = 0; + kd->argv = 0; + + if (uf == 0) + uf = _PATH_UNIX; + 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 (sf == 0) + sf = _PATH_DRUM; + + if ((kd->pmfd = open(mf, flag, 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_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_MEM) != 0) { /* XXX */ + _kvm_err(kd, kd->program, + "%s: not physical memory device", mf); + goto failed; + } + if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) { + _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM); + goto failed; + } + if ((kd->swfd = open(sf, flag, 0)) < 0) { + _kvm_syserr(kd, kd->program, "%s", sf); goto failed; } - swapf = (sf == NULL) ? _PATH_DRUM : sf; /* - * live kernel - avoid looking up nlist entries - * past X_DEADKERNEL. + * Open kvm nlist database. We go ahead and do this + * here so that we don't have to hold on to the vmunix + * path name. Since a kvm application will surely do + * a kvm_nlist(), this probably won't be a wasted effort. + * If the database cannot be opened, open the namelist + * argument so we revert to slow nlist() calls. */ - nl[X_DEADKERNEL].n_name = ""; + if (kvm_dbopen(kd, uf) < 0 && + (kd->nlfd = open(uf, O_RDONLY, 0)) < 0) { + _kvm_syserr(kd, kd->program, "%s", uf); + goto failed; + } + } else { + /* + * This is a crash dump. + * Initalize the virtual address translation machinery, + * but first setup the namelist fd. + */ + if ((kd->nlfd = open(uf, O_RDONLY, 0)) < 0) { + _kvm_syserr(kd, kd->program, "%s", uf); + goto failed; + } + if (_kvm_initvtop(kd) < 0) + goto failed; } - if (swapf != NULL && ((swap = open(swapf, O_RDONLY, 0)) == -1)) { - seterr("can't open %s", swapf); - goto failed; - } - kvmfilesopen++; - if (kvminit == 0 && kvm_init(NULL, NULL, NULL, 0) == -1) /*XXX*/ - return (-1); - return (0); + return (kd); failed: - kvm_close(); - return (-1); + /* + * Copy out the error if doing sane error semantics. + */ + if (errout != 0) + strcpy(errout, kd->errbuf); + (void)kvm_close(kd); + return (0); } -static -kvm_init(uf, mf, sf) - char *uf, *mf, *sf; +kvm_t * +kvm_openfiles(uf, mf, sf, flag, errout) + const char *uf; + const char *mf; + const char *sf; + int flag; + char *errout; { - if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1) - return (-1); - if (getkvars() == -1) - return (-1); - kvminit = 1; + register kvm_t *kd; + + if ((kd = malloc(sizeof(*kd))) == NULL) { + (void)strcpy(errout, strerror(errno)); + return (0); + } + kd->program = 0; + return (_kvm_open(kd, uf, mf, sf, flag, errout)); +} + +kvm_t * +kvm_open(uf, mf, sf, flag, program) + const char *uf; + const char *mf; + const char *sf; + int flag; + const char *program; +{ + register kvm_t *kd; + + if ((kd = malloc(sizeof(*kd))) == NULL && program != NULL) { + (void)fprintf(stderr, "%s: %s\n", strerror(errno)); + return (0); + } + kd->program = program; + return (_kvm_open(kd, uf, mf, sf, flag, NULL)); +} + +int +kvm_close(kd) + kvm_t *kd; +{ + register int error = 0; + + if (kd->pmfd >= 0) + error |= close(kd->pmfd); + if (kd->vmfd >= 0) + error |= close(kd->vmfd); + if (kd->nlfd >= 0) + error |= close(kd->nlfd); + if (kd->swfd >= 0) + error |= close(kd->swfd); + if (kd->db != 0) + error |= (kd->db->close)(kd->db); + if (kd->vmst) + _kvm_freevtop(kd); + if (kd->procbase != 0) + free((void *)kd->procbase); + if (kd->argv != 0) + free((void *)kd->argv); + free((void *)kd); return (0); } -kvm_close() +/* + * Set up state necessary to do queries on the kernel namelist + * data base. If the data base is out-of-data/incompatible with + * given executable, set up things so we revert to standard nlist call. + * Only called for live kernels. Return 0 on success, -1 on failure. + */ +static int +kvm_dbopen(kd, uf) + kvm_t *kd; + const char *uf; { - if (unixx != -1) { - close(unixx); - unixx = -1; - } - if (kmem != -1) { - if (kmem != mem) - close(kmem); - /* otherwise kmem is a copy of mem, and will be closed below */ - kmem = -1; - } - if (mem != -1) { - close(mem); - mem = -1; - } - if (swap != -1) { - close(swap); - swap = -1; - } - if (db != NULL) { - dbm_close(db); - db = NULL; - } - kvminit = 0; - kvmfilesopen = 0; - deadkernel = 0; -} - -kvm_nlist(nl) - struct nlist *nl; -{ - datum key, data; - char dbname[MAXPATHLEN]; + char *cp; + DBT rec; + int dbversionlen; + struct nlist nitem; char dbversion[_POSIX2_LINE_MAX]; char kversion[_POSIX2_LINE_MAX]; - int dbversionlen; - char symbuf[MAXSYMSIZE]; - struct nlist nbuf, *n; - int num, did; + char dbname[MAXPATHLEN]; - if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1) + if ((cp = rindex(uf, '/')) != 0) + uf = cp + 1; + + (void)snprintf(dbname, sizeof(dbname), "%skvm_%s.db", _PATH_VARDB, uf); + kd->db = dbopen(dbname, O_RDONLY, 0, DB_HASH, NULL); + if (kd->db == 0) return (-1); - if (deadkernel) - goto hard2; - /* - * initialize key datum - */ - key.dptr = symbuf; - - if (db != NULL) - goto win; /* off to the races */ - /* - * open database - */ - sprintf(dbname, "%s/kvm_%s", _PATH_VARRUN, basename(unixf)); - if ((db = dbm_open(dbname, O_RDONLY, 0)) == NULL) - goto hard2; /* * read version out of database */ - bcopy("VERSION", symbuf, sizeof ("VERSION")-1); - key.dsize = (sizeof ("VERSION") - 1); - data = dbm_fetch(db, key); - if (data.dptr == NULL) - goto hard1; - bcopy(data.dptr, dbversion, data.dsize); - dbversionlen = data.dsize; + rec.data = VRS_KEY; + rec.size = sizeof(VRS_KEY) - 1; + if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0)) + goto close; + if (rec.data == 0 || rec.size > sizeof(dbversion)) + goto close; + + bcopy(rec.data, dbversion, rec.size); + dbversionlen = rec.size; /* - * read version string from kernel memory + * Read version string from kernel memory. + * Since we are dealing with a live kernel, we can call kvm_read() + * at this point. */ - bcopy("_version", symbuf, sizeof ("_version")-1); - key.dsize = (sizeof ("_version")-1); - data = dbm_fetch(db, key); - if (data.dptr == NULL) - goto hard1; - if (data.dsize != sizeof (struct nlist)) - goto hard1; - bcopy(data.dptr, &nbuf, sizeof (struct nlist)); - lseek(kmem, nbuf.n_value, 0); - if (read(kmem, kversion, dbversionlen) != dbversionlen) - goto hard1; + rec.data = VRS_SYM; + rec.size = sizeof(VRS_SYM) - 1; + if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0)) + goto close; + if (rec.data == 0 || rec.size != sizeof(struct nlist)) + goto close; + bcopy((char *)rec.data, (char *)&nitem, sizeof(nitem)); + if (kvm_read(kd, (u_long)nitem.n_value, kversion, dbversionlen) != + dbversionlen) + goto close; /* - * if they match, we win - otherwise do it the hard way + * If they match, we win - otherwise clear out kd->db so + * we revert to slow nlist(). */ - if (bcmp(dbversion, kversion, dbversionlen) != 0) - goto hard1; + if (bcmp(dbversion, kversion, dbversionlen) == 0) + return (0); +close: + (void)(kd->db->close)(kd->db); + kd->db = 0; + + return (-1); +} + +int +kvm_nlist(kd, nl) + kvm_t *kd; + struct nlist *nl; +{ + register struct nlist *p; + register int nvalid; + /* - * getem from the database. + * If we can't use the data base, revert to the + * slow library call. */ -win: - num = did = 0; - for (n = nl; n->n_name && n->n_name[0]; n++, num++) { - int len; - /* - * clear out fields from users buffer - */ - n->n_type = 0; - n->n_other = 0; - n->n_desc = 0; - n->n_value = 0; - /* - * query db - */ - if ((len = strlen(n->n_name)) > MAXSYMSIZE) { - seterr("symbol too large"); + if (kd->db == 0) + return (__fdnlist(kd->nlfd, nl)); + + /* + * We can use the kvm data base. Go through each nlist entry + * and look it up with a db query. + */ + nvalid = 0; + for (p = nl; p->n_name && p->n_name[0]; ++p) { + register int len; + DBT rec; + + if ((len = strlen(p->n_name)) > 4096) { + /* sanity */ + _kvm_err(kd, kd->program, "symbol too large"); return (-1); } - (void)strcpy(symbuf, n->n_name); - key.dsize = len; - data = dbm_fetch(db, key); - if (data.dptr == NULL || data.dsize != sizeof (struct nlist)) + rec.data = p->n_name; + rec.size = len; + if ((kd->db->get)(kd->db, (DBT *)&rec, (DBT *)&rec, 0)) continue; - bcopy(data.dptr, &nbuf, sizeof (struct nlist)); - n->n_value = nbuf.n_value; - n->n_type = nbuf.n_type; - n->n_desc = nbuf.n_desc; - n->n_other = nbuf.n_other; - did++; - } - return (num - did); -hard1: - dbm_close(db); - db = NULL; -hard2: - num = nlist(unixf, nl); - if (num == -1) - seterr("nlist (hard way) failed"); - return (num); -} - -kvm_getprocs(what, arg) - int what, arg; -{ - static int ocopysize = -1; - - if (kvminit == 0 && kvm_init(NULL, NULL, NULL, 0) == -1) - return (NULL); - if (!deadkernel) { - int ret, copysize; - - if ((ret = getkerninfo(what, NULL, NULL, arg)) == -1) { - setsyserr("can't get estimate for kerninfo"); - return (-1); - } - copysize = ret; - if (copysize > ocopysize || !kvmprocbase) { - if (ocopysize == -1 || !kvmprocbase) - kvmprocbase = - (struct kinfo_proc *)malloc(copysize); - else - kvmprocbase = - (struct kinfo_proc *)realloc(kvmprocbase, - copysize); - if (!kvmprocbase) { - seterr("out of memory"); - return (-1); - } - } - ocopysize = copysize; - if ((ret = getkerninfo(what, kvmprocbase, ©size, - arg)) == -1) { - setsyserr("can't get proc list"); - return (-1); - } - if (copysize % sizeof (struct kinfo_proc)) { - seterr("proc size mismatch (got %d total, kinfo_proc: %d)", - copysize, sizeof (struct kinfo_proc)); - return (-1); - } - kvmnprocs = copysize / sizeof (struct kinfo_proc); - } else { - int nproc; - - if (kvm_read((void *) nl[X_NPROC].n_value, &nproc, - sizeof (int)) == -1) { - seterr("can't read nproc"); - return (-1); - } - if ((kvmprocbase = (struct kinfo_proc *) - malloc(nproc * sizeof (struct kinfo_proc))) == NULL) { - seterr("out of memory (addr: %x nproc = %d)", - nl[X_NPROC].n_value, nproc); - return (-1); - } - kvmnprocs = kvm_doprocs(what, arg, kvmprocbase); - realloc(kvmprocbase, kvmnprocs * sizeof (struct kinfo_proc)); - } - kvmprocptr = kvmprocbase; - - return (kvmnprocs); -} - -/* - * XXX - should NOT give up so easily - especially since the kernel - * may be corrupt (it died). Should gather as much information as possible. - * Follows proc ptrs instead of reading table since table may go - * away soon. - */ -static -kvm_doprocs(what, arg, buff) - int what, arg; - char *buff; -{ - struct proc *p, proc; - register char *bp = buff; - int i = 0; - int doingzomb = 0; - struct eproc eproc; - struct pgrp pgrp; - struct session sess; - struct tty tty; - - /* allproc */ - if (kvm_read((void *) nl[X_ALLPROC].n_value, &p, - sizeof (struct proc *)) == -1) { - seterr("can't read allproc"); - return (-1); - } - -again: - for (; p; p = proc.p_next) { - if (kvm_read(p, &proc, sizeof (struct proc)) == -1) { - seterr("can't read proc at %x", p); - return (-1); - } - if (kvm_read(proc.p_cred, &eproc.e_pcred, - sizeof (struct pcred)) != -1) - (void) kvm_read(eproc.e_pcred.pc_ucred, &eproc.e_ucred, - sizeof (struct ucred)); - - switch(ki_op(what)) { - - case KINFO_PROC_PID: - if (proc.p_pid != (pid_t)arg) - continue; - break; - - - case KINFO_PROC_UID: - if (eproc.e_ucred.cr_uid != (uid_t)arg) - continue; - break; - - case KINFO_PROC_RUID: - if (eproc.e_pcred.p_ruid != (uid_t)arg) - continue; - break; - } + if (rec.data == 0 || rec.size != sizeof(struct nlist)) + continue; + ++nvalid; /* - * gather eproc + * Avoid alignment issues. */ - eproc.e_paddr = p; - if (kvm_read(proc.p_pgrp, &pgrp, sizeof (struct pgrp)) == -1) { - seterr("can't read pgrp at %x", proc.p_pgrp); - return (-1); - } - eproc.e_sess = pgrp.pg_session; - eproc.e_pgid = pgrp.pg_id; - eproc.e_jobc = pgrp.pg_jobc; - if (kvm_read(pgrp.pg_session, &sess, sizeof (struct session)) - == -1) { - seterr("can't read session at %x", pgrp.pg_session); - return (-1); - } - if ((proc.p_flag&P_CONTROLT) && sess.s_ttyp != NULL) { - if (kvm_read(sess.s_ttyp, &tty, sizeof (struct tty)) - == -1) { - seterr("can't read tty at %x", sess.s_ttyp); - return (-1); - } - eproc.e_tdev = tty.t_dev; - eproc.e_tsess = tty.t_session; - if (tty.t_pgrp != NULL) { - if (kvm_read(tty.t_pgrp, &pgrp, sizeof (struct - pgrp)) == -1) { - seterr("can't read tpgrp at &x", - tty.t_pgrp); - return (-1); - } - eproc.e_tpgid = pgrp.pg_id; - } else - eproc.e_tpgid = -1; - } else - eproc.e_tdev = NODEV; - if (proc.p_wmesg) - (void) kvm_read(proc.p_wmesg, eproc.e_wmesg, WMESGLEN); - (void) kvm_read(proc.p_vmspace, &eproc.e_vm, - sizeof (struct vmspace)); - eproc.e_xsize = eproc.e_xrssize = - eproc.e_xccount = eproc.e_xswrss = 0; - - switch(ki_op(what)) { - - case KINFO_PROC_PGRP: - if (eproc.e_pgid != (pid_t)arg) - continue; - break; - - case KINFO_PROC_TTY: - if ((proc.p_flag&P_CONTROLT) == 0 || - eproc.e_tdev != (dev_t)arg) - continue; - break; - } - - i++; - bcopy(&proc, bp, sizeof (struct proc)); - bp += sizeof (struct proc); - bcopy(&eproc, bp, sizeof (struct eproc)); - bp+= sizeof (struct eproc); - } - if (!doingzomb) { - /* zombproc */ - if (kvm_read((void *) nl[X_ZOMBPROC].n_value, &p, - sizeof (struct proc *)) == -1) { - seterr("can't read zombproc"); - return (-1); - } - doingzomb = 1; - goto again; - } - - return (i); -} - -struct proc * -kvm_nextproc() -{ - - if (!kvmprocbase && kvm_getprocs(0, 0) == -1) - return (NULL); - if (kvmprocptr >= (kvmprocbase + kvmnprocs)) { - seterr("end of proc list"); - return (NULL); - } - return((struct proc *)(kvmprocptr++)); -} - -struct eproc * -kvm_geteproc(p) - const struct proc *p; -{ - return ((struct eproc *)(((char *)p) + sizeof (struct proc))); -} - -kvm_setproc() -{ - kvmprocptr = kvmprocbase; -} - -kvm_freeprocs() -{ - - if (kvmprocbase) { - free(kvmprocbase); - kvmprocbase = NULL; - } -} - -struct user * -kvm_getu(p) - const struct proc *p; -{ - register struct kinfo_proc *kp = (struct kinfo_proc *)p; - register int i; - register char *up; - u_int vaddr; - struct swapblk swb; - - if (kvminit == 0 && kvm_init(NULL, NULL, NULL, 0) == -1) - return (NULL); - if (p->p_stat == SZOMB) { - seterr("zombie process"); - return (NULL); - } - - if ((p->p_flag & P_INMEM) == 0) { - vm_offset_t maddr; - - if (swap < 0) { - seterr("no swap"); - return (NULL); - } - /* - * Costly operation, better set enable_swap to zero - * in vm/vm_glue.c, since paging of user pages isn't - * done yet anyway. - */ - if (vatosw(&kp->kp_eproc.e_vm.vm_map, USRSTACK + i * NBPG, - &maddr, &swb) == 0) - return NULL; - - if (maddr == 0 && swb.size < UPAGES * NBPG) - return NULL; - - for (i = 0; i < UPAGES; i++) { - if (maddr) { - (void) lseek(mem, maddr + i * NBPG, 0); - if (read(mem, - (char *)user.upages[i], NBPG) != NBPG) { - setsyserr( - "can't read u for pid %d from %s", - p->p_pid, swapf); - return NULL; - } - } else { - (void) lseek(swap, swb.offset + i * NBPG, 0); - if (read(swap, - (char *)user.upages[i], NBPG) != NBPG) { - setsyserr( - "can't read u for pid %d from %s", - p->p_pid, swapf); - return NULL; - } - } - } - return(&user.user); + bcopy((char *)&((struct nlist *)rec.data)->n_type, + (char *)&p->n_type, + sizeof(p->n_type)); + bcopy((char *)&((struct nlist *)rec.data)->n_value, + (char *)&p->n_value, + sizeof(p->n_value)); } /* - * Read u-area one page at a time for the benefit of post-mortems + * Return the number of entries that weren't found. */ - up = (char *) p->p_addr; - for (i = 0; i < UPAGES; i++) { - klseek(kmem, (off_t)(long)up, 0); - if (read(kmem, user.upages[i], CLBYTES) != CLBYTES) { - setsyserr("cant read page %x of u of pid %d from %s", - up, p->p_pid, kmemf); - return(NULL); - } - up += CLBYTES; - } - pcbpf = (int) btop(p->p_addr); /* what should this be really? */ - - return(&user.user); + return ((p - nl) - nvalid); } -int -kvm_procread(p, addr, buf, len) - const struct proc *p; - const unsigned addr; - unsigned len; - char *buf; +ssize_t +kvm_read(kd, kva, buf, len) + kvm_t *kd; + register u_long kva; + register void *buf; + register size_t len; { - register struct kinfo_proc *kp = (struct kinfo_proc *) p; - struct swapblk swb; - vm_offset_t swaddr = 0, memaddr = 0; - unsigned real_len; - static int last_pid = -1; - static vm_offset_t last_addr; - static char bouncebuf[CLBYTES]; + register int cc; + register void *cp; - real_len = len < (CLBYTES - (addr & CLOFSET)) ? len : (CLBYTES - (addr & CLOFSET)); - - if (p->p_pid != last_pid || last_addr != (addr & ~CLOFSET)) { - if (vatosw(&kp->kp_eproc.e_vm.vm_map, addr & ~CLOFSET, &memaddr, - &swb) == 0) - return 0; - - if (memaddr) { -#if defined(sparc) - memaddr = phys2realphys(memaddr); -#endif - if (lseek(mem, memaddr, 0) == -1) { - setsyserr("kvm_procread: lseek mem"); - return 0; - } - len = read(mem, bouncebuf, CLBYTES); - if (len == -1 || len < CLBYTES) { - last_pid = -1; - setsyserr("kvm_procread: read mem"); - return 0; - } - } else { - swaddr = swb.offset; - if (lseek(swap, swaddr, 0) == -1) { - setsyserr("kvm_procread: lseek swap"); - return 0; - } - len = read(swap, bouncebuf, CLBYTES); - if (len == -1 || len < CLBYTES) { - last_pid = -1; - setsyserr("kvm_procread: read swap"); - return 0; - } + if (ISALIVE(kd)) { + /* + * We're using /dev/kmem. Just read straight from the + * device and let the active kernel do the address translation. + */ + errno = 0; + if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { + _kvm_err(kd, 0, "invalid address (%x)", kva); + return (0); } - } - - memcpy(buf, &bouncebuf[addr & CLOFSET], real_len); - last_pid = p->p_pid; - last_addr = addr & ~CLOFSET; - return real_len; -} - -int -kvm_procreadstr(p, addr, buf, len) - const struct proc *p; - const unsigned addr; - char *buf; - unsigned len; -{ - int done, little; - char copy[200], *pb; - char a; - - done = 0; - copy[0] = '\0'; - while (len) { - little = kvm_procread(p, addr+done, copy, MIN(len, sizeof copy)); - if (little<1) - break; - pb = copy; - while (little--) { - len--; - if( (*buf++ = *pb++) == '\0' ) - return done; - done++; - } - } - return done; -} - -char * -kvm_getargs(p, up) - const struct proc *p; - const struct user *up; -{ - static char *cmdbuf = NULL, ucomm[sizeof(p->p_comm) + 4]; - register char *cp, *acp; - int left, rv; - struct ps_strings arginfo; - - if (cmdbuf == NULL) { - cmdbuf = (char *)malloc(ARG_MAX + sizeof(p->p_comm) + 5); - if (cmdbuf == NULL) - cmdbuf = ucomm; - } - - if (cmdbuf == ucomm || up == NULL || p->p_pid == 0 || p->p_pid == 2) - goto retucomm; - - if (kvm_procread(p, PS_STRINGS, (char *)&arginfo, sizeof(arginfo)) != - sizeof(arginfo)) - goto bad; - - cmdbuf[0] = '\0'; - cp = cmdbuf; - acp = arginfo.ps_argvstr; - left = ARG_MAX + 1; - while (arginfo.ps_nargvstr--) { - if ((rv = kvm_procreadstr(p, acp, cp, left)) >= 0) { - acp += rv + 1; - left -= rv + 1; - cp += rv; - *cp++ = ' '; - *cp = '\0'; - } else - goto bad; - } - cp-- ; *cp = '\0'; - - if (cmdbuf[0] == '-' || cmdbuf[0] == '?' || cmdbuf[0] <= ' ') { - (void) strcat(cmdbuf, " ("); - (void) strncat(cmdbuf, p->p_comm, sizeof(p->p_comm)); - (void) strcat(cmdbuf, ")"); - } - return (cmdbuf); - -bad: - seterr("error locating command name for pid %d", p->p_pid); -retucomm: - (void) strcpy(cmdbuf, "("); - (void) strncat(cmdbuf, p->p_comm, sizeof (p->p_comm)); - (void) strcat(cmdbuf, ")"); - return (cmdbuf); -} - -char * -kvm_getenv(p, up) - const struct proc *p; - const struct user *up; -{ - static char *envbuf = NULL, emptyenv[1]; - register char *cp, *acp; - int left, rv; - struct ps_strings arginfo; - - if (envbuf == NULL) { - envbuf = (char *)malloc(ARG_MAX + 1); - if (envbuf == NULL) - envbuf = emptyenv; - } - - if (envbuf == emptyenv || up == NULL || p->p_pid == 0 || p->p_pid == 2) - goto retemptyenv; - - if (kvm_procread(p, PS_STRINGS, (char *)&arginfo, sizeof(arginfo)) != - sizeof(arginfo)) - goto bad; - - cp = envbuf; - acp = arginfo.ps_envstr; - left = ARG_MAX + 1; - while (arginfo.ps_nenvstr--) { - if ((rv = kvm_procreadstr(p, acp, cp, left)) >= 0) { - acp += rv + 1; - left -= rv + 1; - cp += rv; - *cp++ = ' '; - *cp = '\0'; - } else - goto bad; - } - cp-- ; *cp = '\0'; - return (envbuf); - -bad: - seterr("error locating environment for pid %d", p->p_pid); -retemptyenv: - envbuf[0] = '\0'; - return (envbuf); -} - -static -getkvars() -{ - if (kvm_nlist(nl) == -1) - return (-1); - if (deadkernel) { - /* We must do the sys map first because klseek uses it */ - long addr; - -#if defined(m68k) -#if defined(amiga) || defined(mac68k) - addr = (long) nl[X_CPU040].n_value; - (void) lseek(kmem, addr, 0); - if (read(kmem, (char *) &cpu040, sizeof (cpu040)) - != sizeof (cpu040)) { - seterr("can't read cpu040"); - return (-1); - } -#endif - addr = (long) nl[X_LOWRAM].n_value; - (void) lseek(kmem, addr, 0); - if (read(kmem, (char *) &lowram, sizeof (lowram)) - != sizeof (lowram)) { - seterr("can't read lowram"); - return (-1); - } - lowram = btop(lowram); - Sysseg = (struct ste *) malloc(NBPG); - if (Sysseg == NULL) { - seterr("out of space for Sysseg"); - return (-1); - } - addr = (long) nl[X_SYSSEG].n_value; - (void) lseek(kmem, addr, 0); - read(kmem, (char *)&addr, sizeof(addr)); - (void) lseek(kmem, (long)addr, 0); - if (read(kmem, (char *) Sysseg, NBPG) != NBPG) { - seterr("can't read Sysseg"); - return (-1); - } -#endif -#if defined(i386) - PTD = (struct pde *) malloc(NBPG); - if (PTD == NULL) { - seterr("out of space for PTD"); - return (-1); - } - addr = (long) nl[X_IdlePTD].n_value; - (void) lseek(kmem, addr, 0); - read(kmem, (char *)&addr, sizeof(addr)); - (void) lseek(kmem, (long)addr, 0); - if (read(kmem, (char *) PTD, NBPG) != NBPG) { - seterr("can't read PTD"); - return (-1); - } -#endif - } - if (kvm_read((void *) nl[X_NSWAP].n_value, &nswap, sizeof (long)) == -1) { - seterr("can't read nswap"); - return (-1); - } - if (kvm_read((void *) nl[X_DMMIN].n_value, &dmmin, sizeof (long)) == -1) { - seterr("can't read dmmin"); - return (-1); - } - if (kvm_read((void *) nl[X_DMMAX].n_value, &dmmax, sizeof (long)) == -1) { - seterr("can't read dmmax"); - return (-1); - } - if (kvm_read((void *) nl[X_VM_PAGE_HASH_MASK].n_value, - &vm_page_hash_mask, sizeof (long)) == -1) { - seterr("can't read vm_page_hash_mask"); - return (-1); - } - if (kvm_read((void *) nl[X_VM_PAGE_BUCKETS].n_value, - &vm_page_buckets, sizeof (long)) == -1) { - seterr("can't read vm_page_buckets"); - return (-1); - } - if (kvm_read((void *) nl[X_PAGE_SHIFT].n_value, - &page_shift, sizeof (long)) == -1) { - seterr("can't read page_shift"); - return (-1); - } - - return (0); -} - -int -kvm_read(loc, buf, len) - void *loc; - void *buf; -{ - int n; - - if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1) - return (-1); - klseek(kmem, (off_t)(long)loc, 0); - if ((n = read(kmem, buf, len)) != len) { - if (n == -1) - setsyserr("error reading kmem at %#x", loc); - else - seterr("short read on kmem at %#x", loc); - return (-1); - } - return (len); -} - -static void -klseek(fd, loc, off) - int fd; - off_t loc; - int off; -{ - - if (deadkernel) { - if ((loc = Vtophys(loc)) == -1) - return; - } - (void) lseek(fd, (off_t)loc, off); -} - -static off_t -Vtophys(loc) - u_long loc; -{ - off_t newloc = (off_t) -1; -#if defined(m68k) - int p, ste, pte; - - ste = *(int *)&Sysseg[btos(loc)]; - if ((ste & SG_V) == 0) { - seterr("vtophys: segment not valid"); - return((off_t) -1); - } - p = btop(loc & SG_PMASK); - newloc = (ste & SG_FRAME) + (p * sizeof(struct pte)); - (void) lseek(mem, newloc, 0); - if (read(mem, (char *)&pte, sizeof pte) != sizeof pte) { - seterr("vtophys: cannot locate pte"); - return((off_t) -1); - } - newloc = pte & PG_FRAME; - if (pte == PG_NV || newloc < (off_t)ptob(lowram)) { - seterr("vtophys: page not valid"); - return((off_t) -1); - } - newloc = (newloc - (off_t)ptob(lowram)) + (loc & PGOFSET); -#endif -#if defined(i386) - struct pde pde; - struct pte pte; - int p; - - pde = PTD[loc >> PDSHIFT]; - if (pde.pd_v == 0) { - seterr("vtophys: page directory entry not valid"); - return((off_t) -1); - } - p = btop(loc & PT_MASK); - newloc = pde.pd_pfnum + (p * sizeof(struct pte)); - (void) lseek(kmem, (long)newloc, 0); - if (read(kmem, (char *)&pte, sizeof pte) != sizeof pte) { - seterr("vtophys: cannot obtain desired pte"); - return((off_t) -1); - } - newloc = pte.pg_pfnum; - if (pte.pg_v == 0) { - seterr("vtophys: page table entry not valid"); - return((off_t) -1); - } - newloc += (loc & PGOFSET); -#endif - return((off_t) newloc); -} - -/* - * locate address of unwired or swapped page - */ - -static int -vatosw(mp, vaddr, maddr, swb) -vm_map_t mp; -vm_offset_t vaddr; -vm_offset_t *maddr; -struct swapblk *swb; -{ - struct vm_object vm_object; - struct vm_map_entry vm_entry; - long saddr, addr, off; - int i; - - saddr = addr = (long)mp->header.next; -#ifdef DEBUG - fprintf(stderr, "vatosw: head=%x\n", &mp->header); -#endif - for (i = 0; ; i++) { - /* Weed through map entries until vaddr in range */ - if (kvm_read((void *) addr, &vm_entry, sizeof(vm_entry)) == -1) { - seterr("vatosw: can't read vm_map_entry"); - return 0; - } -#ifdef DEBUG - fprintf(stderr, "vatosw: %d/%d, vaddr=%x, start=%x, end=%x ", - i, mp->nentries, vaddr, vm_entry.start, vm_entry.end); - fprintf(stderr, "addr=%x, next=%x\n", addr, vm_entry.next); -#endif - if ((vaddr >= vm_entry.start) && (vaddr < vm_entry.end)) - if (vm_entry.object.vm_object != 0) + cc = read(kd->vmfd, buf, len); + if (cc < 0) { + _kvm_syserr(kd, 0, "kvm_read"); + return (0); + } else if (cc < len) + _kvm_err(kd, kd->program, "short read"); + return (cc); + } else { + cp = buf; + while (len > 0) { + u_long pa; + + cc = _kvm_kvatop(kd, kva, &pa); + if (cc == 0) + return (0); + if (cc > len) + cc = len; + errno = 0; + if (lseek(kd->pmfd, (off_t)pa, 0) == -1 && errno != 0) { + _kvm_syserr(kd, 0, _PATH_MEM); break; - else { - seterr("vatosw: no object\n"); - return 0; } - - addr = (long)vm_entry.next; - - if (addr == saddr) { - seterr("vatosw: map not found\n"); - return 0; + cc = read(kd->pmfd, cp, cc); + if (cc < 0) { + _kvm_syserr(kd, kd->program, "kvm_read"); + break; + } + /* + * If kvm_kvatop returns a bogus value or our core + * file is truncated, we might wind up seeking beyond + * the end of the core file in which case the read will + * return 0 (EOF). + */ + if (cc == 0) + break; + (char *)cp += cc; + kva += cc; + len -= cc; } + return ((char *)cp - (char *)buf); } + /* NOTREACHED */ +} - if (vm_entry.is_a_map || vm_entry.is_sub_map) { -#ifdef DEBUG - fprintf(stderr, "vatosw: is a %smap\n", - vm_entry.is_sub_map ? "sub " : ""); -#endif - seterr("vatosw: is a %smap\n", - vm_entry.is_sub_map ? "sub " : ""); - return 0; - } +ssize_t +kvm_write(kd, kva, buf, len) + kvm_t *kd; + register u_long kva; + register const void *buf; + register size_t len; +{ + register int cc; - /* Locate memory object */ - off = (vaddr - vm_entry.start) + vm_entry.offset; - addr = (long)vm_entry.object.vm_object; - while (1) { - if (kvm_read((void *) addr, &vm_object, sizeof (vm_object)) == -1) { - seterr("vatosw: can't read vm_object"); - return 0; + if (ISALIVE(kd)) { + /* + * Just like kvm_read, only we write. + */ + errno = 0; + if (lseek(kd->vmfd, (off_t)kva, 0) == -1 && errno != 0) { + _kvm_err(kd, 0, "invalid address (%x)", kva); + return (0); } - -#ifdef DEBUG - fprintf(stderr, "vatosw: find page: object %#x offset %x\n", - addr, off); -#endif - - /* Lookup in page queue */ - if ((i = findpage(addr, off, maddr)) != -1) - return i; - - if (vm_object.pager != 0 && - (i = pager_get(&vm_object, off, swb)) != -1) - return i; - - if (vm_object.shadow == 0) - break; - -#ifdef DEBUG - fprintf(stderr, "vatosw: shadow obj at %x: offset %x+%x\n", - addr, off, vm_object.shadow_offset); -#endif - - addr = (long)vm_object.shadow; - off += vm_object.shadow_offset; + cc = write(kd->vmfd, buf, len); + if (cc < 0) { + _kvm_syserr(kd, 0, "kvm_write"); + return (0); + } else if (cc < len) + _kvm_err(kd, kd->program, "short write"); + return (cc); + } else { + _kvm_err(kd, kd->program, + "kvm_write not implemented for dead kernels"); + return (0); } - - seterr("vatosw: page not found\n"); - return 0; -} - - -int -pager_get(object, off, swb) -struct vm_object *object; -long off; -struct swapblk *swb; -{ - struct pager_struct pager; - struct swpager swpager; - struct swblock swblock; - - /* Find address in swap space */ - if (kvm_read(object->pager, &pager, sizeof (pager)) == -1) { - seterr("pager_get: can't read pager"); - return 0; - } - if (pager.pg_type != PG_SWAP) { - seterr("pager_get: weird pager\n"); - return 0; - } - - /* Get swap pager data */ - if (kvm_read(pager.pg_data, &swpager, sizeof (swpager)) == -1) { - seterr("pager_get: can't read swpager"); - return 0; - } - - off += object->paging_offset; - - /* Read swap block array */ - if (kvm_read((void *) swpager.sw_blocks + - (off/dbtob(swpager.sw_bsize)) * sizeof swblock, - &swblock, sizeof (swblock)) == -1) { - seterr("pager_get: can't read swblock"); - return 0; - } - - off %= dbtob(swpager.sw_bsize); - - if (swblock.swb_mask & (1 << atop(off))) { - swb->offset = dbtob(swblock.swb_block) + off; - swb->size = dbtob(swpager.sw_bsize) - off; - return 1; - } - - return -1; -} - -static int -findpage(object, offset, maddr) -long object; -long offset; -vm_offset_t *maddr; -{ - struct pglist bucket; - struct vm_page mem; - long addr, baddr; - - baddr = vm_page_buckets + - vm_page_hash(object,offset) * sizeof(struct pglist); - - if (kvm_read((void *) baddr, &bucket, sizeof (bucket)) == -1) { - seterr("can't read vm_page_bucket"); - return 0; - } - - addr = (long)bucket.tqh_first; - - while (addr != NULL) { - if (kvm_read((void *) addr, &mem, sizeof (mem)) == -1) { - seterr("can't read vm_page"); - return 0; - } - - if ((long)mem.object == object && mem.offset == offset) { - *maddr = (long)mem.phys_addr; - return 1; - } - - addr = (long)mem.hashq.tqe_next; - } - - return -1; -} - -#if defined(sparc) -/* - * This comes from the bowels of pmap.c - */ -#define MAXMEM (128 * 1024 * 1024) /* no more than 128 MB phys mem */ -#define NPGBANK 16 /* 2^4 pages per bank (64K / bank) */ -#define BSHIFT 4 /* log2(NPGBANK) */ -#define BOFFSET (NPGBANK - 1) -#define BTSIZE (MAXMEM / NBPG / NPGBANK) - -static int pmap_dtos[BTSIZE]; /* dense to sparse */ -static int pmap_stod[BTSIZE]; /* sparse to dense */ - -#define HWTOSW(pg) (pmap_stod[(pg) >> BSHIFT] | ((pg) & BOFFSET)) -#define SWTOHW(pg) (pmap_dtos[(pg) >> BSHIFT] | ((pg) & BOFFSET)) -/* -- */ - -static int pmap_dtos_valid; - -/* - * Translate a VM physical address to a hardware physical address. - */ -static vm_offset_t -phys2realphys(memaddr) -vm_offset_t memaddr; -{ - if (nl[X_PMAP_DTOS].n_value == 0) - /* This is possibly a sun4 */ - return memaddr; - - if (!pmap_dtos_valid) { - if (kvm_read((void *)nl[X_PMAP_DTOS].n_value, - pmap_dtos, sizeof (pmap_dtos)) == -1) { - seterr("can't read pmap_dtos table"); - return -1; - } - pmap_dtos_valid = 1; - } - return (SWTOHW(atop(memaddr)) << PGSHIFT) | (memaddr & PGOFSET); -} -#endif - -#include -static char errbuf[_POSIX2_LINE_MAX]; - -static void -seterr(va_alist) - va_dcl -{ - char *fmt; - va_list ap; - - va_start(ap); - fmt = va_arg(ap, char *); - (void) vsnprintf(errbuf, _POSIX2_LINE_MAX, fmt, ap); -#ifdef DEBUG - (void) fprintf(stderr, "%s\n", errbuf); -#endif - va_end(ap); -} - -static void -setsyserr(va_alist) - va_dcl -{ - char *fmt, *cp; - va_list ap; - extern int errno; - - va_start(ap); - fmt = va_arg(ap, char *); - (void) vsnprintf(cp = errbuf, _POSIX2_LINE_MAX, fmt, ap); - cp += strlen(cp); - (void) snprintf(cp, _POSIX2_LINE_MAX - (cp - errbuf), ": %s", - strerror(errno)); -#ifdef DEBUG - (void) fprintf(stderr, "%s\n", errbuf); -#endif - va_end(ap); -} - -char * -kvm_geterr() -{ - return (errbuf); + /* NOTREACHED */ }