198944Sobrien/* Kernel Object Display facility for Cisco
298944Sobrien   Copyright 1999, 2000 Free Software Foundation, Inc.
398944Sobrien
498944Sobrien   Written by Tom Tromey <tromey@cygnus.com>.
598944Sobrien
698944SobrienThis file is part of GDB.
798944Sobrien
898944SobrienThis program is free software; you can redistribute it and/or modify
998944Sobrienit under the terms of the GNU General Public License as published by
1098944Sobrienthe Free Software Foundation; either version 2 of the License, or
1198944Sobrien(at your option) any later version.
1298944Sobrien
1398944SobrienThis program is distributed in the hope that it will be useful,
1498944Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of
1598944SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1698944SobrienGNU General Public License for more details.
1798944Sobrien
1898944SobrienYou should have received a copy of the GNU General Public License
1998944Sobrienalong with this program; if not, write to the Free Software
2098944SobrienFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
2198944Sobrien
2298944Sobrien#include "defs.h"
2398944Sobrien#include "gdb_string.h"
2498944Sobrien#include "kod.h"
2598944Sobrien
2698944Sobrien#ifdef HAVE_STDLIB_H
2798944Sobrien#include <stdlib.h>
2898944Sobrien#endif
2998944Sobrien
3098944Sobrien/* Define this to turn off communication with target.  */
3198944Sobrien/* #define FAKE_PACKET */
3298944Sobrien
3398944Sobrien/* Size of buffer used for remote communication.  */
3498944Sobrien#define PBUFSIZ 400
3598944Sobrien
3698944Sobrien/* Pointers to gdb callbacks.  */
3798944Sobrienstatic void (*gdb_kod_display) (char *);
3898944Sobrienstatic void (*gdb_kod_query) (char *, char *, int *);
3998944Sobrien
4098944Sobrien
4198944Sobrien
4298944Sobrien/* Initialize and return library name and version.
4398944Sobrien   The gdb side of KOD, kod.c, passes us two functions: one for
4498944Sobrien   displaying output (presumably to the user) and the other for
4598944Sobrien   querying the target.  */
4698944Sobrienchar *
4798944Sobriencisco_kod_open (kod_display_callback_ftype *display_func,
4898944Sobrien		kod_query_callback_ftype *query_func)
4998944Sobrien{
5098944Sobrien  char buffer[PBUFSIZ];
5198944Sobrien  int bufsiz = PBUFSIZ;
5298944Sobrien  int i, count;
5398944Sobrien
5498944Sobrien  gdb_kod_display = display_func;
5598944Sobrien  gdb_kod_query = query_func;
5698944Sobrien
5798944Sobrien  /* Get the OS info, and check the version field.  This is the stub
5898944Sobrien     version, which we use to see whether we will understand what
5998944Sobrien     comes back.  This is lame, but the `qKoL' request doesn't
6098944Sobrien     actually provide enough configurability.
6198944Sobrien
6298944Sobrien     Right now the only defined version number is `0.0.0'.
6398944Sobrien     This stub supports qKoI and the `a' (any) object requests qKaL
6498944Sobrien     and qKaI.  Each `a' object is returned as a 4-byte integer ID.
6598944Sobrien     An info request on an object returns a pair of 4-byte integers;
6698944Sobrien     the first is the object pointer and the second is the thread ID.  */
6798944Sobrien
6898944Sobrien#ifndef FAKE_PACKET
6998944Sobrien  (*gdb_kod_query) ("oI;", buffer, &bufsiz);
7098944Sobrien#else
7198944Sobrien  strcpy (buffer, "Cisco IOS/Classic/13.4 0.0.0");
7298944Sobrien#endif
7398944Sobrien
7498944Sobrien  count = 2;
7598944Sobrien  for (i = 0; count && buffer[i] != '\0'; ++i)
7698944Sobrien    {
7798944Sobrien      if (buffer[i] == ' ')
7898944Sobrien	--count;
7998944Sobrien    }
8098944Sobrien
8198944Sobrien  if (buffer[i] == '\0')
8298944Sobrien    error ("Remote returned malformed packet\n");
8398944Sobrien  if (strcmp (&buffer[i], "0.0.0"))
8498944Sobrien    error ("Remote returned unknown stub version: %s\n", &buffer[i]);
8598944Sobrien
8698944Sobrien  /* Return name, version, and description.  I hope we have enough
8798944Sobrien     space.  */
8898944Sobrien  return (xstrdup ("gdbkodcisco v0.0.0 - Cisco Kernel Object Display"));
8998944Sobrien}
9098944Sobrien
9198944Sobrien/* Close the connection.  */
9298944Sobrienvoid
9398944Sobriencisco_kod_close (void)
9498944Sobrien{
9598944Sobrien}
9698944Sobrien
9798944Sobrien/* Print a "bad packet" message.  */
9898944Sobrienstatic void
9998944Sobrienbad_packet (void)
10098944Sobrien{
10198944Sobrien  (*gdb_kod_display) ("Remote target returned malformed packet.\n");
10298944Sobrien}
10398944Sobrien
10498944Sobrien/* Print information about currently known kernel objects.
10598944Sobrien   We currently ignore the argument.  There is only one mode of
10698944Sobrien   querying the Cisco kernel: we ask for a dump of everything, and
10798944Sobrien   it returns it.  */
10898944Sobrienvoid
10998944Sobriencisco_kod_request (char *arg, int from_tty)
11098944Sobrien{
11198944Sobrien  char buffer[PBUFSIZ], command[PBUFSIZ];
11298944Sobrien  int done = 0, i;
11398944Sobrien  int fail = 0;
11498944Sobrien
11598944Sobrien  char **sync_ids = NULL;
11698944Sobrien  int sync_len = 0;
11798944Sobrien  int sync_next = 0;
11898944Sobrien  char *prev_id = NULL;
11998944Sobrien
12098944Sobrien  if (! arg || strcmp (arg, "any"))
12198944Sobrien    {
12298944Sobrien      /* "Top-level" command.  This is really silly, but it also seems
12398944Sobrien	 to be how KOD is defined.  */
12498944Sobrien      /* Even sillier is the fact that this first line must start
12598944Sobrien	 with the word "List".  See kod.tcl.  */
12698944Sobrien      (*gdb_kod_display) ("List of Cisco Kernel Objects\n");
12798944Sobrien      (*gdb_kod_display) ("Object\tDescription\n");
12898944Sobrien      (*gdb_kod_display) ("any\tAny and all objects\n");
12998944Sobrien      return;
13098944Sobrien    }
13198944Sobrien
13298944Sobrien  while (! done)
13398944Sobrien    {
13498944Sobrien      int off = 0;		/* Where we are in the string.  */
13598944Sobrien      long count;		/* Number of objects in this packet.  */
13698944Sobrien      int bufsiz = PBUFSIZ;
13798944Sobrien      char *s_end;
13898944Sobrien
13998944Sobrien      strcpy (command, "aL");
14098944Sobrien      if (prev_id)
14198944Sobrien	{
14298944Sobrien	  strcat (command, ",");
14398944Sobrien	  strcat (command, prev_id);
14498944Sobrien	}
14598944Sobrien      strcat (command, ";");
14698944Sobrien
14798944Sobrien#ifndef FAKE_PACKET
14898944Sobrien      /* We talk to the target by calling through the query function
14998944Sobrien	 passed to us when we were initialized.  */
15098944Sobrien      (*gdb_kod_query) (command, buffer, &bufsiz);
15198944Sobrien#else
15298944Sobrien      /* Fake up a multi-part packet.  */
15398944Sobrien      if (! strncmp (&command[3], "a500005a", 8))
15498944Sobrien	strcpy (buffer, "KAL,01,1,f500005f;f500005f;");
15598944Sobrien      else
15698944Sobrien	strcpy (buffer, "KAL,02,0,a500005a;a500005a;de02869f;");
15798944Sobrien#endif
15898944Sobrien
15998944Sobrien      /* Empty response is an error.  */
16098944Sobrien      if (strlen (buffer) == 0)
16198944Sobrien	{
16298944Sobrien	  (*gdb_kod_display) ("Remote target did not recognize kernel object query command.\n");
16398944Sobrien	  fail = 1;
16498944Sobrien	  break;
16598944Sobrien	}
16698944Sobrien
16798944Sobrien      /* If we don't get a `K' response then the buffer holds the
16898944Sobrien	 target's error message.  */
16998944Sobrien      if (buffer[0] != 'K')
17098944Sobrien	{
17198944Sobrien	  (*gdb_kod_display) (buffer);
17298944Sobrien	  fail = 1;
17398944Sobrien	  break;
17498944Sobrien	}
17598944Sobrien
17698944Sobrien      /* Make sure we get the response we expect.  */
17798944Sobrien      if (strncmp (buffer, "KAL,", 4))
17898944Sobrien	{
17998944Sobrien	  bad_packet ();
18098944Sobrien	  fail = 1;
18198944Sobrien	  break;
18298944Sobrien	}
18398944Sobrien      off += 4;
18498944Sobrien
18598944Sobrien      /* Parse out the count.  We expect to convert exactly two
18698944Sobrien	 characters followed by a comma.  */
18798944Sobrien      count = strtol (&buffer[off], &s_end, 16);
18898944Sobrien      if (s_end - &buffer[off] != 2 || buffer[off + 2] != ',')
18998944Sobrien	{
19098944Sobrien	  bad_packet ();
19198944Sobrien	  fail = 1;
19298944Sobrien	  break;
19398944Sobrien	}
19498944Sobrien      off += 3;
19598944Sobrien
19698944Sobrien      /* Parse out the `done' flag.  */
19798944Sobrien      if ((buffer[off] != '0' && buffer[off] != '1')
19898944Sobrien	  || buffer[off + 1] != ',')
19998944Sobrien	{
20098944Sobrien	  bad_packet ();
20198944Sobrien	  fail = 1;
20298944Sobrien	  break;
20398944Sobrien	}
20498944Sobrien      done = buffer[off] == '1';
20598944Sobrien      off += 2;
20698944Sobrien
20798944Sobrien      /* Id of the last item; we might this to construct the next
20898944Sobrien	 request.  */
20998944Sobrien      prev_id = &buffer[off];
21098944Sobrien      if (strlen (prev_id) < 8 || buffer[off + 8] != ';')
21198944Sobrien	{
21298944Sobrien	  bad_packet ();
21398944Sobrien	  fail = 1;
21498944Sobrien	  break;
21598944Sobrien	}
21698944Sobrien      buffer[off + 8] = '\0';
21798944Sobrien      off += 9;
21898944Sobrien
21998944Sobrien      sync_len += count;
22098944Sobrien      sync_ids = (char **) xrealloc (sync_ids, sync_len * sizeof (char *));
22198944Sobrien
22298944Sobrien      for (i = 0; i < count; ++i)
22398944Sobrien	{
22498944Sobrien	  if (strlen (&buffer[off]) < 8 || buffer[off + 8] != ';')
22598944Sobrien	    {
22698944Sobrien	      bad_packet ();
22798944Sobrien	      fail = 1;
22898944Sobrien	      break;
22998944Sobrien	    }
23098944Sobrien	  buffer[off + 8] = '\0';
23198944Sobrien	  sync_ids[sync_next++] = xstrdup (&buffer[off]);
23298944Sobrien	  off += 9;
23398944Sobrien	}
23498944Sobrien
23598944Sobrien      if (buffer[off] != '\0')
23698944Sobrien	{
23798944Sobrien	  bad_packet ();
23898944Sobrien	  fail = 1;
23998944Sobrien	  break;
24098944Sobrien	}
24198944Sobrien    }
24298944Sobrien
24398944Sobrien  /* We've collected all the sync object IDs.  Now query to get the
24498944Sobrien     specific information, and arrange to print this info.  */
24598944Sobrien  if (! fail)
24698944Sobrien    {
24798944Sobrien      (*gdb_kod_display) ("Object ID\tObject Pointer\tThread ID\n");
24898944Sobrien
24998944Sobrien      for (i = 0; i < sync_next; ++i)
25098944Sobrien	{
25198944Sobrien	  int off = 0;
25298944Sobrien	  int bufsiz = PBUFSIZ;
25398944Sobrien
25498944Sobrien	  /* For now assume a query can be accomplished in a single
25598944Sobrien	     transaction.  This is implied in the protocol document.
25698944Sobrien	     See comments above, and the KOD protocol document, to
25798944Sobrien	     understand the parsing of the return value.  */
25898944Sobrien	  strcpy (command, "aI,");
25998944Sobrien	  strcat (command, sync_ids[i]);
26098944Sobrien	  strcat (command, ";");
26198944Sobrien
26298944Sobrien#ifndef FAKE_PACKET
26398944Sobrien	  (*gdb_kod_query) (command, buffer, &bufsiz);
26498944Sobrien#else
26598944Sobrien	  strcpy (buffer, "KAI,");
26698944Sobrien	  strcat (buffer, sync_ids[i]);
26798944Sobrien	  strcat (buffer, ",ffef00a0,cd00123d;");
26898944Sobrien#endif
26998944Sobrien
27098944Sobrien	  if (strlen (buffer) == 0)
27198944Sobrien	    {
27298944Sobrien	      (*gdb_kod_display) ("Remote target did not recognize KOD command.\n");
27398944Sobrien	      break;
27498944Sobrien	    }
27598944Sobrien
27698944Sobrien	  if (strncmp (buffer, "KAI,", 4))
27798944Sobrien	    {
27898944Sobrien	      bad_packet ();
27998944Sobrien	      break;
28098944Sobrien	    }
28198944Sobrien	  off += 4;
28298944Sobrien
28398944Sobrien	  if (strncmp (&buffer[off], sync_ids[i], 8)
28498944Sobrien	      || buffer[off + 8] != ',')
28598944Sobrien	    {
28698944Sobrien	      bad_packet ();
28798944Sobrien	      break;
28898944Sobrien	    }
28998944Sobrien	  off += 9;
29098944Sobrien
29198944Sobrien	  /* Extract thread id and sync object pointer.  */
29298944Sobrien	  if (strlen (&buffer[off]) != 2 * 8 + 2
29398944Sobrien	      || buffer[off + 8] != ','
29498944Sobrien	      || buffer[off + 17] != ';')
29598944Sobrien	    {
29698944Sobrien	      bad_packet ();
29798944Sobrien	      break;
29898944Sobrien	    }
29998944Sobrien
30098944Sobrien	  buffer[off + 8] = '\0';
30198944Sobrien	  buffer[off + 17] = '\0';
30298944Sobrien
30398944Sobrien	  /* Display the result.  */
30498944Sobrien	  (*gdb_kod_display) (sync_ids[i]);
30598944Sobrien	  (*gdb_kod_display) ("\t");
30698944Sobrien	  (*gdb_kod_display) (&buffer[off]);
30798944Sobrien	  (*gdb_kod_display) ("\t");
30898944Sobrien	  (*gdb_kod_display) (&buffer[off + 9]);
30998944Sobrien	  (*gdb_kod_display) ("\n");
31098944Sobrien	}
31198944Sobrien    }
31298944Sobrien
31398944Sobrien  /* Free memory.  */
31498944Sobrien  for (i = 0; i < sync_next; ++i)
31598944Sobrien    xfree (sync_ids[i]);
31698944Sobrien  xfree (sync_ids);
31798944Sobrien}
318