/*
 * module.cc -- Linux kernel module main program
 * Eddie Kohler, Robert Morris
 *
 * Copyright (c) 1999-2000 Massachusetts Institute of Technology
 * Copyright (c) 2000 Mazu Networks, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include "modulepriv.hh"

#include <click/skbmgr.hh>
#include <click/lexer.hh>
#include <click/router.hh>
#include <click/straccum.hh>
#include <click/confparse.hh>
#include <click/bighashmap_arena.hh>
#include <click/notifier.hh>
#include <click/nameinfo.hh>

int click_mode_r, click_mode_w, click_mode_x, click_mode_dir;

extern "C" int click_accessible();
extern "C" int click_cleanup_packages();

KernelErrorHandler *click_logged_errh = 0;
static KernelErrorHandler *syslog_errh = 0;
Router *click_router = 0;
Master *click_master = 0;


/***************************** Global handlers *******************************/

static String
read_cycles(Element *, void *)
{
  StringAccum sa;
  sa << click_get_cycles() << " cycles\n";
  return sa.take_string();
}

#ifdef HAVE_LINUX_READ_NET_SKBCOUNT
extern "C" int read_net_skbcount(void);
#endif

static String
read_meminfo(Element *, void *)
{
    extern size_t click_dmalloc_curnew, click_dmalloc_totalnew;
#if CLICK_DMALLOC
    extern size_t click_dmalloc_curmem, click_dmalloc_maxmem;
#endif
    StringAccum sa;
    sa << "outstanding news " << click_dmalloc_totalnew << "\n"
       << "news " << click_dmalloc_curnew << "\n";
#if CLICK_DMALLOC
    sa << "current allocated mem " << click_dmalloc_curmem << '\n'
       << "max allocated mem " << click_dmalloc_maxmem << '\n';
#endif
#ifdef HAVE_LINUX_READ_NET_SKBCOUNT
    sa << "net_skbcount " << read_net_skbcount() << "\n";
#endif
    return sa.take_string();
}

static String
read_packages(Element *, void *)
{
  StringAccum sa;
  Vector<String> v;
  click_public_packages(v);
  for (int i = 0; i < v.size(); i++)
    sa << v[i] << "\n";
  return sa.take_string();
}


/******************************* Assertions **********************************/

#if HAVE_KERNEL_ASSERT
static bool assert_stops_router = false;
#endif

extern "C" void
click_assert_failed(const char *file, int line, const char *problem_text)
{
  click_chatter("%s:%d: assertion failed: %s", file, line, problem_text);
#if HAVE_KERNEL_ASSERT
  if (assert_stops_router) {
    if (click_router) {
      click_chatter("%s:%d: assertion failed: Asking router to stop", file, line);
      click_router->set_runcount(Router::STOP_RUNCOUNT);
    } else
      click_chatter("%s:%d: assertion failed: No router to stop", file, line);
  }
#endif
}

#if HAVE_KERNEL_ASSERT
static String
read_assert_stop(Element *, void *)
{
  return (assert_stops_router ? "true\n" : "false\n");
}

static int
write_assert_stop(const String &s, Element *, void *, ErrorHandler *errh)
{
  bool stop;
  if (!cp_bool(cp_uncomment(s), &stop))
    return errh->error("assert_stop must be a boolean");
  else {
    assert_stops_router = stop;
    return 0;
  }
}
#endif


/****************************** Error handlers *******************************/

void
KernelErrorHandler::log_line(const char *begin, const char *end)
{
  static_assert(LOGBUF_SIZ == LOGBUF_SAVESIZ * 2);

  // ensure begin <= end
  if (begin > end)
    begin = end;
  
  // skip "chatter: " for message log
  if (begin + 9 <= end && memcmp(begin, "chatter: ", 9) == 0)
    begin += 9;

  // manipulate log buffer to prevent memory overflow
  if (_pos + end - begin > LOGBUF_SIZ - 1 && _pos >= LOGBUF_SAVESIZ) {
    memcpy(&_logbuf[0], &_logbuf[LOGBUF_SAVESIZ], _pos - LOGBUF_SAVESIZ);
    _pos -= LOGBUF_SAVESIZ;
    _generation++;
  }
  if (_pos + end - begin > LOGBUF_SIZ - 1) {
    _pos = 0;
    _generation += 2;
  }
  if (_pos + end - begin > LOGBUF_SIZ - 1)
    begin = end - (LOGBUF_SIZ - 1);

  // log line
  memcpy(&_logbuf[_pos], begin, end - begin);
  _pos += end - begin;
  _logbuf[_pos++] = '\n';
}

void
KernelErrorHandler::handle_text(Seriousness seriousness, const String &message)
{
  // print message to syslog
  const char *begin = message.begin();
  const char *end = message.end();
  while (begin < end) {
    const char *newline = find(begin, end, '\n');
    printk("<1>%.*s\n", newline - begin, begin);
    log_line(begin, newline);
    begin = newline + 1;
  }

  // panic on fatal errors
  if (seriousness >= ERR_MIN_FATAL)
    panic("click");
}

inline String
KernelErrorHandler::stable_string() const
{
  return String::stable_string(&_logbuf[0], &_logbuf[_pos]);
}

static String
read_errors(Element *, void *thunk)
{
  KernelErrorHandler *errh = (thunk ? syslog_errh : click_logged_errh);
  if (errh)
    // OK to return a stable_string, even though the data is not really
    // stable, because we use it for a very short time (HANDLER_REREAD).
    // Problems are possible, of course.
    return errh->stable_string();
  else
    return String::out_of_memory_string();
}

void
click_clear_error_log()
{
  if (click_logged_errh)
    click_logged_errh->clear_log();
  if (syslog_errh)
    syslog_errh->clear_log();
}



/******************** Module initialization and cleanup **********************/

extern "C" int
init_module()
{
  // C++ static initializers
  String::static_initialize();
  NameInfo::static_initialize();
  cp_va_static_initialize();

  // error initialization
  syslog_errh = new KernelErrorHandler;
  click_logged_errh = new KernelErrorHandler;
  ErrorHandler::static_initialize(new LandmarkErrorHandler(syslog_errh, "chatter"));

  // default provisions
  Router::static_initialize();
  NotifierSignal::static_initialize();
  CLICK_DEFAULT_PROVIDES;

  // thread manager, sk_buff manager, config manager
  click_init_sched(ErrorHandler::default_handler());
  skbmgr_init();
  click_init_config();
  
  // global handlers
  Router::add_read_handler(0, "packages", read_packages, 0);
  Router::add_read_handler(0, "meminfo", read_meminfo, 0);
  Router::add_read_handler(0, "cycles", read_cycles, 0);
  Router::add_read_handler(0, "errors", read_errors, 0);
  Router::change_handler_flags(0, "errors", 0, HANDLER_REREAD);
  Router::add_read_handler(0, "messages", read_errors, (void *)1);
  Router::change_handler_flags(0, "messages", 0, HANDLER_REREAD);
#if HAVE_KERNEL_ASSERT
  Router::add_read_handler(0, "assert_stop", read_assert_stop, 0);
  Router::add_write_handler(0, "assert_stop", write_assert_stop, 0);
#endif

  // filesystem interface
  // set modes based on 'accessible'
  if (click_accessible()) {
    click_mode_r = S_IRUSR | S_IRGRP | S_IROTH;
    click_mode_x = S_IXUSR | S_IXGRP | S_IXOTH;
  } else {
    click_mode_r = S_IRUSR | S_IRGRP;
    click_mode_x = S_IXUSR | S_IXGRP;
  }
  click_mode_w = S_IWUSR | S_IWGRP;
  click_mode_dir = S_IFDIR | click_mode_r | click_mode_x;

  init_clickfs();

  return 0;
}

void click_dmalloc_cleanup();

extern "C" void
cleanup_module()
{
  extern size_t click_dmalloc_curnew; /* glue.cc */
  
  // filesystem interface
  cleanup_clickfs();

  // extra packages, global handlers
  click_cleanup_packages();
  Router::static_cleanup();
  
  // config manager, thread manager, sk_buff manager
  click_cleanup_config();
  click_cleanup_sched();
  skbmgr_cleanup();

  cp_va_static_cleanup();

  // error handlers
  ErrorHandler::static_cleanup();
  delete click_logged_errh;
  delete syslog_errh;
  click_logged_errh = syslog_errh = 0;
  
  printk("<1>click module exiting\n");

  // HashMap
  HashMap_ArenaFactory::static_cleanup();
  
  // String (after any operations that might destroy Strings)
  NameInfo::static_cleanup();
  String::static_cleanup();

  // report memory leaks
  if (Element::nelements_allocated)
    printk("<1>click error: %d elements still allocated\n", Element::nelements_allocated);
  if (click_dmalloc_curnew) {
    printk("<1>click error: %d outstanding news\n", click_dmalloc_curnew);
    click_dmalloc_cleanup();
  }
#ifdef HAVE_LINUX_READ_NET_SKBCOUNT
  printk("<1>net_skbcount: %d\n", read_net_skbcount());
#endif
}

#ifdef MODULE_LICENSE
MODULE_LICENSE("Dual BSD/GPL");
#endif


syntax highlighted by Code2HTML, v. 0.9.1