From 0291ec1e75cc24f8b4791369b9c5dcb4c5cbe6b6 Mon Sep 17 00:00:00 2001 From: ehouse Date: Sat, 10 Nov 2007 05:41:49 +0000 Subject: [PATCH] Turn on self-spawning: main loop should never crash but exists only to respawn child when it does. Add crash command to test this. Add ability to set logging level from ctrl port. --- xwords4/relay/Makefile | 2 +- xwords4/relay/configs.cpp | 3 +- xwords4/relay/configs.h | 3 ++ xwords4/relay/ctrl.cpp | 76 +++++++++++++++++++++++++++++++------- xwords4/relay/states.cpp | 27 +++++++++----- xwords4/relay/states.h | 4 +- xwords4/relay/xwrelay.conf | 6 ++- xwords4/relay/xwrelay.cpp | 16 +++++++- 8 files changed, 106 insertions(+), 31 deletions(-) diff --git a/xwords4/relay/Makefile b/xwords4/relay/Makefile index c32614542..938c3c5f4 100644 --- a/xwords4/relay/Makefile +++ b/xwords4/relay/Makefile @@ -28,7 +28,7 @@ SRC = xwrelay.cpp \ OBJ = $(patsubst %.cpp,%.o,$(SRC)) LDFLAGS += -pthread -g -lmcheck -CPPFLAGS += -g -Wall -DSVN_REV=\"$(shell svnversion -n .)\" +CPPFLAGS += -DSPAWN_SELF -g -Wall -DSVN_REV=\"$(shell svnversion -n .)\" # turn on semaphore debugging # CPPFLAGS += -DDEBUG_LOCKS diff --git a/xwords4/relay/configs.cpp b/xwords4/relay/configs.cpp index 61a94a472..cf10a00fd 100644 --- a/xwords4/relay/configs.cpp +++ b/xwords4/relay/configs.cpp @@ -41,7 +41,6 @@ RelayConfigs* RelayConfigs::instance = NULL; /* static */ RelayConfigs* RelayConfigs::GetConfigs() { - assert( instance != NULL ); return instance; } @@ -107,6 +106,8 @@ RelayConfigs::parse( const char* fname ) m_serverName = value; } else if ( 0 == strcmp( line, "IDFILE" ) ) { m_idFileName = value; + } else if ( 0 == strcmp( line, "LOGLEVEL" ) ) { + m_logLevel = atoi(value); } else { logf( XW_LOGERROR, "unknown key %s with value %s\n", line, value ); diff --git a/xwords4/relay/configs.h b/xwords4/relay/configs.h index 7e6acd4f0..19b7009c3 100644 --- a/xwords4/relay/configs.h +++ b/xwords4/relay/configs.h @@ -39,6 +39,8 @@ class RelayConfigs { time_t GetHeartbeatInterval() { return m_heartbeatInterval; } const char* GetServerName() { return m_serverName.c_str(); } const char* GetIdFileName() { return m_idFileName.c_str(); } + int GetLogLevel(void) { return m_logLevel; } + void SetLogLevel(int ll) { m_logLevel = ll; } private: RelayConfigs( const char* cfile ); @@ -48,6 +50,7 @@ class RelayConfigs { time_t m_heartbeatInterval; int m_ctrlport; int m_port; + int m_logLevel; int m_nWorkerThreads; std::string m_serverName; std::string m_idFileName; diff --git a/xwords4/relay/ctrl.cpp b/xwords4/relay/ctrl.cpp index 4efffb173..b285d414e 100644 --- a/xwords4/relay/ctrl.cpp +++ b/xwords4/relay/ctrl.cpp @@ -43,10 +43,12 @@ #include "crefmgr.h" #include "mlock.h" #include "xwrelay_priv.h" +#include "configs.h" /* this is *only* for testing. Don't abuse!!!! */ extern pthread_rwlock_t gCookieMapRWLock; +/* Return of true means exit the ctrl thread */ typedef bool (*CmdPtr)( int socket, const char** args ); typedef struct FuncRec { @@ -70,6 +72,7 @@ static bool cmd_set( int socket, const char** args ); static bool cmd_shutdown( int socket, const char** args ); static bool cmd_rev( int socket, const char** args ); static bool cmd_uptime( int socket, const char** args ); +static bool cmd_crash( int socket, const char** args ); static void print_to_sock( int sock, bool addCR, const char* what, ... ) @@ -102,6 +105,7 @@ static const FuncRec gFuncs[] = { { "set", cmd_set }, { "rev", cmd_rev }, { "uptime", cmd_uptime }, + { "crash", cmd_crash }, }; static bool @@ -110,9 +114,11 @@ cmd_quit( int socket, const char** args ) if ( 0 == strcmp( "help", args[1] ) ) { print_to_sock( socket, true, "* %s (disconnect from ctrl port)", args[0] ); + return false; } else { + print_to_sock( socket, true, "bye bye" ); + return true; } - return 0; } static void @@ -136,14 +142,14 @@ static bool cmd_start( int socket, const char** args ) { print_to_sock( socket, true, "* %s (unimplemented)", args[0] ); - return 1; + return false; } static bool cmd_stop( int socket, const char** args ) { print_to_sock( socket, true, "* %s (unimplemented)", args[0] ); - return 1; + return false; } static bool @@ -185,24 +191,50 @@ cmd_kill_eject( int socket, const char** args ) ; print_to_sock( socket, true, msg, args[0], expl, args[0], args[0] ); } - return 1; + return false; } /* cmd_kill_eject */ static bool cmd_get( int socket, const char** args ) { - print_to_sock( socket, true, - "* %s -- lists all attributes (unimplemented)\n" - "* %s (unimplemented)", - args[0], args[0] ); - return 1; + if ( 0 == strcmp( args[1], "help" ) ) { + print_to_sock( socket, true, + "* %s -- lists all attributes (unimplemented)\n" + "* %s loglevel", + args[0], args[0] ); + } else { + const char* attr = args[1]; + if ( (NULL != attr) && (0 == strcmp( attr, "loglevel" )) ) { + RelayConfigs* rc = RelayConfigs::GetConfigs(); + if ( NULL != rc ) { + print_to_sock( socket, true, "loglevel=%d\n", + rc->GetLogLevel() ); + } else { + logf( XW_LOGERROR, "RelayConfigs::GetConfigs() => NULL" ); + } + } + } + return false; } static bool cmd_set( int socket, const char** args ) { - print_to_sock( socket, true, "* %s (unimplemented)", args[0] ); - return 1; + if ( 0 == strcmp( args[1], "help" ) ) { + print_to_sock( socket, true, "* %s loglevel \n", args[0] ); + } else { + const char* attr = args[1]; + const char* val = args[2]; + if ( (NULL != attr) + && (0 == strcmp( attr, "loglevel" )) + && (NULL != val) ) { + RelayConfigs* rc = RelayConfigs::GetConfigs(); + if ( rc != NULL ) { + rc->SetLogLevel( atoi(val) ); + } + } + } + return false; } static bool @@ -215,7 +247,7 @@ cmd_rev( int socket, const char** args ) } else { print_to_sock( socket, true, "svn rev: %s", SVN_REV ); } - return 0; + return false; } static bool @@ -241,7 +273,23 @@ cmd_uptime( int socket, const char** args ) "uptime: %d days, %d hours, %d minutes, %ld seconds", days, hours, minutes, seconds ); } - return 0; + return false; +} + +static bool +cmd_crash( int socket, const char** args ) +{ + if ( 0 == strcmp( args[1], "help" ) ) { + print_to_sock( socket, true, + "* %s -- fires an assert (debug case) or divides-by-zero", + args[0] ); + } else { + assert(0); + int i = 1; + while ( i > 0 ) --i; + return 6/i > 0; + } + return false; } static bool @@ -250,7 +298,7 @@ cmd_shutdown( int socket, const char** args ) print_to_sock( socket, true, "* %s -- shuts down relay (exiting main) (unimplemented)", args[0] ); - return 1; + return false; } static void diff --git a/xwords4/relay/states.cpp b/xwords4/relay/states.cpp index 0ac9f6ba2..ed002f6df 100644 --- a/xwords4/relay/states.cpp +++ b/xwords4/relay/states.cpp @@ -81,6 +81,10 @@ StateTable g_stateTable[] = { { XWS_CONNECTING, XWE_DISCONNMSG, XWA_DISCONNECT, XWS_CONNECTING }, { XWS_MISSING, XWE_DISCONNMSG, XWA_DISCONNECT, XWS_MISSING }, + /* I'm seeing this but not sure how to handle. Might disconnect be + needed now */ +{ XWS_MISSING, XWE_FORWARDMSG, XWA_DISCONNECT, XWS_MISSING }, + { XWS_ANY, XWE_NOMORESOCKETS, XWA_NONE, XWS_DEAD }, { XWS_ANY, XWE_SHUTDOWN, XWA_SHUTDOWN, XWS_DEAD }, @@ -115,33 +119,36 @@ StateTable g_stateTable[] = { /* This is our bread-n-butter */ { XWS_ALLCONNECTED, XWE_FORWARDMSG, XWA_FWD, XWS_ALLCONNECTED }, -{ XWS_DEAD, XWE_REMOVESOCKET, XWA_REMOVESOCKET, XWS_DEAD }, +{ XWS_DEAD, XWE_REMOVESOCKET, XWA_REMOVESOCKET, XWS_DEAD } - /* Marks end of table */ -{ XWS_NONE, XWE_NONE, XWA_NONE, XWS_NONE } }; -int +bool getFromTable( XW_RELAY_STATE curState, XW_RELAY_EVENT curEvent, XW_RELAY_ACTION* takeAction, XW_RELAY_STATE* nextState ) { + bool found = false; StateTable* stp = g_stateTable; - while ( stp->stateStart != XWS_NONE ) { + const StateTable* end = stp + sizeof(g_stateTable)/sizeof(g_stateTable[0]); + while ( stp < end ) { if ( stp->stateStart == curState || stp->stateStart == XWS_ANY ) { if ( stp->stateEvent == curEvent || stp->stateEvent == XWE_ANY ) { *takeAction = stp->stateAction; *nextState = stp->stateEnd; - return 1; + found = true; + break; } } ++stp; } - logf( XW_LOGERROR, "==> ERROR :: unable to find transition from %s on event %s", - stateString(curState), eventString(curEvent) ); - - return 0; + if ( !found ) { + logf( XW_LOGERROR, "==> ERROR :: unable to find transition from %s " + "on event %s", + stateString(curState), eventString(curEvent) ); + } + return found; } /* getFromTable */ #define CASESTR(s) case s: return #s diff --git a/xwords4/relay/states.h b/xwords4/relay/states.h index 33e64bcf9..817a5b1d9 100644 --- a/xwords4/relay/states.h +++ b/xwords4/relay/states.h @@ -149,8 +149,8 @@ typedef enum { } XW_RELAY_ACTION; -int getFromTable( XW_RELAY_STATE curState, XW_RELAY_EVENT curEvent, - XW_RELAY_ACTION* takeAction, XW_RELAY_STATE* nextState ); +bool getFromTable( XW_RELAY_STATE curState, XW_RELAY_EVENT curEvent, + XW_RELAY_ACTION* takeAction, XW_RELAY_STATE* nextState ); char* stateString( XW_RELAY_STATE state ); diff --git a/xwords4/relay/xwrelay.conf b/xwords4/relay/xwrelay.conf index 812c81a8f..2c67dc2da 100644 --- a/xwords4/relay/xwrelay.conf +++ b/xwords4/relay/xwrelay.conf @@ -26,4 +26,8 @@ SERVERNAME=eehouse.org # Where will the file live that stores the last ID used for a new # connName. -#IDFILE=/home/ehouse/xwrelay_id.txt +IDFILE=/home/eehouse/xwrelay_id.txt + +# Initial level of logging. See xwrelay_priv.h for values. Currently +# 0 means errors only, 1 info, 2 verbose and 3 very verbose. +LOGLEVEL=0 diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 24cf6d6ed..f1792ab8f 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -73,7 +73,8 @@ void logf( XW_LogLevel level, const char* format, ... ) { - if ( level <= XW_LOGINFO ) { + RelayConfigs* rc = RelayConfigs::GetConfigs(); + if ( NULL == rc || level <= rc->GetLogLevel() ) { FILE* where = stderr; struct tm* timp; struct timeval tv; @@ -376,6 +377,16 @@ static int make_socket( unsigned long addr, unsigned short port ) { int sock = socket( AF_INET, SOCK_STREAM, 0 ); + assert( sock ); + + /* We may be relaunching after crashing with sockets open. SO_REUSEADDR + allows them to be immediately rebound. */ + int t = true; + if ( 0 != setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &t, sizeof(t) ) ) { + logf( XW_LOGERROR, "setsockopt failed. errno = %s (%d)\n", + strerror(errno), errno ); + return -1; + } struct sockaddr_in sockAddr; sockAddr.sin_family = AF_INET; @@ -543,6 +554,8 @@ int main( int argc, char** argv ) PermID::SetServerName( serverName ); PermID::SetIDFileName( idFileName ); + /* add signal handling here */ + #ifdef SPAWN_SELF /* loop forever, relaunching children as they die. */ for ( ; ; ) { @@ -554,7 +567,6 @@ int main( int argc, char** argv ) logf( XW_LOGINFO, "parent waiting on child pid=%d", pid ); waitpid( pid, &status, 0 ); printWhy( status ); - sleep( 45 ); /* give time to close sockets? */ } else { logf( XW_LOGERROR, "fork() => %s", strerror(errno) ); }