move the kernel file to /var/db
[unix-history] / usr / src / lib / libkvm / kvm.c
index 1621b96..3df3668 100644 (file)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
  */
 
 #if defined(LIBC_SCCS) && !defined(lint)
-static char sccsid[] = "@(#)kvm.c      5.11 (Berkeley) %G%";
+static char sccsid[] = "@(#)kvm.c      5.25 (Berkeley) %G%";
 #endif /* LIBC_SCCS and not lint */
 
 #endif /* LIBC_SCCS and not lint */
 
-#include <machine/pte.h>
-#include <machine/vmparam.h>
 #include <sys/param.h>
 #include <sys/user.h>
 #include <sys/proc.h>
 #include <sys/param.h>
 #include <sys/user.h>
 #include <sys/proc.h>
-#include <sys/file.h>
-#include <sys/text.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/vmmac.h>
 #include <sys/ioctl.h>
 #include <sys/ioctl.h>
-#include <sys/tty.h>
-#include <kvm.h>
-#include <ctype.h>
-#include <vis.h>
+#include <sys/stat.h>
+#include <machine/vmparam.h>
+#include <fcntl.h>
 #include <nlist.h>
 #include <nlist.h>
-#include <pwd.h>
-#include <string.h>
+#include <kvm.h>
 #include <ndbm.h>
 #include <ndbm.h>
-#include <limits.h>
 #include <paths.h>
 #include <stdio.h>
 #include <paths.h>
 #include <stdio.h>
+#include <string.h>
+#include <ctype.h>
 
 
-/*
- * files
- */
-static 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;
-/*
- * random other stuff
- */
-static struct pte *Usrptmap, *usrpt;
-static int     dmmin, dmmax;
-static struct  pte *Sysmap;
-static int     Syssize;
-static int     pcbpf;
-static int     argaddr0;       /* XXX */
-static int     argaddr1;
-static int     nswap;
-static char    *tmp;
-#if defined(hp300)
-static int     lowram;
-#endif
-
-#define basename(cp)   ((tmp=rindex((cp), '/')) ? tmp+1 : (cp))
-#define        MAXSYMSIZE      256
-
-#if defined(hp300)
-#define pftoc(f)       ((f) - lowram)
-#define iskva(v)       (1)
-#endif
-
-#ifndef pftoc
-#define pftoc(f)       (f)
-#endif
-#ifndef iskva
-#define iskva(v)       ((v) & KERNBASE)
-#endif
-
-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
-       /*
-        * everything here and down, only if a dead kernel
-        */
-       { "_Sysmap" },
-#define        X_SYSMAP        5
-#define        X_DEADKERNEL    X_SYSMAP
-       { "_Syssize" },
-#define        X_SYSSIZE       6
-       { "_allproc" },
-#define X_ALLPROC      7
-       { "_zombproc" },
-#define X_ZOMBPROC     8
-       { "_nproc" },
-#define        X_NPROC         9
-#define        X_LAST          9
-#if defined(hp300)
-       { "_lowram" },
-#define        X_LOWRAM        (X_LAST+1)
-#endif
-       { "" },
-};
+#include <vm/vm.h>     /* ??? kinfo_proc currently includes this*/
+#include <vm/vm_param.h>
+#include <vm/swap_pager.h>
+#include <sys/kinfo_proc.h>
 
 
-/*
- * 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)
-       char *uf, *mf, *sf; 
-{
-       if (kvmfilesopen)
-               return (1);
-       unixx = mem = kmem = swap = -1;
-       unixf = (uf == NULL) ? _PATH_UNIX : uf; 
-       memf = (mf == NULL) ? _PATH_MEM : mf;
-
-       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;
-       } else {
-               kmemf = _PATH_KMEM;
-               if ((kmem = open(kmemf, O_RDONLY, 0)) == -1) {
-                       setsyserr("can't open %s", kmemf);
-                       goto failed;
-               }
-               swapf = (sf == NULL) ?  _PATH_DRUM : sf;
-               /*
-                * live kernel - avoid looking up nlist entries
-                * past X_DEADKERNEL.
-                */
-               nl[X_DEADKERNEL].n_name = "";
-       }
-       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);
-failed:
-       kvm_close();
-       return (-1);
-}
+#include <limits.h>
 
 
-static
-kvm_init(uf, mf, sf)
-       char *uf, *mf, *sf;
-{
-       if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1)
-               return (-1);
-       if (getkvars() == -1)
-               return (-1);
-       kvminit = 1;
+#include "kvm_private.h"
 
 
-       return (0);
-}
+#include <stdarg.h>
+
+static int kvm_dbopen(kvm_t *, const char *);
 
 
-kvm_close()
+char *
+kvm_geterr(kvm_t *kd)
 {
 {
-       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;
-       if (Sysmap) {
-               free(Sysmap);
-               Sysmap = NULL;
-       }
+       return (kd->errbuf);
 }
 
 }
 
-kvm_nlist(nl)
-       struct nlist *nl;
+/*
+ * 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
+_kvm_err(kvm_t *kd, const char *program, const char *fmt, ...)
 {
 {
-       datum key, data;
-       char dbname[MAXPATHLEN];
-       char dbversion[_POSIX2_LINE_MAX];
-       char kversion[_POSIX2_LINE_MAX];
-       int dbversionlen;
-       char symbuf[MAXSYMSIZE+1];
-       struct nlist nbuf, *n;
-       int num, did;
+       va_list ap;
 
 
-       if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1)
-               return (-1);
-       if (deadkernel)
-               goto hard2;
-       /*
-        * initialize key datum
-        */
-       key.dptr = symbuf;
-       symbuf[0] = KVMDB_NLIST;
+       va_start(ap, fmt);
+       if (program != 0) {
+               fprintf(stderr, "%s: ", program);
+               vfprintf(stderr, fmt, ap);
+               fputc('\n', stderr);
+       } else
+               vsnprintf(kd->errbuf, sizeof(kd->errbuf), (char *)fmt, ap);
 
 
-       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+1, sizeof ("VERSION")-1);
-       key.dsize = (sizeof ("VERSION") - 1) + 1;
-       data = dbm_fetch(db, key);
-       if (data.dptr == NULL)
-               goto hard1;
-       bcopy(data.dptr, dbversion, data.dsize);
-       dbversionlen = data.dsize;
-       /*
-        * read version string from kernel memory
-        */
-       bcopy("_version", symbuf+1, sizeof ("_version")-1);
-       key.dsize = (sizeof ("_version")-1) + 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;
-       /*
-        * if they match, we win - otherwise do it the hard way
-        */
-       if (bcmp(dbversion, kversion, dbversionlen) != 0)
-               goto hard1;
-       /*
-        * getem from the database.
-        */
-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("kvm_nlist: symbol too large");
-                       return (-1);
-               }
-               strcpy(symbuf+1, n->n_name);
-               key.dsize = len + 1;
-               data = dbm_fetch(db, key);
-               if (data.dptr == NULL || data.dsize != sizeof (struct nlist))
-                       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:
-       return (nlist(unixf, nl));      /* XXX seterr if -1 */
+       va_end(ap);
 }
 
 }
 
-kvm_getprocs(what, arg)
+void
+_kvm_syserr(kvm_t *kd, const char *program, const char *fmt, ...)
 {
 {
-       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 ((kvmprocbase = (struct kinfo_proc *)malloc(copysize)) 
-                    == NULL) {
-                       seterr("out of memory");
-                       return (-1);
-               }
-               if ((ret = getkerninfo(what, kvmprocbase, &copysize, 
-                    arg)) == -1) {
-                       setsyserr("can't get proc list");
-                       return (-1);
-               }
-               if (copysize % sizeof (struct kinfo_proc)) {
-                       seterr("proc size mismatch (kinfo_proc: %d)",
-                               sizeof (struct kinfo_proc));
-                       return (-1);
-               }
-               kvmnprocs = copysize / sizeof (struct kinfo_proc);
+       va_list ap;
+       register int n;
+
+       va_start(ap, fmt);
+       if (program != 0) {
+               fprintf(stderr, "%s: ", program);
+               vfprintf(stderr, fmt, ap);
+               fprintf(stderr, ": ");
+               perror((char *)0);
        } else {
        } else {
-               int nproc;
+               register char *cp = kd->errbuf;
 
 
-               if (kvm_read(nl[X_NPROC].n_value, &nproc, sizeof (int)) !=
-                   sizeof (int)) {
-                       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));
+               vsnprintf(cp, sizeof(kd->errbuf), (char *)fmt, ap);
+               n = strlen(cp);
+               snprintf(&cp[n], sizeof(kd->errbuf) - n, ": %s",
+                        strerror(errno));
        }
        }
-       kvmprocptr = kvmprocbase;
+       va_end(ap);
+}
 
 
-       return (kvmnprocs);
+void *
+_kvm_malloc(kd, n)
+       register kvm_t *kd;
+       register size_t n;
+{
+       void *p = (void *)malloc(n);
+
+       if (p == 0)
+               _kvm_err(kd, kd->program, "out of memory");
+       return (p);
 }
 
 }
 
-/*
- * 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;
+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 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;
-       struct text text;
-
-       /* allproc */
-       if (kvm_read(nl[X_ALLPROC].n_value, &p, 
-           sizeof (struct proc *)) != sizeof (struct proc *)) {
-               seterr("can't read allproc");
-               return (-1);
+       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;
 
 
-again:
-       for (; p; p = proc.p_nxt) {
-               if (kvm_read(p, &proc, sizeof (struct proc)) !=
-                   sizeof (struct proc)) {
-                       seterr("can't read proc at %x", p);
-                       return (-1);
-               }
-               switch(ki_op(what)) {
-                       
-               case KINFO_PROC_PID:
-                       if (proc.p_pid != (pid_t)arg)
-                               continue;
-                       break;
-
-
-               case KINFO_PROC_UID:
-                       if (proc.p_uid != (uid_t)arg)
-                               continue;
-                       break;
-
-               case KINFO_PROC_RUID:
-                       if (proc.p_ruid != (uid_t)arg)
-                               continue;
-                       break;
-               }
+       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)) {
                /*
                /*
-                * gather eproc
+                * 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.)
                 */
                 */
-               eproc.e_paddr = p;
-               if (kvm_read(proc.p_pgrp, &pgrp, sizeof (struct pgrp)) !=
-                   sizeof (struct pgrp)) {
-                       seterr("can't read pgrp at %x", proc.p_pgrp);
-                       return (-1);
+               if (strcmp(mf, _PATH_MEM) != 0) {       /* XXX */
+                       _kvm_err(kd, kd->program,
+                                "%s: not physical memory device", mf);
+                       goto failed;
                }
                }
-               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))
-                  != sizeof (struct session)) {
-                       seterr("can't read session at %x", pgrp.pg_session);
-                       return (-1);
+               if ((kd->vmfd = open(_PATH_KMEM, flag)) < 0) {
+                       _kvm_syserr(kd, kd->program, "%s", _PATH_KMEM);
+                       goto failed;
                }
                }
-               if ((proc.p_flag&SCTTY) && sess.s_ttyp != NULL) {
-                       if (kvm_read(sess.s_ttyp, &tty, sizeof (struct tty))
-                           != sizeof (struct tty)) {
-                               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)) != sizeof (struct pgrp)) {
-                                       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)
-                       kvm_read(proc.p_wmesg, eproc.e_wmesg, WMESGLEN);
-               if (proc.p_textp) {
-                       kvm_read(proc.p_textp, &text, sizeof (text));
-                       eproc.e_xsize = text.x_size;
-                       eproc.e_xrssize = text.x_rssize;
-                       eproc.e_xccount = text.x_ccount;
-                       eproc.e_xswrss = text.x_swrss;
-               } else {
-                       eproc.e_xsize = eproc.e_xrssize =
-                         eproc.e_xccount = eproc.e_xswrss = 0;
+               if ((kd->swfd = open(sf, flag, 0)) < 0) {
+                       _kvm_syserr(kd, kd->program, "%s", sf);
+                       goto failed;
                }
                }
-
-               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&SCTTY) == 0 || 
-                            eproc.e_tdev != (dev_t)arg)
-                               continue;
-                       break;
+               /*
+                * 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.
+                */
+               if (kvm_dbopen(kd, uf) < 0 && 
+                   (kd->nlfd = open(uf, O_RDONLY, 0)) < 0) {
+                       _kvm_syserr(kd, kd->program, "%s", uf);
+                       goto failed;
                }
                }
-
-               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(nl[X_ZOMBPROC].n_value, &p, 
-                   sizeof (struct proc *)) != sizeof (struct proc *)) {
-                       seterr("can't read zombproc");
-                       return (-1);
+       } 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;
                }
                }
-               doingzomb = 1;
-               goto again;
+               if (_kvm_initvtop(kd) < 0)
+                       goto failed;
        }
        }
-
-       return (i);
+       return (kd);
+failed:
+       /*
+        * Copy out the error if doing sane error semantics.
+        */
+       if (errout != 0)
+               strcpy(errout, kd->errbuf);
+       (void)kvm_close(kd);
+       return (0);
 }
 
 }
 
-struct proc *
-kvm_nextproc()
+kvm_t *
+kvm_openfiles(uf, mf, sf, flag, errout)
+       const char *uf;
+       const char *mf;
+       const char *sf;
+       int flag;
+       char *errout;
 {
 {
+       register kvm_t *kd = (kvm_t *)malloc(sizeof(*kd));
 
 
-       if (!kvmprocbase && kvm_getprocs(0, 0) == -1)
-               return (NULL);
-       if (kvmprocptr >= (kvmprocbase + kvmnprocs)) {
-               seterr("end of proc list");
-               return (NULL);
+       if (kd == 0) {
+               strcpy(errout, "out of memory");
+               return (0);
        }
        }
-       return((struct proc *)(kvmprocptr++));
+       kd->program = 0;
+       return _kvm_open(kd, uf, mf, sf, flag, errout);
 }
 
 }
 
-struct eproc *
-kvm_geteproc(p)
-       struct proc *p;
+kvm_t *
+kvm_open(uf, mf, sf, flag, program)
+       const char *uf;
+       const char *mf;
+       const char *sf;
+       int flag;
+       const char *program;
 {
 {
-       return ((struct eproc *)(((char *)p) + sizeof (struct proc)));
+       register kvm_t *kd = (kvm_t *)malloc(sizeof(*kd));
+
+       if (kd == 0 && program != 0) {
+               fprintf(stderr, "%s: out of memory", program);
+               return (0);
+       }
+       kd->program = program;
+       return _kvm_open(kd, uf, mf, sf, flag, (char *)0);
 }
 
 }
 
-kvm_setproc()
+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)
+               dbm_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);
 
 
-       kvmprocptr = kvmprocbase;
+       return (0);
 }
 
 }
 
-kvm_freeprocs()
+/*
+ * 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;
 {
 {
+       char *cp;
+       datum rec;
+       int dbversionlen;
+       struct nlist nitem;
+       char dbversion[_POSIX2_LINE_MAX];
+       char kversion[_POSIX2_LINE_MAX];
+       char dbname[MAXPATHLEN];
 
 
-       if (kvmprocbase) {
-               free(kvmprocbase);
-               kvmprocbase = NULL;
-       }
-}
+       if ((cp = rindex(uf, '/')) != 0)
+               uf = cp + 1;
 
 
-struct user *
-kvm_getu(p)
-       struct proc *p;
-{
-       struct pte *pteaddr, apte;
-       struct pte arguutl[HIGHPAGES+(CLSIZE*2)];
-       register int i;
-       int ncl;
-
-       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 & SLOAD) == 0) {
-               if (swap < 0) {
-                       seterr("no swap");
-                       return (NULL);
-               }
-               (void) lseek(swap, (long)dtob(p->p_swaddr), 0);
-               if (read(swap, (char *)&user.user, sizeof (struct user)) != 
-                   sizeof (struct user)) {
-                       seterr("can't read u for pid %d from %s\n",
-                           p->p_pid, swapf);
-                       return (NULL);
-               }
-               pcbpf = 0;
-               argaddr0 = 0;
-               argaddr1 = 0;
-               return (&user.user);
-       }
-       pteaddr = &Usrptmap[btokmx(p->p_p0br) + p->p_szpt - 1];
-       klseek(kmem, (long)pteaddr, 0);
-       if (read(kmem, (char *)&apte, sizeof(apte)) != sizeof(apte)) {
-               seterr("can't read indir pte to get u for pid %d from %s",
-                   p->p_pid, kmemf);
-               return (NULL);
-       }
-       lseek(mem, (long)ctob(pftoc(apte.pg_pfnum+1)) - sizeof(arguutl), 0);
-       if (read(mem, (char *)arguutl, sizeof(arguutl)) != sizeof(arguutl)) {
-               seterr("can't read page table for u of pid %d from %s",
-                   p->p_pid, memf);
-               return (NULL);
-       }
-       if (arguutl[0].pg_fod == 0 && arguutl[0].pg_pfnum)
-               argaddr0 = ctob(pftoc(arguutl[0].pg_pfnum));
-       else
-               argaddr0 = 0;
-       if (arguutl[CLSIZE*1].pg_fod == 0 && arguutl[CLSIZE*1].pg_pfnum)
-               argaddr1 = ctob(pftoc(arguutl[CLSIZE*1].pg_pfnum));
-       else
-               argaddr1 = 0;
-       pcbpf = arguutl[CLSIZE*2].pg_pfnum;
-       ncl = (sizeof (struct user) + CLBYTES - 1) / CLBYTES;
-       while (--ncl >= 0) {
-               i = ncl * CLSIZE;
-               lseek(mem,
-                     (long)ctob(pftoc(arguutl[(CLSIZE*2)+i].pg_pfnum)), 0);
-               if (read(mem, user.upages[i], CLBYTES) != CLBYTES) {
-                       seterr("can't read page %d of u of pid %d from %s",
-                           arguutl[(CLSIZE*2)+i].pg_pfnum, p->p_pid, memf);
-                       return(NULL);
-               }
-       }
-       return (&user.user);
-}
+       sprintf(dbname, "%skvm_%s", _PATH_VARRUN, uf);
+       kd->db = dbm_open(dbname, O_RDONLY, 0);
+       if (kd->db == 0)
+               return (-1);
+       /*
+        * read version out of database
+        */
+       rec.dptr = VRS_KEY;
+       rec.dsize = sizeof(VRS_KEY) - 1;
+       rec = dbm_fetch(kd->db, rec);
+       if (rec.dptr == 0 || rec.dsize > sizeof(dbversion))
+               goto close;
+
+       bcopy(rec.dptr, dbversion, rec.dsize);
+       dbversionlen = rec.dsize;
+       /*
+        * Read version string from kernel memory.
+        * Since we are dealing with a live kernel, we can call kvm_read()
+        * at this point.
+        */
+       rec.dptr = VRS_SYM;
+       rec.dsize = sizeof(VRS_SYM) - 1;
+       rec = dbm_fetch(kd->db, rec);
+       if (rec.dptr == 0 || rec.dsize != sizeof(struct nlist))
+               goto close;
+       bcopy((char *)rec.dptr, (char *)&nitem, sizeof(nitem));
+       if (kvm_read(kd, (u_long)nitem.n_value, kversion, dbversionlen) != 
+           dbversionlen)
+               goto close;
+       /*
+        * If they match, we win - otherwise clear out kd->db so
+        * we revert to slow nlist().
+        */
+       if (bcmp(dbversion, kversion, dbversionlen) == 0)
+               return (0);
+close:
+       dbm_close(kd->db);
+       kd->db = 0;
 
 
-char *
-kvm_getargs(p, up)
-       struct proc *p;
-       struct user *up;
-{
-       char cmdbuf[CLBYTES*2];
-       union {
-               char    argc[CLBYTES*2];
-               int     argi[CLBYTES*2/sizeof (int)];
-       } argspac;
-       register char *cp;
-       register int *ip;
-       char c;
-       int nbad;
-       struct dblock db;
-       char *file;
-
-       if (up == NULL || p->p_pid == 0 || p->p_pid == 2)
-               goto retucomm;
-       if ((p->p_flag & SLOAD) == 0 || argaddr1 == 0) {
-               if (swap < 0 || p->p_ssize == 0)
-                       goto retucomm;
-               vstodb(0, CLSIZE, &up->u_smap, &db, 1);
-               (void) lseek(swap, (long)dtob(db.db_base), 0);
-               if (read(swap, (char *)&argspac.argc[CLBYTES], CLBYTES)
-                       != CLBYTES)
-                       goto bad;
-               vstodb(1, CLSIZE, &up->u_smap, &db, 1);
-               (void) lseek(swap, (long)dtob(db.db_base), 0);
-               if (read(swap, (char *)&argspac.argc[0], CLBYTES) != CLBYTES)
-                       goto bad;
-               file = swapf;
-       } else {
-               if (argaddr0) {
-                       lseek(mem, (long)argaddr0, 0);
-                       if (read(mem, (char *)&argspac, CLBYTES) != CLBYTES)
-                               goto bad;
-               } else
-                       bzero(&argspac, CLBYTES);
-               lseek(mem, (long)argaddr1, 0);
-               if (read(mem, &argspac.argc[CLBYTES], CLBYTES) != CLBYTES)
-                       goto bad;
-               file = memf;
-       }
-       ip = &argspac.argi[CLBYTES*2/sizeof (int)];
-       ip -= 2;                /* last arg word and .long 0 */
-       while (*--ip) {
-               if (ip == argspac.argi)
-                       goto retucomm;
-       }
-       *(char *)ip = ' ';
-       ip++;
-       nbad = 0;
-       for (cp = (char *)ip; cp < &argspac.argc[CLBYTES*2]; cp++) {
-               c = *cp & 0177;
-               if (c == 0)
-                       *cp = ' ';
-               else if (c < ' ' || c > 0176) {
-                       if (++nbad >= 5*(0+1)) {        /* eflg -> 0 XXX */
-                               *cp++ = ' ';
-                               break;
-                       }
-                       *cp = '?';
-               } else if (0 == 0 && c == '=') {        /* eflg -> 0 XXX */
-                       while (*--cp != ' ')
-                               if (cp <= (char *)ip)
-                                       break;
-                       break;
-               }
-       }
-       *cp = 0;
-       while (*--cp == ' ')
-               *cp = 0;
-       cp = (char *)ip;
-       (void) strncpy(cmdbuf, cp, &argspac.argc[CLBYTES*2] - cp);
-       if (cp[0] == '-' || cp[0] == '?' || cp[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 from %s\n",
-           p->p_pid, file);
-retucomm:
-       (void) strcpy(cmdbuf, " (");
-       (void) strncat(cmdbuf, p->p_comm, sizeof (p->p_comm));
-       (void) strcat(cmdbuf, ")");
-       return (cmdbuf);
+       return (-1);
 }
 
 }
 
-
-static
-getkvars()
+int
+kvm_nlist(kd, nl)
+       kvm_t *kd;
+       struct nlist *nl;
 {
 {
+       register struct nlist *p;
+       register int nvalid;
 
 
-       if (kvm_nlist(nl) == -1)
-               return (-1);
-       if (deadkernel) {
-               /* We must do the sys map first because klseek uses it */
-               long    addr;
-
-               Syssize = nl[X_SYSSIZE].n_value;
-               Sysmap = (struct pte *)
-                       calloc((unsigned) Syssize, sizeof (struct pte));
-               if (Sysmap == NULL) {
-                       seterr("out of space for Sysmap");
-                       return (-1);
-               }
-               addr = (long) nl[X_SYSMAP].n_value;
-               addr &= ~KERNBASE;
-               (void) lseek(kmem, addr, 0);
-               if (read(kmem, (char *) Sysmap, Syssize * sizeof (struct pte))
-                   != Syssize * sizeof (struct pte)) {
-                       seterr("can't read Sysmap");
-                       return (-1);
-               }
-#if defined(hp300)
-               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);
-#endif
-       }
-       usrpt = (struct pte *)nl[X_USRPT].n_value;
-       Usrptmap = (struct pte *)nl[X_USRPTMAP].n_value;
-       if (kvm_read((long)nl[X_NSWAP].n_value, &nswap, sizeof (long)) !=
-           sizeof (long)) {
-               seterr("can't read nswap");
-               return (-1);
-       }
-       if (kvm_read((long)nl[X_DMMIN].n_value, &dmmin, sizeof (long)) !=
-           sizeof (long)) {
-               seterr("can't read dmmin");
-               return (-1);
-       }
-       if (kvm_read((long)nl[X_DMMAX].n_value, &dmmax, sizeof (long)) !=
-           sizeof (long)) {
-               seterr("can't read dmmax");
-               return (-1);
-       }
-       return (0);
-}
+       /*
+        * If we can't use the data base, revert to the 
+        * slow library call.
+        */
+       if (kd->db == 0)
+               return (__fdnlist(kd->nlfd, nl));
 
 
-kvm_read(loc, buf, len)
-       unsigned long loc;
-       char *buf;
-{
-       if (kvmfilesopen == 0 && kvm_openfiles(NULL, NULL, NULL) == -1)
-               return (-1);
-       if (iskva(loc)) {
-               klseek(kmem, loc, 0);
-               if (read(kmem, buf, len) != len) {
-                       seterr("error reading kmem at %x\n", loc);
-                       return (-1);
-               }
-       } else {
-               lseek(mem, loc, 0);
-               if (read(mem, buf, len) != len) {
-                       seterr("error reading mem at %x\n", loc);
+       /*
+        * We can use the kvm data base.  Go through each nlist entry
+        * and look it up with a dbm query.
+        */
+       nvalid = 0;
+       for (p = nl; p->n_name && p->n_name[0]; ++p) {
+               register int len;
+               datum rec;
+
+               if ((len = strlen(p->n_name)) > 4096) {
+                       /* sanity */
+                       _kvm_err(kd, kd->program, "symbol too large");
                        return (-1);
                }
                        return (-1);
                }
+               rec.dptr = p->n_name;
+               rec.dsize = len;
+               rec = dbm_fetch(kd->db, rec);
+               if (rec.dptr == 0 || rec.dsize != sizeof(struct nlist))
+                       continue;
+               ++nvalid;
+               /*
+                * Avoid alignment issues.
+                */
+               bcopy((char *)&((struct nlist *)rec.dptr)->n_type,
+                     (char *)&p->n_type, 
+                     sizeof(p->n_type));
+               bcopy((char *)&((struct nlist *)rec.dptr)->n_value,
+                     (char *)&p->n_value, 
+                     sizeof(p->n_value));
        }
        }
-       return (len);
+       /*
+        * Return the number of entries that weren't found.
+        */
+       return ((p - nl) - nvalid);
 }
 
 }
 
-static
-klseek(fd, loc, off)
-       int fd;
-       off_t loc;
-       int off;
+ssize_t
+kvm_write(kd, kva, buf, len)
+       kvm_t *kd;
+       register u_long kva;
+       register const char *buf;
+       register size_t len;
 {
 {
-
-       if (deadkernel) {
-               off_t vtophys();
-
-               if ((loc = vtophys(loc)) == -1)
-                       return;
-       }
-       (void) lseek(fd, (off_t)loc, off);
+       _kvm_err(kd, kd->program, "kvm_write not implemented");
+       return (ssize_t)(0);
 }
 
 }
 
-/*
- * Given a base/size pair in virtual swap area,
- * return a physical base/size pair which is the
- * (largest) initial, physically contiguous block.
- */
-static
-vstodb(vsbase, vssize, dmp, dbp, rev)
-       register int vsbase;
-       int vssize;
-       struct dmap *dmp;
-       register struct dblock *dbp;
+ssize_t
+kvm_read(kd, kva, buf, len)
+       kvm_t *kd;
+       register u_long kva;
+       register char *buf;
+       register size_t len;
 {
 {
-       register int blk = dmmin;
-       register swblk_t *ip = dmp->dm_map;
-
-       vsbase = ctod(vsbase);
-       vssize = ctod(vssize);
-       if (vsbase < 0 || vsbase + vssize > dmp->dm_size)
-               /*panic("vstodb")*/;
-       while (vsbase >= blk) {
-               vsbase -= blk;
-               if (blk < dmmax)
-                       blk *= 2;
-               ip++;
-       }
-       if (*ip <= 0 || *ip + blk > nswap)
-               /*panic("vstodb")*/;
-       dbp->db_size = MIN(vssize, blk - vsbase);
-       dbp->db_base = *ip + (rev ? blk - (vsbase + dbp->db_size) : vsbase);
-}
+       register int cc;
+       register char *cp;
 
 
-static off_t
-vtophys(loc)
-       long loc;
-{
-       int p;
-       off_t newloc;
-       register struct pte *pte;
-
-       newloc = loc & ~KERNBASE;
-       p = btop(newloc);
-#if defined(vax) || defined(tahoe)
-       if ((loc & KERNBASE) == 0) {
-               seterr("vtophys: translating non-kernel address");
-               return((off_t) -1);
-       }
-#endif
-       if (p >= Syssize) {
-               seterr("vtophys: page out of bound (%d>=%d)", p, Syssize);
-               return((off_t) -1);
-       }
-       pte = &Sysmap[p];
-       if (pte->pg_v == 0 && (pte->pg_fod || pte->pg_pfnum == 0)) {
-               seterr("vtophys: page not valid");
-               return((off_t) -1);
-       }
-#if defined(hp300)
-       if (pte->pg_pfnum < lowram) {
-               seterr("vtophys: non-RAM page (%d<%d)", pte->pg_pfnum, lowram);
-               return((off_t) -1);
+       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);
+               }
+               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 (ssize_t)(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;
+                       }
+                       cc = read(kd->pmfd, cp, cc);
+                       if (cc < 0) {
+                               _kvm_syserr(kd, kd->program, "kvm_read");
+                               break;
+                       }
+                       cp += cc;
+                       kva += cc;
+                       len -= cc;
+               }
+               return (cp - buf);
        }
        }
-#endif
-       loc = (long) (ptob(pftoc(pte->pg_pfnum)) + (loc & PGOFSET));
-       return(loc);
-}
-
-#include <varargs.h>
-static char errbuf[_POSIX2_LINE_MAX];
-
-static
-seterr(va_alist)
-       va_dcl
-{
-       char *fmt;
-       va_list ap;
-
-       va_start(ap);
-       fmt = va_arg(ap, char *);
-       (void) vsprintf(errbuf, fmt, ap);
-       va_end(ap);
-}
-
-static
-setsyserr(va_alist)
-       va_dcl
-{
-       char *fmt, *cp;
-       va_list ap;
-       extern int errno;
-
-       va_start(ap);
-       fmt = va_arg(ap, char *);
-       (void) vsprintf(errbuf, fmt, ap);
-       for (cp=errbuf; *cp; cp++)
-               ;
-       sprintf(cp, ": %s", strerror(errno));
-       va_end(ap);
-}
-
-char *
-kvm_geterr()
-{
-       return (errbuf);
+       /* NOTREACHED */
 }
 }