/* 
 *------------------------------------------------------------------
 * api_shared.c - API message handling, common code for both clients
 * and the vlib process itself.
 * 
 *
 * Copyright (c) 2009 Cisco and/or its affiliates.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at:
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *------------------------------------------------------------------
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <vppinfra/format.h>
#include <vppinfra/byte_order.h>
#include <vppinfra/error.h>
#include <vlib/vlib.h>
#include <vlib/unix/unix.h>
#include <vlibapi/api.h>
#include <vppinfra/elog.h>

api_main_t api_main;

void vl_msg_api_barrier_sync(void) __attribute__((weak));
void vl_msg_api_barrier_sync(void) { }

void vl_msg_api_barrier_release(void) __attribute__((weak));
void vl_msg_api_barrier_release(void) { }

void vl_msg_api_increment_missing_client_counter(void)
{
    api_main_t * am = &api_main;
    am->missing_clients++;
}

typedef enum {
    DUMP, 
    CUSTOM_DUMP,
    REPLAY,
    INITIALIZERS,
} vl_api_replay_t;

int vl_msg_api_rx_trace_enabled(api_main_t *am)
{
    return (am->rx_trace && am->rx_trace->enabled);
}

int vl_msg_api_tx_trace_enabled(api_main_t *am)
{
    return (am->tx_trace && am->tx_trace->enabled);
}

/*
 * vl_msg_api_trace
 */
void vl_msg_api_trace(api_main_t *am, vl_api_trace_t *tp, void *msg)
{
    u8 **this_trace;
    u8 **old_trace;
    u8 *msg_copy;
    trace_cfg_t *cfgp;
    u16 msg_id = ntohs(*((u16 *)msg));

    cfgp = am->api_trace_cfg + msg_id;

    if (!cfgp || !cfgp->trace_enable)
        return;

    msg_copy = 0;

    if (tp->nitems == 0) {
        clib_warning ("tp->nitems is 0");
        return;
    }

    if (vec_len(tp->traces) < tp->nitems) {
        vec_add1(tp->traces, 0);
        this_trace = tp->traces + vec_len(tp->traces) - 1;
    } else {
        tp->wrapped = 1;
        old_trace = tp->traces + tp->curindex++;
        if (tp->curindex == tp->nitems)
            tp->curindex = 0;
        vec_free(*old_trace);
        this_trace = old_trace;
    }

    vec_validate(msg_copy, cfgp->size - 1);
    clib_memcpy(msg_copy, msg, cfgp->size);
    *this_trace = msg_copy;
}

int vl_msg_api_trace_onoff(api_main_t *am, vl_api_trace_which_t which, 
                           int onoff)
{
    vl_api_trace_t *tp;
    int rv;

    switch(which)
    {
    case VL_API_TRACE_TX:
        tp = am->tx_trace;
        if (tp == 0) {
            vl_msg_api_trace_configure (am, which, 1024);
            tp = am->tx_trace;
        }
        break;
        
    case VL_API_TRACE_RX:
        tp = am->rx_trace;
        if (tp == 0) {
            vl_msg_api_trace_configure (am, which, 1024);
            tp = am->rx_trace;
        }
        break;
        
    default:
        /* duh? */
        return -1;
    }

    /* Configured? */
    if (tp == 0 || tp->nitems == 0)
        return -1;

    rv = tp->enabled;
    tp->enabled = onoff;

    return rv;
}

int vl_msg_api_trace_free(api_main_t *am, vl_api_trace_which_t which)
{
    vl_api_trace_t *tp;
    int i;

    switch(which)
    {
    case VL_API_TRACE_TX:
        tp = am->tx_trace;
        break;
        
    case VL_API_TRACE_RX:
        tp = am->rx_trace;
        break;
        
    default:
        /* duh? */
        return -1;
    }

    /* Configured? */
    if (!tp || tp->nitems == 0)
        return -1;

    tp->curindex = 0;
    tp->wrapped = 0;

    for (i = 0; i < vec_len(tp->traces); i++) {
        vec_free(tp->traces[i]);
    }
    vec_free(tp->traces);

    return 0;
}

int vl_msg_api_trace_save(api_main_t *am, 
                          vl_api_trace_which_t which, FILE *fp)
{
    vl_api_trace_t *tp;
    vl_api_trace_file_header_t fh;
    int i;
    u8 *msg;

    switch(which)
    {
    case VL_API_TRACE_TX:
        tp = am->tx_trace;
        break;
        
    case VL_API_TRACE_RX:
        tp = am->rx_trace;
        break;
        
    default:
        /* duh? */
        return -1;
    }

    /* Configured, data present? */
    if (tp == 0 || tp->nitems == 0 || vec_len(tp->traces) == 0)
        return -1;

    /* "Dare to be stupid" check */
    if (fp == 0) {
        return -2;
    }

    /* Write the file header */
    fh.nitems = vec_len(tp->traces);
    fh.endian = tp->endian;
    fh.wrapped = tp->wrapped;

    if (fwrite(&fh, sizeof(fh), 1, fp) != 1) {
        return (-10);
    }

    /* No-wrap case */
    if (tp->wrapped == 0) {
        /*
         * Note: vec_len return 0 when fed a NULL pointer.
         * Unfortunately, the static analysis tool doesn't
         * figure it out, hence the suppressed warnings.
         * What a great use of my time. 
         */
        for (i = 0; i < vec_len(tp->traces); i++) {
            /*sa_ignore NO_NULL_CHK*/
            msg = tp->traces[i];
            /*
             * This retarded check required to pass
             * [sic] SA-checking. 
             */
            if (!msg)
                continue;
            if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
                return (-11);
            }
        }
    } else {                    
        /* Wrap case: write oldest -> end of buffer */
        for (i = tp->curindex; i < vec_len(tp->traces); i++) {
            msg = tp->traces[i];
            /*
             * This retarded check required to pass
             * [sic] SA-checking 
             */
            if (!msg)
                continue;

            if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
                return (-12);
            }
        }
        /* write beginning of buffer -> oldest-1 */
        for (i = 0; i < tp->curindex; i++) {
            /*sa_ignore NO_NULL_CHK*/
            msg = tp->traces[i];
            /*
             * This retarded check required to pass
             * [sic] SA-checking 
             */
            if (!msg)
                continue;

            if (fwrite(msg, 1, vec_len(msg), fp) != vec_len(msg)) {
                return (-13);
            }
        }
    }
    return 0;
}

int vl_msg_api_trace_configure(api_main_t *am, vl_api_trace_which_t which, 
                               u32 nitems)
{
    vl_api_trace_t *tp;
    int was_on = 0;

    switch(which)
    {
    case VL_API_TRACE_TX:
        tp = am->tx_trace;
        if (tp == 0) {
            vec_validate(am->tx_trace, 0);
            tp = am->tx_trace;
        }
        break;
        
    case VL_API_TRACE_RX:
        tp = am->rx_trace;
        if (tp == 0) {
            vec_validate(am->rx_trace, 0);
            tp = am->rx_trace;
        }

        break;

    default:
        return -1;

    }

    if (tp->enabled) {
        was_on = vl_msg_api_trace_onoff(am, which, 0);
    }
    if (tp->traces) {
        vl_msg_api_trace_free(am, which);
    }

    memset(tp, 0, sizeof(*tp));

    if (clib_arch_is_big_endian) {
        tp->endian = VL_API_BIG_ENDIAN;
    } else {
        tp->endian = VL_API_LITTLE_ENDIAN;
    }

    tp->nitems = nitems;
    if (was_on) {
        (void)vl_msg_api_trace_onoff(am, which, was_on);
    }
    return 0;
}

always_inline void msg_handler_internal (api_main_t *am, 
					 void *the_msg, 
					 int trace_it,
					 int do_it, 
					 int free_it)
{
    u16 id = ntohs(*((u16 *)the_msg));
    u8 *(*print_fp)(void *, void *);

    if (id < vec_len(am->msg_handlers) && 
        am->msg_handlers[id]) {
        if (trace_it)
            vl_msg_api_trace(am, am->rx_trace, the_msg);
        
        if (am->msg_print_flag) {
            fformat (stdout, "[%d]: %s\n", id, 
                     am->msg_names[id]);
            print_fp = (void *)am->msg_print_handlers[id];
            if (print_fp == 0) {
                fformat(stdout, "  [no registered print fn]\n");
            } else {
                (*print_fp)(the_msg, stdout);
            }
        }

        if (do_it) {
            if (!am->is_mp_safe[id])
                vl_msg_api_barrier_sync();
            (*am->msg_handlers[id])(the_msg);
            if (!am->is_mp_safe[id])
                vl_msg_api_barrier_release();
        }
    } else {
        clib_warning("no handler for msg id %d", id);
    }

    if (free_it) 
        vl_msg_api_free(the_msg);
}

/* set to 1 if you want before/after message handler event logging */
#define ELOG_API_MESSAGE_HANDLERS 0

#if ELOG_API_MESSAGE_HANDLERS > 0
static u32 elog_id_for_msg_name (vlib_main_t * vm, char *msg_name)
{
  uword * p, r;
  static uword * h;
  u8 *name_copy;

  if (! h)
      h = hash_create_string (0, sizeof (uword));

  p = hash_get_mem (h, msg_name);
  if (p)
    return p[0];
  r = elog_string (&vm->elog_main, "%s", msg_name);

  name_copy = format (0, "%s%c", msg_name, 0);

  hash_set_mem (h, name_copy, r);

  return r;
}
#endif

/* This is only to be called from a vlib/vnet app */
void vl_msg_api_handler_with_vm_node (api_main_t *am, 
                                      void *the_msg, vlib_main_t *vm, 
                                      vlib_node_runtime_t *node)
{
  u16 id = ntohs(*((u16 *)the_msg));
  u8 *(*handler)(void *, void *, void *);
    
#if ELOG_API_MESSAGE_HANDLERS > 0
    {
      ELOG_TYPE_DECLARE (e) = {
        .format = "api-msg: %s",
        .format_args = "T4",
      };
      struct { u32 c; } * ed;
      ed = ELOG_DATA (&vm->elog_main, e);
      if (id < vec_len (am->msg_names))
        ed->c = elog_id_for_msg_name (vm, am->msg_names[id]);
      else
        ed->c = elog_id_for_msg_name (vm, "BOGUS");
    }
#endif    

  if (id < vec_len(am->msg_handlers) && 
      am->msg_handlers[id]) {
    handler = (void *)am->msg_handlers[id];

    if (am->rx_trace && am->rx_trace->enabled)
      vl_msg_api_trace(am, am->rx_trace, the_msg);

    if (!am->is_mp_safe[id])
      vl_msg_api_barrier_sync();
    (*handler)(the_msg, vm, node);
    if (!am->is_mp_safe[id])
      vl_msg_api_barrier_release();
  } else {
    clib_warning("no hander for msg id %d", id);
  }
  /* 
   * Special-case, so we can e.g. bounce messages off the vnet
   * main thread without copying them...
   */
  if (!(am->message_bounce[id]))
    vl_msg_api_free(the_msg);

#if ELOG_API_MESSAGE_HANDLERS > 0
    {
      ELOG_TYPE_DECLARE (e) = {
        .format = "api-msg-done: %s",
        .format_args = "T4",
      };
      struct { u32 c; } * ed;
      ed = ELOG_DATA (&vm->elog_main, e);
      if (id < vec_len (am->msg_names))
        ed->c = elog_id_for_msg_name (vm, am->msg_names[id]);
      else
        ed->c = elog_id_for_msg_name (vm, "BOGUS");
    }
#endif
}

void vl_msg_api_handler (void *the_msg)
{
    api_main_t *am = &api_main;

    msg_handler_internal (am, the_msg, 
                          (am->rx_trace 
                           && am->rx_trace->enabled) /* trace_it */, 
                          1 /* do_it */, 1 /* free_it */);
}

void vl_msg_api_handler_no_free (void *the_msg)
{
    api_main_t *am = &api_main;
    msg_handler_internal (am, the_msg, 
                          (am->rx_trace 
                           && am->rx_trace->enabled) /* trace_it */, 
                          1 /* do_it */, 0 /* free_it */);
}

void vl_msg_api_handler_no_trace_no_free (void *the_msg)
{
    api_main_t *am = &api_main;
    msg_handler_internal (am, the_msg, 0/* trace_it */, 1 /* do_it */, 
                          0 /* free_it */);
}

/*
 * Add a trace record to the API message trace buffer, if
 * API message tracing is enabled. Handy for adding sufficient
 * data to the trace to reproduce autonomous state, as opposed to
 * state downloaded via control-plane API messages. Example: the NAT 
 * application creates database entries based on packet traffic, not
 * control-plane messages.
 *
 */
void vl_msg_api_trace_only (void *the_msg)
{
    api_main_t *am = &api_main;

    msg_handler_internal (am, the_msg, 
                          (am->rx_trace 
                           && am->rx_trace->enabled) /* trace_it */,
                          0 /* do_it */, 0 /* free_it */);
}

void vl_msg_api_cleanup_handler (void *the_msg)
{
    api_main_t *am = &api_main;
    u16 id = ntohs(*((u16 *)the_msg));

    if (PREDICT_FALSE(id >= vec_len(am->msg_cleanup_handlers))) {
        clib_warning ("_vl_msg_id too large: %d\n", id);
        return;
    }
    if (am->msg_cleanup_handlers[id])
        (*am->msg_cleanup_handlers[id])(the_msg);

    vl_msg_api_free(the_msg);
}

/*
 * vl_msg_api_replay_handler
 */
void vl_msg_api_replay_handler(void *the_msg)
{
    api_main_t *am = &api_main;

    u16 id = ntohs(*((u16 *)the_msg));

    if (PREDICT_FALSE(id >= vec_len(am->msg_handlers))) {
        clib_warning ("_vl_msg_id too large: %d\n", id);
        return;
    }
    /* do NOT trace the message... */
    if (am->msg_handlers[id])
        (*am->msg_handlers[id])(the_msg);
    /* do NOT free the message buffer... */
}
/*
 * vl_msg_api_socket_handler
 */
void vl_msg_api_socket_handler(void *the_msg)
{
    api_main_t *am = &api_main;

    msg_handler_internal (am, the_msg, 
                          (am->rx_trace 
                           && am->rx_trace->enabled) /* trace_it */,
                          1 /* do_it */, 0 /* free_it */);
}

#define foreach_msg_api_vector                  \
_(msg_names)                                    \
_(msg_handlers)                                 \
_(msg_cleanup_handlers)                         \
_(msg_endian_handlers)                          \
_(msg_print_handlers)                           \
_(api_trace_cfg)				\
_(message_bounce)				\
_(is_mp_safe)

void vl_msg_api_config (vl_msg_api_msg_config_t *c)
{
    api_main_t *am = &api_main;

    ASSERT(c->id > 0);

#define _(a) vec_validate (am->a, c->id);
    foreach_msg_api_vector;
#undef _

    am->msg_names[c->id] = c->name;
    am->msg_handlers[c->id] = c->handler;
    am->msg_cleanup_handlers[c->id] = c->cleanup;
    am->msg_endian_handlers[c->id] = c->endian;
    am->msg_print_handlers[c->id] = c->print;
    am->message_bounce[c->id] = c->message_bounce;
    am->is_mp_safe[c->id] = c->is_mp_safe;

    am->api_trace_cfg[c->id].size = c->size;
    am->api_trace_cfg[c->id].trace_enable = c->traced;
    am->api_trace_cfg[c->id].replay_enable = c->replay;
}

/* 
 * vl_msg_api_set_handlers
 * preserve the old API for a while
 */
void vl_msg_api_set_handlers(int id, char *name, void *handler, void *cleanup,
                             void *endian, void *print, int size, int traced)
{
    vl_msg_api_msg_config_t cfg;
    vl_msg_api_msg_config_t *c = &cfg;

    c->id = id;
    c->name = name;
    c->handler = handler;
    c->cleanup = cleanup;
    c->endian = endian;
    c->print = print;
    c->size = size;
    c->traced = traced;
    c->replay = 1;
    c->message_bounce = 0;
    c->is_mp_safe = 0;
    vl_msg_api_config (c);
}

void vl_msg_api_set_cleanup_handler(int msg_id, void *fp)
{
    api_main_t *am = &api_main;
    ASSERT(msg_id > 0);

    vec_validate(am->msg_cleanup_handlers, msg_id);
    am->msg_cleanup_handlers[msg_id] = fp;
}

void vl_msg_api_queue_handler(unix_shared_memory_queue_t *q)
{
    uword msg;

    while (!unix_shared_memory_queue_sub(q, (u8 *)&msg, 0))
        vl_msg_api_handler((void *)msg);
}

vl_api_trace_t *vl_msg_api_trace_get(api_main_t *am, vl_api_trace_which_t which)
{
    switch(which)
    {
    case VL_API_TRACE_RX:
        return am->rx_trace;
    case VL_API_TRACE_TX:
        return am->tx_trace;
    default:
        return 0;
    }
}

void vl_noop_handler (void *mp) { }

clib_error_t *
vl_api_init (vlib_main_t *vm)
{
    static u8 once;
    api_main_t *am = &api_main;

    if (once)
        return 0;
    
    once = 1;

    am->region_name = "/unset";

    return (0);
}

void vl_msg_api_custom_dump_configure (api_main_t *am) __attribute__((weak));
void vl_msg_api_custom_dump_configure (api_main_t *am) { }

VLIB_INIT_FUNCTION (vl_api_init);

static void vl_msg_api_process_file (vlib_main_t *vm, u8 *filename, 
                                     u32 first_index, u32 last_index, 
                                     vl_api_replay_t which)
{
    vl_api_trace_file_header_t * hp;
    int i, fd;
    struct stat statb;
    size_t file_size;
    u8 *msg;
    u8 endian_swap_needed = 0;
    api_main_t * am = &api_main;
    static u8 *tmpbuf;
    u32 nitems;
    void **saved_print_handlers = 0;

    fd = open ((char *) filename, O_RDONLY);

    if (fd < 0) {
        vlib_cli_output (vm, "Couldn't open %s\n", filename);
        return;
    }

    if (fstat(fd, &statb) < 0) {
        vlib_cli_output (vm, "Couldn't stat %s\n", filename);
        return;
    }

    if (! (statb.st_mode & S_IFREG) || (statb.st_size < sizeof (*hp))) {
        vlib_cli_output (vm, "File not plausible: %s\n", filename);
        return;
    }

    file_size = statb.st_size;
    file_size = (file_size + 4095) & ~(4096);

    hp = mmap (0, file_size, PROT_READ, MAP_PRIVATE, fd, 0);

    if (hp == (vl_api_trace_file_header_t *)MAP_FAILED) {
        vlib_cli_output (vm, "mmap failed: %s\n", filename);
        close(fd);
        return;
    }
    close(fd);

    if ((clib_arch_is_little_endian && hp->endian == VL_API_BIG_ENDIAN)
        || (clib_arch_is_big_endian && hp->endian == VL_API_LITTLE_ENDIAN))
        endian_swap_needed = 1;
    
    if (endian_swap_needed)
        nitems = ntohl(hp->nitems);
    else
        nitems = hp->nitems;

    if (last_index == (u32) ~0) {
        last_index = nitems - 1;
    }
    
    if (first_index >= nitems || last_index >= nitems) {
        vlib_cli_output (vm, "Range (%d, %d) outside file range (0, %d)\n",
                         first_index, last_index, nitems-1);
        return;
    }
    if (hp->wrapped)
        vlib_cli_output (vm, 
                         "Note: wrapped/incomplete trace, results may vary\n");
    
    if (which == CUSTOM_DUMP) {
        saved_print_handlers = (void **) vec_dup (am->msg_print_handlers);
        vl_msg_api_custom_dump_configure (am);
    }


    msg = (u8 *)(hp+1);
    
    for (i = 0; i < first_index; i++) {
        trace_cfg_t *cfgp;
        int size;
        u16 msg_id;
        
        if (clib_arch_is_little_endian)
            msg_id = ntohs(*((u16 *)msg));
        else
            msg_id = *((u16 *)msg);
        
        cfgp = am->api_trace_cfg + msg_id;
        if (!cfgp) {
            vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
            return;
        }
        size = cfgp->size;
        msg += size;
    }
    
    for (; i <= last_index; i++) {
        trace_cfg_t *cfgp;
        u16 *msg_idp;
        u16 msg_id;
        int size;
        
        if (which == DUMP)
            vlib_cli_output (vm, "---------- trace %d -----------\n", i);

        if (clib_arch_is_little_endian)
            msg_id = ntohs(*((u16 *)msg));
        else
            msg_id = *((u16 *)msg);
        
        cfgp = am->api_trace_cfg + msg_id;
        if (!cfgp) {
            vlib_cli_output (vm, "Ugh: msg id %d no trace config\n", msg_id);
            return;
        }
        size = cfgp->size;

        /* Copy the buffer (from the read-only mmap'ed file) */
        vec_validate (tmpbuf, size-1 + sizeof(uword));
        clib_memcpy (tmpbuf+sizeof(uword), msg, size);
        memset (tmpbuf, 0xf, sizeof(uword));

        /* 
         * Endian swap if needed. All msg data is supposed to be 
         * in network byte order. All msg handlers are supposed to
         * know that. The generic message dumpers don't know that.
         * One could fix apigen, I suppose.
         */
        if ((which == DUMP && clib_arch_is_little_endian) 
            || endian_swap_needed) {
            void (*endian_fp)(void *);
            if (msg_id >= vec_len (am->msg_endian_handlers)
                || (am->msg_endian_handlers[msg_id] == 0)) {
                vlib_cli_output (vm, "Ugh: msg id %d no endian swap\n", msg_id);
                return;
            }
            endian_fp = am->msg_endian_handlers[msg_id];
            (*endian_fp)(tmpbuf+sizeof(uword));
        }

        /* msg_id always in network byte order */
        if (clib_arch_is_little_endian) {
            msg_idp = (u16 *)(tmpbuf+sizeof(uword));
            *msg_idp = msg_id;
        }

        switch (which) {
        case CUSTOM_DUMP:
        case DUMP:
            if (msg_id < vec_len(am->msg_print_handlers) &&
                am->msg_print_handlers [msg_id]) {
                u8 *(*print_fp)(void *, void *);
                
                print_fp = (void *)am->msg_print_handlers[msg_id];
                (*print_fp)(tmpbuf+sizeof(uword), vm);
            } else {
                vlib_cli_output (vm, "Skipping msg id %d: no print fcn\n",
                                 msg_id);
                break;
            }
            break;

        case INITIALIZERS:
            if (msg_id < vec_len(am->msg_print_handlers) &&
                am->msg_print_handlers [msg_id]) {
                u8 * s;
                int j;
                u8 *(*print_fp)(void *, void *);

                print_fp = (void *)am->msg_print_handlers[msg_id];
                
                vlib_cli_output (vm, "/*");
                
                (*print_fp)(tmpbuf+sizeof(uword), vm);
                vlib_cli_output (vm, "*/\n");
                
                s = format (0, "static u8 * vl_api_%s_%d[%d] = {",
                            am->msg_names[msg_id], i, 
                            am->api_trace_cfg[msg_id].size);
                
                for (j = 0; j < am->api_trace_cfg[msg_id].size; j++) {
                    if ((j & 7) == 0)
                        s = format (s, "\n    ");    
                    s = format (s, "0x%02x,", tmpbuf[sizeof(uword)+j]);
                }
                s = format (s, "\n};\n%c", 0);
                vlib_cli_output (vm, (char *)s);
                vec_free(s);
            }
            break;
                
        case REPLAY:
            if (msg_id < vec_len(am->msg_print_handlers) &&
                am->msg_print_handlers [msg_id] && cfgp->replay_enable) {
                void (*handler)(void *);
                
                handler = (void *)am->msg_handlers[msg_id];

                if (!am->is_mp_safe[msg_id])
                    vl_msg_api_barrier_sync();
                (*handler)(tmpbuf+sizeof(uword));
                if (!am->is_mp_safe[msg_id])
                    vl_msg_api_barrier_release();
            } else {
                if (cfgp->replay_enable)
                    vlib_cli_output (vm, "Skipping msg id %d: no handler\n",
                                     msg_id);
                break;
            }
            break;
        }

        _vec_len(tmpbuf) = 0;
        msg += size;
    }
    
    if (saved_print_handlers) {
        clib_memcpy (am->msg_print_handlers, saved_print_handlers, 
                vec_len(am->msg_print_handlers) * sizeof (void *));
        vec_free (saved_print_handlers);
    }

    munmap (hp, file_size);
}

u8 * format_vl_msg_api_trace_status (u8 * s, va_list * args)
{
    api_main_t * am = va_arg (*args, api_main_t *);
    vl_api_trace_which_t which = va_arg (*args, vl_api_trace_which_t);
    vl_api_trace_t *tp;
    char *trace_name;

    switch(which)
    {
    case VL_API_TRACE_TX:
        tp = am->tx_trace;
        trace_name = "TX trace";
        break;
        
    case VL_API_TRACE_RX:
        tp = am->rx_trace;
        trace_name = "RX trace";
        break;

    default:
        abort();
    }

    if (tp == 0) {
        s = format (s, "%s: not yet configured.\n", trace_name);
        return s;
    }

    s = format (s, "%s: used %d of %d items, %s enabled, %s wrapped\n",
                trace_name, vec_len (tp->traces), tp->nitems, 
                tp->enabled ? "is" : "is not",
                tp->wrapped ? "has" : "has not");
    return s;
}

static u8 post_mortem_dump_enabled;

static clib_error_t *
api_trace_command_fn (vlib_main_t * vm,
		 unformat_input_t * input,
		 vlib_cli_command_t * cmd)
{
    u32 nitems = 256<<10;
    api_main_t * am = &api_main;
    vl_api_trace_which_t which = VL_API_TRACE_RX;
    u8 *filename;
    u32 first = 0;
    u32 last = (u32)~0;
    FILE *fp;
    int rv;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat (input, "on") || unformat (input, "enable")) {
            if (unformat (input, "nitems %d", &nitems))
                ;
            vl_msg_api_trace_configure (am, which, nitems);
            vl_msg_api_trace_onoff (am, which, 1 /* on */);
        } else if (unformat (input, "off")) {
            vl_msg_api_trace_onoff (am, which, 0);
        } else if (unformat (input, "save %s", &filename)) {
            u8 * chroot_filename;
            if (strstr((char *)filename, "..") 
                || index((char *)filename, '/'))
            {
                vlib_cli_output (vm, "illegal characters in filename '%s'", 
                                 filename);
                return 0;
            }

            chroot_filename = format (0, "/tmp/%s%c", filename, 0);

            vec_free(filename);
            
            fp = fopen ((char *)chroot_filename, "w");
            if (fp == NULL) {
                vlib_cli_output (vm, "Couldn't create %s\n", chroot_filename);
                return 0;
            }
            rv = vl_msg_api_trace_save (am, which, fp);
            fclose (fp);
            if (rv < 0)
                vlib_cli_output (vm, "ERROR: %d", rv);
            else
                vlib_cli_output (vm, "API trace saved to %s\n", 
                                 chroot_filename);
            vec_free (chroot_filename);
        } else if (unformat (input, "dump %s", &filename)) {
            vl_msg_api_process_file (vm, filename, first, last, DUMP);
        } else if (unformat (input, "custom-dump %s", &filename)) {
            vl_msg_api_process_file (vm, filename, first, last, CUSTOM_DUMP);
        } else if (unformat (input, "replay %s", &filename)) {
            vl_msg_api_process_file (vm, filename, first, last, REPLAY);
        } else if (unformat (input, "initializers %s", &filename)) {
            vl_msg_api_process_file (vm, filename, first, last, INITIALIZERS);
        } else if (unformat (input, "tx")) {
            which = VL_API_TRACE_TX;
        } else if (unformat (input, "first %d", &first)) {
            ;
        } else if (unformat (input, "last %d", &last)) {
            ;
        } else if (unformat (input, "status")) {
            vlib_cli_output (vm, "%U", format_vl_msg_api_trace_status,
                             am, which);
        } else if (unformat (input, "free")) {
            vl_msg_api_trace_onoff (am, which, 0);
            vl_msg_api_trace_free (am, which);
        } else if (unformat (input, "post-mortem-on"))
            post_mortem_dump_enabled = 1;
        else if (unformat (input, "post-mortem-off"))        
            post_mortem_dump_enabled = 0;
        else
            return clib_error_return (0, "unknown input `%U'",
                                      format_unformat_error, input);
    }
    return 0;
}

VLIB_CLI_COMMAND (api_trace_command, static) = {
    .path = "api trace",
    .short_help = 
    "api trace [on|off][dump|save|replay <file>][status][free][post-mortem-on]",
    .function = api_trace_command_fn,
};

static clib_error_t *
api_config_fn (vlib_main_t * vm, unformat_input_t * input)
{
    u32 nitems = 256<<10;
    vl_api_trace_which_t which = VL_API_TRACE_RX;
    api_main_t * am = &api_main;

    while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) {
        if (unformat (input, "on") || unformat (input, "enable")) {
            if (unformat (input, "nitems %d", &nitems))
                ;
            vl_msg_api_trace_configure (am, which, nitems);
            vl_msg_api_trace_onoff (am, which, 1 /* on */);
            post_mortem_dump_enabled = 1;
        } else
            return clib_error_return (0, "unknown input `%U'",
                                      format_unformat_error, input);
    }
    return 0;
}

VLIB_CONFIG_FUNCTION (api_config_fn, "api-trace");

void vl_msg_api_post_mortem_dump (void)
{
    api_main_t * am = &api_main;
    FILE *fp;
    char filename[64];
    int rv;

    if (post_mortem_dump_enabled == 0)
        return;

    snprintf (filename, sizeof(filename), "/tmp/api_post_mortem.%d",
              getpid());

    fp = fopen (filename, "w");
    if (fp == NULL) {
        rv = write (2, "Couldn't create ", 16);
        rv = write (2, filename, strlen(filename));
        rv = write (2, "\n", 1);
        return;
    }
    rv = vl_msg_api_trace_save (am, VL_API_TRACE_RX, fp);
    fclose (fp);
    if (rv < 0) {
        rv = write (2, "Failed to save post-mortem API trace to ", 40);
        rv = write (2, filename, strlen(filename));
        rv = write (2, "\n", 1);
    }
    
}

/* Layered message handling support */

void vl_msg_api_register_pd_handler (void *fp, u16 msg_id_host_byte_order)
{
    api_main_t * am = &api_main;

    /* Mild idiot proofing */
    if (msg_id_host_byte_order > 10000)
        clib_warning ("msg_id_host_byte_order endian issue? %d arg vs %d",
                      msg_id_host_byte_order, 
                      clib_net_to_host_u16 (msg_id_host_byte_order));
    vec_validate (am->pd_msg_handlers, msg_id_host_byte_order);
    am->pd_msg_handlers[msg_id_host_byte_order] = fp;
}

int vl_msg_api_pd_handler (void *mp, int rv)
{
    api_main_t * am = &api_main;
    int (*fp)(void *, int);
    u16 msg_id;

    if (clib_arch_is_little_endian)
        msg_id = clib_net_to_host_u16(*((u16 *)mp));
    else
        msg_id = *((u16 *)mp);
    
    if (msg_id >= vec_len (am->pd_msg_handlers)
        || am->pd_msg_handlers[msg_id] == 0)
        return rv;

    fp = am->pd_msg_handlers [msg_id];
    rv = (*fp)(mp, rv);
    return rv;
}

void vl_msg_api_set_first_available_msg_id (u16 first_avail)
{
    api_main_t * am = &api_main;
    
    am->first_available_msg_id = first_avail;
}

u16 vl_msg_api_get_msg_ids (char * name, int n)
{
    api_main_t * am = &api_main;
    u8 * name_copy;
    vl_api_msg_range_t * rp;
    uword * p;
    u16 rv;

    if (am->msg_range_by_name == 0)
        am->msg_range_by_name = hash_create_string (0, sizeof(uword));

    name_copy = format (0, "%s%c", name, 0);
    
    p = hash_get_mem (am->msg_range_by_name, name_copy);
    if (p) {
        clib_warning ("WARNING: duplicate message range registration for '%s'",
                      name_copy);
        vec_free(name_copy);
        return ((u16) ~0);
    }

    if (n < 0 || n > 1024) {
        clib_warning 
            ("WARNING: bad number of message-IDs (%d) requested by '%s'",
             n, name_copy);
        vec_free(name_copy);
        return ((u16) ~0);
    }

    vec_add2 (am->msg_ranges, rp, 1);

    rv = rp->first_msg_id = am->first_available_msg_id;
    am->first_available_msg_id += n;
    rp->last_msg_id = am->first_available_msg_id - 1;
    rp->name = name_copy;

    hash_set_mem (am->msg_range_by_name, name_copy, rp - am->msg_ranges);

    return rv;
}