trema/trema-edge

View on GitHub
src/lib/trema.c

Summary

Maintainability
Test Coverage
/*
 * Author: Yasuhito Takamiya <yasuhito@gmail.com>
 *
 * Copyright (C) 2008-2013 NEC Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License, version 2, as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */


#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "trema.h"
#include "daemon.h"
#include "doubly_linked_list.h"
#include "log.h"
#include "messenger.h"
#include "openflow_application_interface.h"
#include "packetin_filter_interface.h"
#include "timer.h"
#include "trema_private.h"
#include "utility.h"
#include "wrapper.h"

const char DEFAULT_DUMP_SERVICE_NAME[] = "dump_service";


#ifdef UNIT_TESTING

#ifdef init_log
#undef init_log
#endif
#define init_log mock_init_log
bool mock_init_log( const char *ident, const char *log_directory, logging_type type );

#ifdef error
#undef error
#endif
#define error mock_error
void mock_error( const char *format, ... );

#ifdef set_logging_level
#undef set_logging_level
#endif
#define set_logging_level mock_set_logging_level
bool mock_set_logging_level( const char *level );

#ifdef daemonize
#undef daemonize
#endif
#define daemonize mock_daemonize
void mock_daemonize( const char *home );

#ifdef write_pid
#undef write_pid
#endif
#define write_pid mock_write_pid
void mock_write_pid( const char *directory, const char *name );

#ifdef unlink_pid
#undef unlink_pid
#endif
#define unlink_pid mock_unlink_pid
void mock_unlink_pid( const char *directory, const char *name );

#ifdef rename_pid
#undef rename_pid
#endif
#define rename_pid mock_rename_pid
void mock_rename_pid( const char *directory, const char *old, const char *new );

#ifdef read_pid
#undef read_pid
#endif
#define read_pid mock_read_pid
pid_t mock_read_pid( const char *directory, const char *name );

#ifdef kill
#undef kill
#endif
#define kill mock_kill
int mock_kill( pid_t pid, int sig );

#ifdef sleep
#undef sleep
#endif
#define sleep mock_sleep
unsigned int mock_sleep( unsigned int seconds );

#ifdef init_event_handler
#undef init_event_handler
#endif
#define init_event_handler mock_init_event_handler
void mock_init_event_handler( void );

#ifdef start_event_handler
#undef start_event_handler
#endif
#define start_event_handler mock_start_event_handler
void mock_start_event_handler( void );

#ifdef stop_event_handler
#undef stop_event_handler
#endif
#define stop_event_handler mock_stop_event_handler
void mock_stop_event_handler( void );

#ifdef init_messenger
#undef init_messenger
#endif
#define init_messenger mock_init_messenger
void mock_init_messenger( const char *working_directory );

#ifdef start_messenger
#undef start_messenger
#endif
#define start_messenger mock_start_messenger
void mock_start_messenger( void );

#ifdef flush_messenger
#undef flush_messenger
#endif
#define flush_messenger mock_flush_messenger
void mock_flush_messenger( void );

#ifdef stop_messenger
#undef stop_messenger
#endif
#define stop_messenger mock_stop_messenger
void mock_stop_messenger( void );

#ifdef finalize_messenger
#undef finalize_messenger
#endif
#define finalize_messenger mock_finalize_messenger
void mock_finalize_messenger( void );

#ifdef start_messenger_dump
#undef start_messenger_dump
#endif
#define start_messenger_dump mock_start_messenger_dump
void mock_start_messenger_dump( const char *dump_app_name, const char *dump_service_name );

#ifdef stop_messenger_dump
#undef stop_messenger_dump
#endif
#define stop_messenger_dump mock_stop_messenger_dump
void mock_stop_messenger_dump();

#ifdef messenger_dump_enabled
#undef messenger_dump_enabled
#endif
#define messenger_dump_enabled mock_messenger_dump_enabled
bool mock_messenger_dump_enabled();

#ifdef die
#undef die
#endif
#define die mock_die
void mock_die( const char *format, ... );

#ifdef exit
#undef exit
#endif
#define exit mock_exit
void mock_exit( int status );

#ifdef openflow_application_interface_is_initialized
#undef openflow_application_interface_is_initialized
#endif
#define openflow_application_interface_is_initialized mock_openflow_application_interface_is_initialized
bool mock_openflow_application_interface_is_initialized( void );

#ifdef finalize_openflow_application_interface
#undef finalize_openflow_application_interface
#endif
#define finalize_openflow_application_interface mock_finalize_openflow_application_interface
bool mock_finalize_openflow_application_interface( void );

#ifdef printf
#undef printf
#endif
#define printf mock_printf
int mock_printf(const char *format, ...);

typedef struct stat _stat;
#ifdef stat
#undef stat
#endif
#define stat mock_stat
int mock_stat( const char *path, _stat *buf );

#ifdef debug
#undef debug
#endif
#define debug mock_debug
void mock_debug( const char *format, ... );

#ifdef init_stat
#undef init_stat
#endif
#define init_stat mock_init_stat
bool mock_init_stat();

#ifdef finalize_stat
#undef finalize_stat
#endif
#define finalize_stat mock_finalize_stat
bool mock_finalize_stat();

#ifdef init_timer
#undef init_timer
#endif
#define init_timer mock_init_timer
bool mock_init_timer();

#ifdef finalize_timer
#undef finalize_timer
#endif
#define finalize_timer mock_finalize_timer
bool mock_finalize_timer();

#ifdef set_external_callback
#undef set_external_callback
#endif
#define set_external_callback mock_set_external_callback
bool mock_set_external_callback( void ( *callback ) ( void ) );

#ifdef dump_stats
#undef dump_stats
#endif
#define dump_stats mock_dump_stats
void mock_dump_stats();

#ifdef finalize_packetin_filter_interface
#undef finalize_packetin_filter_interface
#endif
#define finalize_packetin_filter_interface mock_finalize_packetin_filter_interface
bool mock_finalize_packetin_filter_interface();

#define static

#endif // UNIT_TESTING


#ifndef UNIT_TESTING
#define _stat struct stat
#endif // not UNIT_TESTING


static bool initialized = false;
static bool trema_started = false;
static bool run_as_daemon = false;
static int use_exit_handler = 1;
static int use_hup_handler = 1;
static int use_usr1_handler = 1;
static int use_usr2_handler = 1;
static char *trema_name = NULL;
static char *executable_name = NULL;
static char *trema_log = NULL;
static char *trema_pid = NULL;
static char *trema_sock = NULL;
static logging_type log_output_type = LOGGING_TYPE_FILE;
static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;


static struct option long_options[] = {
  { "name", 1, NULL, 'n' },
  { "daemonize", 0, NULL, 'd' },
  { "logging_level", 1, NULL, 'l' },
  { "no_exit_handler", 0, &use_exit_handler, 0 },
  { "no_hup_handler", 0, &use_hup_handler, 0 },
  { "no_usr1_handler", 0, &use_usr1_handler, 0 },
  { "no_usr2_handler", 0, &use_usr2_handler, 0 },
  { "syslog", 0, NULL, 'g' },
  { "help", 0, NULL, 'h' },
  { NULL, 0, NULL, 0 },
};
static char short_options[] = "n:dl:gh";


/**
 * Default usage function shown on <tt>-h</tt> or <tt>--help</tt>.
 *
 * This can be overridden by defining your own <tt>void usage( void )</tt>
 * in your Trema application.
 */
void
usage() {
  printf(
    "Usage: %s [OPTION]...\n"
    "\n"
    "  -n, --name=SERVICE_NAME     service name\n"
    "  -d, --daemonize             run in the background\n"
    "  -l, --logging_level=LEVEL   set logging level\n"
    "  -g, --syslog                output log messages to syslog\n"
    "  -h, --help                  display this help and exit\n",
    executable_name
  );
}


static const char *
get_trema_log() {
  if ( trema_log == NULL ) {
    char path[ PATH_MAX ];
    sprintf( path, "%s/log", get_trema_tmp() );
    trema_log = xstrdup( path );
  }
  return trema_log;
}


static const char *
get_trema_pid() {
  if ( trema_pid == NULL ) {
    char path[ PATH_MAX ];
    sprintf( path, "%s/pid", get_trema_tmp() );
    trema_pid = xstrdup( path );
  }
  return trema_pid;
}


static const char *
get_trema_sock() {
  if ( trema_sock == NULL ) {
    char path[ PATH_MAX ];
    sprintf( path, "%s/sock", get_trema_tmp() );
    trema_sock = xstrdup( path );
  }
  return trema_sock;
}


static void
maybe_finalize_openflow_application_interface() {
  if ( openflow_application_interface_is_initialized() ) {
    finalize_openflow_application_interface();
  }
}


static void
die_unless_initialized() {
  if ( !initialized ) {
    die( "Trema is not initialized. Call init_trema() first." );
  }
}


static void
finalize_trema() {
  die_unless_initialized();

  debug( "Terminating %s...", get_trema_name() );

  maybe_finalize_openflow_application_interface();
  finalize_packetin_filter_interface();
  finalize_messenger();
  finalize_stat();
  finalize_timer();
  trema_started = false;
  unlink_pid( get_trema_pid(), get_trema_name() );
  xfree( trema_name );
  trema_name = NULL;
  xfree( executable_name );
  executable_name = NULL;

  unset_trema_home();
  unset_trema_tmp();

  xfree( trema_log );
  trema_log = NULL;

  initialized = false;
}


static void
check_trema_tmp() {
  _stat st;
  int ret = stat( get_trema_tmp(), &st );
  if ( ( ret != 0 ) && ( errno == ENOENT ) ) {
    die( "Trema temporary directory does not exist: %s", get_trema_tmp() );
  }
}


static void
reset_getopt() {
  optind = 0;
  opterr = 1;
}


static void
parse_argv( int *argc, char ***argv ) {
  assert( argc != NULL );
  assert( argv != NULL );

  int argc_tmp = *argc;
  char *new_argv[ *argc ];

  run_as_daemon = false;

  set_trema_name( basename( ( *argv )[ 0 ] ) );
  executable_name = xstrdup( get_trema_name() );

  for ( int i = 0; i < *argc; ++i ) {
    new_argv[ i ] = ( *argv )[ i ];
  }

  for ( ;; ) {
    opterr = 0;
    int c = getopt_long( *argc, *argv, short_options, long_options, NULL );

    if ( c == -1 ) {
      break;
    }

    switch ( c ) {
      case 'n':
        set_trema_name( optarg );
        break;
      case 'd':
        run_as_daemon = true;
        break;
      case 'l':
        set_logging_level( optarg );
        break;
      case 'g':
        log_output_type = LOGGING_TYPE_SYSLOG;
        break;
      case 'h':
        usage();
        xfree( trema_name );
        xfree( executable_name );
        exit( EXIT_SUCCESS );
        break;
      default:
        continue;
    }

    if ( optarg == NULL || strchr( new_argv[ optind - 1 ], '=' ) != NULL ) {
      argc_tmp -= 1;
      new_argv[ optind - 1 ] = NULL;
    }
    else {
      argc_tmp -= 2;
      new_argv[ optind - 1 ] = NULL;
      new_argv[ optind - 2 ] = NULL;
    }
  }

  for ( int i = 0, j = 0; i < *argc; ++i ) {
    if ( new_argv[ i ] != NULL ) {
      ( *argv )[ j ] = new_argv[ i ];
      j++;
    }
  }
  if ( argc_tmp < *argc ) {
    ( *argv )[ argc_tmp ] = NULL;
  }
  *argc = argc_tmp;

  reset_getopt();
}


static void
ignore_sigpipe() {
  struct sigaction sigpipe_ignore;

  memset( &sigpipe_ignore, 0, sizeof( struct sigaction ) );
  sigpipe_ignore.sa_handler = SIG_IGN;
  sigaction( SIGPIPE, &sigpipe_ignore, NULL );
}


static void
set_exit_handler() {
  struct sigaction signal_exit;

  memset( &signal_exit, 0, sizeof( struct sigaction ) );
  signal_exit.sa_handler = ( void * ) stop_trema;
  sigaction( SIGINT, &signal_exit, NULL );
  sigaction( SIGTERM, &signal_exit, NULL );
}


static void
do_restart_log() {
  restart_log( NULL );
}


static void
set_do_restart_log_as_external_callback() {
  set_external_callback( do_restart_log );
}


static void
set_hup_handler() {
  struct sigaction signal_hup;

  memset( &signal_hup, 0, sizeof( struct sigaction ) );
  signal_hup.sa_handler = ( void * ) set_do_restart_log_as_external_callback;
  sigaction( SIGHUP, &signal_hup, NULL );
}


static void
set_dump_stats_as_external_callback() {
  set_external_callback( dump_stats );
}


static void
set_usr1_handler() {
  struct sigaction signal_usr1;

  memset( &signal_usr1, 0, sizeof( struct sigaction ) );
  signal_usr1.sa_handler = ( void * ) set_dump_stats_as_external_callback;
  sigaction( SIGUSR1, &signal_usr1, NULL );
}


static void
toggle_messenger_dump() {
  if ( messenger_dump_enabled() ) {
    stop_messenger_dump();
  }
  else {
    start_messenger_dump( get_trema_name(), DEFAULT_DUMP_SERVICE_NAME );
  }
}


static void
set_usr2_handler() {
  struct sigaction signal_usr2;

  memset( &signal_usr2, 0, sizeof( struct sigaction ) );
  signal_usr2.sa_handler = ( void * ) toggle_messenger_dump;
  sigaction( SIGUSR2, &signal_usr2, NULL );
}


static void
maybe_daemonize() {
  if ( run_as_daemon ) {
    daemonize( get_trema_home() );
  }
}


/**
 * Call this function before using any other Trema functions in your
 * applications.
 *
 * It will initialize everything needed to handle OpenFlow events and
 * parses some standard command line options. <tt>argc</tt> and
 * <tt>argv</tt> are adjusted accordingly so your own code will never
 * see those standard arguments.
 */
void
init_trema( int *argc, char ***argv ) {
  assert( argc != NULL );
  assert( argv != NULL );

  pthread_mutex_lock( &mutex );

  trema_name = NULL;
  trema_log = NULL;
  executable_name = NULL;
  initialized = false;
  trema_started = false;
  run_as_daemon = false;
  log_output_type = LOGGING_TYPE_FILE;

  parse_argv( argc, argv );
  set_trema_home();
  set_trema_tmp();
  check_trema_tmp();
  if ( !run_as_daemon ) {
    log_output_type |= LOGGING_TYPE_STDOUT;
  }
  init_log( get_trema_name(), get_trema_log(), log_output_type );
  ignore_sigpipe();
  if ( use_exit_handler ) {
    set_exit_handler();
  }
  if ( use_hup_handler ) {
    set_hup_handler();
  }
  if ( use_usr1_handler ) {
    set_usr1_handler();
  }
  if ( use_usr2_handler ) {
    set_usr2_handler();
  }
  init_messenger( get_trema_sock() );
  init_stat();
  init_timer();

  initialized = true;

  pthread_mutex_unlock( &mutex );
}


/**
 * Runs the main loop.
 */
void
start_trema() {
  start_trema_up();
  start_event_handler();
  start_trema_down();
}


void
start_trema_up() {
  pthread_mutex_lock( &mutex );

  die_unless_initialized();

  debug( "Starting %s ... (TREMA_HOME = %s)", get_trema_name(), get_trema_home() );

  maybe_daemonize();
  write_pid( get_trema_pid(), get_trema_name() );
  trema_started = true;

  start_messenger();
}


void
start_trema_down() {
  finalize_trema();

  pthread_mutex_unlock( &mutex );
}


void
flush() {
  flush_messenger();
}


/**
 * Exits from the main loop.
 */
void
stop_trema() {
  stop_event_handler();
  stop_messenger();
}


void
set_trema_name( const char *name ) {
  assert( name != NULL );
  if ( trema_name != NULL ) {
    if ( trema_started ) {
      rename_pid( get_trema_pid(), trema_name, name );
      rename_log( name );
    }
    xfree( trema_name );
  }
  trema_name = xstrdup( name );

  if ( initialized ) {
    restart_log( trema_name );
  }
}


/**
 * Returns the ID of your Trema application.
 */
const char *
get_trema_name() {
  assert( trema_name != NULL );
  return trema_name;
}


/**
 * Returns the basename of <tt>argv[ 0 ]</tt>. This function is useful
 * for your application's <tt>usage()</tt> function.
 */
const char *
get_executable_name() {
  assert( executable_name != NULL );
  return executable_name;
}


pid_t
get_trema_process_from_name( const char *name ) {
  return read_pid( get_trema_pid(), name );
}


bool
terminate_trema_process( pid_t pid ) {
  assert( pid > 0 );
  int res = kill( pid, SIGTERM );
  if ( res < 0 ) {
    if ( errno == ESRCH ) {
      return true;
    }
    error( "Failed to kill %d ( %s [%d] ).", pid, strerror( errno ), errno );
    return false;
  }

  int try = 0;
  const int max_try = 10;
  while ( kill( pid, 0 ) == 0 && ++try <= max_try ) {
    sleep( 1 );
  }
  if ( try > max_try ) {
    error( "Failed to terminate %d.", pid );
    return false;
  }
  return true;
}


/*
 * Local variables:
 * c-basic-offset: 2
 * indent-tabs-mode: nil
 * End:
 */