BBN-V6/progs/snap.c

Compare this file to the similar file:
Show the results in this format:

/*
 * snap -- take a snapshot of a running process
 *
 * Generates a core image file, suitable for use by adb, of a specified
 * process image. Reads from /dev/mem or swap device.
 *
 * snap [-k] [-u] pid [-c core] [-s system] [-m memory] [-swap swapdev]
 * snap [-k] [-u] -a u_addr [-c core] [-m memory]
 *
 *   pid: the process-id. Used with system and memory to get address and
 *        residence (memory or swap device) of process image.
 *   u_addr: address in memory of process image.
 *
 * Either u_addr or pid must be specified. If u_addr is specified, any swap
 * device specified is ignored.
 *
 *   core: output core-image file
 *       The default is "core", unless -k specified,
 *       in which case it is "kernel".
 *   system: system namefile to use (default /unix)
 *   memory: file to copy proc entry and in-core core image from (def. /dev/mem)
 *   swapdev: file to copy swapped-out core image from (def. current swap dev)
 *   -k: generate kernel snapshot with U block of specified process. Like
 *       copying /dev/kmem except that the current process (at 0140000) is the
 *       one specified.
 *   -u: Copy out only the U block instead of the whole process image.
 *
 * Algorithm:
 *       Find process image:
 *          If pid given, look up _proc; get offset and residence from "memory"
 *          If u_addr given, it is the offset
 *       If in core
 *          copy from /dev/mem
 *       else
 *          find out swap device
 *          copy from there
 *
 * Note: requires phototypesetter compiler
 * BUGS
 *    -a without -k or -u copies 65k bytes. ought to copy (u_dsize+u_ssize+16)*64.
 *
 * Originally coded by BBN(dan) Jan 23 1978
 * Modified by BBN(dan) April 24 1979: now checks process id to make sure it is
 *    within range, checks to make sure that process is alive, and checks status
 *    of process when copy done to make sure it has not died or moved. If it has
 *    moved, snap will now retry.
 */

#include "../h/param.h"
#include "../h/proc.h"
#include "/usr/sys/h/statbuf.h"

char *ME;

main(argc, argv)
   int argc;
   char *argv[];
   {
   char *sysfile;
   char *corefile;
   char *swapfile;
   char *outfile;
   int i;
   int rgid;
   int pid;
   long offset;
   long size;
   int kflag, uflag;
   int corefd;
   int outfd;
   int procfd;
   struct proc *procp;
   extern char *atoiv();
   extern char *aotol();

   ME = argv[0];

   sysfile = "/unix";
   corefile = "/dev/mem";
   swapfile = 0;           /* Will fill in with current swapdev if need */
   outfile = 0;            /* Uninitialized */

   if (argc == 1)
      {
      fdprintf(2, "Usage:\n");
      fdprintf(2,
"%s [-k] [-u] pid [-c core] [-s sys] [-m mem] [-swap swapdev] [-a u_addr]\n",
      ME);
      exit(-1);
      }

/* Process arguments */

   pid = -1;
   kflag = 0;
   uflag = 0;
   offset = -1;
   for (i = 1; i < argc; i++)
      {
      if (seq(argv[i], "-k"))
         kflag++;
      else if (seq(argv[i], "-u"))
         uflag++;
      else if (seq(argv[i], "-s"))
         sysfile = argv[++i];
      else if (seq(argv[i], "-m"))
         corefile = argv[++i];
      else if (seq(argv[i], "-swap"))
         swapfile = argv[++i];
      else if (seq(argv[i], "-c"))
         outfile = argv[++i];
      else if (seq(argv[i], "-a"))
         {
         if (*aotol(argv[++i], &offset) || offset < 0)
            {
            fdprintf(2, "%s: Bad u block address \"%s\".\n", ME, argv[i]);
            exit(-1);
            }
         }
      else if (argv[i][0] == '-')
         {
         fdprintf(2, "%s: Unknown control argument \"%s\".\n", ME, argv[i]);
         exit(-1);
         }
      else if (pid == -1)  /* Still need a pid */
         {
         if (*atoiv(argv[i], &pid))
            {
            fdprintf(2, "%s: Bad process id \"%s\".\n", ME, argv[i]);
            exit(-1);
            }
         }
      else
         {
         fdprintf(2, "%s: Extraneous argument \"%s\".\n", ME, argv[i]);
         exit(-1);
         }
      if (i == argc)
         {
         fdprintf(2, "%s: %s requires an argument.\n", ME, argv[i-1]);
         exit(1);
         }
      }

   if (outfile == 0)
      if (kflag)
         outfile = "kernel";
      else
         outfile = "core";
   if (pid == -1 && offset == -1)
      {
      fdprintf(2, "%s: Either a process id or -a must be given.\n", ME);
      exit(-1);
      }
   if (pid != -1 && offset != -1)
      {
      fdprintf(2, "%s: Cannot specify both pid and -a.\n", ME);
      exit(-1);
      }

/* Open corefile, set corefd */

   if ((corefd = open(corefile, 0)) == -1)
      {
      fdprintf(2, "%s: %s. \"%s\"\n", ME, errmsg(0), corefile);
      exit(1);
      }

   if (pid != -1)
      {
      int swapfd;
      int swapdev;

      if (pid >= NPROC)
         {
         fdprintf(2, "%s: No such process ID: %d.\n", ME, pid);
         exit(-1);
         }
      getvals(sysfile, &procp, &swapdev);

RETRY:
      seekread(pid, corefd, procp, corefile, &proc[0]);

      if (proc[0].p_stat == 0)
         {
         fdprintf(2, "%s: No such process: %d.\n", ME, pid);
         exit(1);
         }

      if ((proc[0].p_flag & SLOAD) == 0)
         {
         printf("Reading from swap device...\n");
         if (swapfile == 0)
            if (swapdev != 0)
               {
               if (seek(corefd, swapdev, 0) == -1)
                  {
                  fdprintf(2, "%s: %s. Seek on \"%s\" failed.\n", ME,
                               errmsg(0), corefile);
                  exit(1);
                  }
               if (read(corefd, &swapdev, 2) != 2)
                  {
                  fdprintf(2, "%s: Error reading \"%s\".\n", ME, corefile);
                  exit(1);
                  }
               swapfile = findswap(swapdev, "/dev/hps10");
               }
            else
               {
               fdprintf(2, "%s: Symbol not found. \"_swapdev\" in \"%s\"\n",
                           ME, sysfile);
               exit(1);
               }
         if ((swapfd = open(swapfile, 0)) == -1)
            {
            fdprintf(2, "%s: %s. \"%s\"\n", ME, errmsg(0), swapfile);
            exit(1);
            }
         offset = (long)proc[0].p_addr * 01000;
         size = (long)proc[0].p_size * 01000;
         procfd = swapfd;
         }
      else
         {
         offset = (long)proc[0].p_addr * 0100;
         size = (long)proc[0].p_size * 0100;
         procfd = corefd;
         }
      }
   else
      {
      size = 0202000L;     /* For -a, just copy max possible */
      procfd = corefd;
      }

/* Finally create output file... */

   rgid = getgid() & 0377;
   setgid(rgid | (rgid<<8));
   if ((outfd = creat(outfile, 0644)) == -1)
      {
      fdprintf(2, "%s: %s. Cannot create \"%s\".\n", ME, errmsg(0), outfile);
      exit(1);
      }

/* And copy */

   if (kflag)
      {
      copy(corefd, outfd, 0L, 0140000L);
      copy(procfd, outfd, offset, 02000L);
      }
   else
      copy(procfd, outfd, offset, uflag? 02000:size);

/* Check if it moved */

   if (pid != -1)
      {
      struct proc proc2;

      seekread(pid, corefd, procp, corefile, &proc2);
      if (proc2.p_stat == 0)
         {
         printf(
"Process died while it was being copied. File may be inaccurate.\n"
            );
         exit(0);
         }
      if ( ((proc2.p_flag & SLOAD) != (proc[0].p_flag & SLOAD))
            || (proc2.p_addr != proc[0].p_addr)
            || (proc2.p_size != proc[0].p_size) )
         {
         printf("Process moved while it was being copied. Retrying...\n");
         goto RETRY;
         }
      }

   exit(0);
   }

/* -------------------------- S E E K R E A D ----------------------- */
/*
 * seekread() seeks to the process table entry and reads it into proc.
 */
seekread(pid, corefd, procp, corefile, proc)
int  pid;           /* Process ID */
int  corefd;        /* File descriptor of memory file */
struct proc *procp; /* Position of _proc in memory file */
char *corefile;     /* Name of memory file (for diagnostics) */
struct proc proc[]; /* Process table slot (output) */
{
   if (seek(corefd, procp + pid, 0) == -1)   /* Seek to _proc */
      {
      fdprintf(2, "%s: %s. Cannot seek to process slot in \"%s\".\n",
                   ME, errmsg(0), corefile);
      exit(1);
      }

   if (read(corefd, &proc[0], sizeof(proc[0])) != sizeof(proc[0]))
      {
      fdprintf(2, "%s: Error reading \"%s\".\n", ME, corefile);
      exit(1);
      }
}

/* -------------------------- G E T V A L S ------------------------- */
/*
 * getvals(sysfile, &procp, &swapdev) examines the namelist of sysfile
 * in order to look up "_proc" and "swapdev". Their values are assigned to
 * "procp" and "swapdev" respectively. If "_proc" is not found, it is a fatal
 * error; "swapdev" may not be needed, however.
 */
struct
   {
   char    name[8];
   int     type;
   int     value;
   } nl[ ]
   {
   "_proc\0\0", 0, 0,
   "_swapdev", 0, 0,
   0, 0, 0
   };

getvals(sysfile, procpp, swapdevp)
   char *sysfile;
   int *procpp;
   int *swapdevp;
   {

/* Try to open sysfile to see if it is accessible */

   if (open(sysfile, 0) == -1)
      {
      fdprintf(2, "%s: %s. \"%s\"\n", ME, errmsg(0), sysfile);
      exit(1);
      }

/* It is. Set procp and swapdev. */

   nlist(sysfile, nl);

   if (nl[0].type)
      *procpp = nl[0].value;
   else
      {
      fdprintf(2, "%s: Symbol not found. \"%.8s\" in \"%s\"\n",
                  ME, nl[0].name, sysfile);
      exit(1);
      }

   if (nl[1].type)
      *swapdevp = nl[1].value;
   else
      *swapdevp = 0;
   }

/* -------------------------- C O P Y --------------------------------------- */
/*
 * copy(infd, outfd, offset, length) seeks to byte offset in infd and copies
 * length bytes to outfd.
 */
copy(infd, outfd, offset, len)
   int infd, outfd;
   long offset;
   long len;
   {
   char buffer[512];
   int nreq;
   int nact;

   if (lseek(infd, offset, 0) == -1)
      {
      fdprintf(2, "%s: %s. Seek error.\n", ME, errmsg(0));
      exit(1);
      }

   while(len)
      {
      nreq = sizeof(buffer);
      if (len < nreq)
         nreq = len;
      nact = read(infd, buffer, nreq);
      if (nact == 0)
         {
         fdprintf(2, "%s: Premature EOF.\n", ME);
         exit(1);
         }
      if (nact == -1)
         {
         fdprintf(2, "%s: %s. Error while reading.\n", ME, errmsg(0));
         exit(1);
         }
      nreq = nact;
      nact = write(outfd, buffer, nreq);
      if (nact < 0)
         {
         fdprintf(2, "%s: %s. Error writing output file.\n", ME, errmsg(0));
         exit(1);
         }
      if (nact == 0)
         {
         fdprintf(2, "%s: EOF while writing output file.\n", ME, errmsg(0));
         exit(1);
         }
      if (nact < nreq)
         {
         fdprintf(2, "%s: Write error on output file.\n", ME);
         exit(1);
         }
      len =- nact;
      }
   }

/* -------------------------- F I N D S W A P ------------------------------ */
/*
 * findswap(swapdev, guess) searches through /dev for the special file with
 * major and minor device numbers in swapdev. It first stats "guess" to see if
 * it matches. This saves time. It returns a pointer to the name of the swap
 * device.
 */
char *
findswap(swapdev, guess)
   int swapdev;
   char *guess;
   {
   register struct
      {
      int dir_ino;
      char dir_n[14];
      } *p;
   register i;
   int f;
   char fname[20];
   char dbuf[512];
   struct statbuf sbuf;

   if (stat(guess, &sbuf) == 0 && (sbuf.s_flags&SFMT) == SFBLK
   && sbuf.s_addr[0] == swapdev)
      return(guess);

   f = open("/dev", 0);
   if (f < 0)
      {
      fdprintf(2, "%s: %s. Cannot open \"/dev\".\n", ME, errmsg(0));
      exit(1);
      }

   while ((i = read(f, dbuf, 512)) > 0)
      {
      while(i < 512)
         dbuf[i++] = 0;
      for (p = dbuf; p < dbuf + 512; p++)
         {
         if (p->dir_ino == 0) /* Unused entry */
            continue;
         scopy(p->dir_n, scopy("/dev/", fname, -1), -1);
         if (stat(fname, &sbuf) < 0)      /* Cannot stat */
            continue;
         if ((sbuf.s_flags&SFMT) != SFBLK)   /* Block special file? */
            continue;
         if (sbuf.s_addr[0] == swapdev)      /* Of the right value? */
            return(fname);
         }
      }
   close(f);
   fdprintf(2, "%s: Cannot find swap device.\n", ME);
   exit(1);
   }

/* -------------------------- S E Q --------------------------------- */
/*
 * int = seq(str1, str2)
 * Compares str1 and str2; returns 1 if equal, 0 if not
 */
int
seq(s1, s2)
   char *s1, *s2;
   {
   register char *p1, *p2;

   p1 = s1;
   p2 = s2;
   while (*p1 == *p2++)
      if (*p1++ == 0)
         return(1);
   return(0);
   }

/* -------------------------- A O T O L ------------------------------ */
/*
 * aotol -- convert octal string to long number
 */
char *
aotol(str, lng)
   char *str;
   long *lng;
   {
   long temp;
   register char c;
   register char *s;

   s = str;
   temp = 0;
   for(;;)
      {
      c = *s++ - '0';
      if (c >= 0 && c <= 7)
         temp = (temp<<3) + c;
      else
         {
         *lng = temp;
         return(s-1);
         }
      }
   }

/* -------------------------- S C O P Y ----------------------------- */
/*
 * str = scopy(str1, str2, &str2[lastbyte])
 * Copies str1 into str2, truncating if necessary. Returns a pointer to the
 * null. The simplest way to use this function is as follows:
 *    scopy(src, dest, -1);
 * which copies without regard for the length of dest.
 */
char *
scopy(s1, s2, s3)
   char *s1;
   char *s2;
   char *s3;
   {
   register char *p1;
   register char *p2;
   register char *p3;

   p1 = s1;
   p2 = s2;
   p3 = s3;

   while(p2 < p3 && *p1)
      *p2++ = *p1++;

   /* Either p2 = p3 or p1 points at null byte. Either way, terminate. */

   *p2 = 0;
   return(p2);
   }