diff --git a/xwords4/android/XWords4/res/xml/xwprefs.xml b/xwords4/android/XWords4/res/xml/xwprefs.xml index d0872209d..44a98ef7d 100644 --- a/xwords4/android/XWords4/res/xml/xwprefs.xml +++ b/xwords4/android/XWords4/res/xml/xwprefs.xml @@ -168,13 +168,13 @@ android:defaultValue="false" /> - - - - - - - + gi->players[newPlayer]))) { PerTurnInfo* newInfo = &board->pti[newPlayer]; XP_U16 oldPlayer = board->selPlayer; model_foreachPendingCell( board->model, newPlayer, diff --git a/xwords4/common/comms.c b/xwords4/common/comms.c index 8d961cc8a..100160050 100644 --- a/xwords4/common/comms.c +++ b/xwords4/common/comms.c @@ -284,7 +284,9 @@ comms_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isServer, MPASSIGN(result->mpool, mpool); result->isServer = isServer; - XP_MEMCPY( &result->procs, procs, sizeof(result->procs) ); + if ( !!procs ) { + XP_MEMCPY( &result->procs, procs, sizeof(result->procs) ); + } result->util = util; #ifdef XWFEATURE_RELAY @@ -1308,9 +1310,13 @@ relayPreProcess( CommsCtxt* comms, XWStreamCtxt* stream, XWHostID* senderID ) cookieID, srcID, destID ); /* If these values don't check out, drop it */ - XP_ASSERT( COMMS_RELAYSTATE_ALLCONNECTED == comms->r.relayState - || COMMS_RELAYSTATE_CONNECTED == comms->r.relayState - || COMMS_RELAYSTATE_RECONNECTED == comms->r.relayState ); + /* When a message comes in via proxy (rather than a connection) state + may not be as expected. Just commenting these out is probably the + wrong fix. Maybe instead the constructor takes a flag that means + "assume you're connected" Revisit this. */ + /* XP_ASSERT( COMMS_RELAYSTATE_ALLCONNECTED == comms->r.relayState */ + /* || COMMS_RELAYSTATE_CONNECTED == comms->r.relayState */ + /* || COMMS_RELAYSTATE_RECONNECTED == comms->r.relayState ); */ if ( destID == comms->r.myHostID ) { /* When would this not happen? */ consumed = XP_FALSE; diff --git a/xwords4/common/game.c b/xwords4/common/game.c index 1ab0b26d5..0ee1ed425 100644 --- a/xwords4/common/game.c +++ b/xwords4/common/game.c @@ -229,7 +229,9 @@ game_makeFromStream( MPFORMAL XWStreamCtxt* stream, XWGame* game, gi->nPlayers ); server_prefsChanged( game->server, cp ); board_prefsChanged( game->board, cp ); - draw_dictChanged( draw, dict ); + if ( !!draw ) { + draw_dictChanged( draw, dict ); + } success = XP_TRUE; } while( XP_FALSE ); } diff --git a/xwords4/dawg/.gitignore b/xwords4/dawg/.gitignore index 82f214b31..4935774d4 100644 --- a/xwords4/dawg/.gitignore +++ b/xwords4/dawg/.gitignore @@ -2,5 +2,6 @@ *.xwd *.pdb *.stamp +*.dict *.dict.gz dict2dawg diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 34ed7f8c0..673692333 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -1458,12 +1458,12 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) g_globals.cGlobals.socketChangedClosure = &g_globals; g_globals.cGlobals.addAcceptor = curses_socket_acceptor; - g_globals.cp.showBoardArrow = XP_TRUE; - g_globals.cp.showRobotScores = params->showRobotScores; - g_globals.cp.hideTileValues = params->hideValues; + g_globals.cGlobals.cp.showBoardArrow = XP_TRUE; + g_globals.cGlobals.cp.showRobotScores = params->showRobotScores; + g_globals.cGlobals.cp.hideTileValues = params->hideValues; #ifdef XWFEATURE_SLOW_ROBOT - g_globals.cp.robotThinkMin = params->robotThinkMin; - g_globals.cp.robotThinkMax = params->robotThinkMax; + g_globals.cGlobals.cp.robotThinkMin = params->robotThinkMin; + g_globals.cGlobals.cp.robotThinkMax = params->robotThinkMax; #endif dict = params->dict; @@ -1490,136 +1490,141 @@ cursesmain( XP_Bool isServer, LaunchParams* params ) struct sigaction act2 = { .sa_handler = SIGWINCH_handler }; sigaction( SIGWINCH, &act2, NULL ); - initCurses( &g_globals ); - getmaxyx( g_globals.boardWin, height, width ); + if ( !!params->pipe && !!params->fileName ) { + read_pipe_then_close( &g_globals.cGlobals ); + } else { - g_globals.draw = (struct CursesDrawCtx*) - cursesDrawCtxtMake( g_globals.boardWin ); + initCurses( &g_globals ); + getmaxyx( g_globals.boardWin, height, width ); - TransportProcs procs = { - .closure = &g_globals, - .send = LINUX_SEND, + g_globals.draw = (struct CursesDrawCtx*) + cursesDrawCtxtMake( g_globals.boardWin ); + + TransportProcs procs = { + .closure = &g_globals, + .send = LINUX_SEND, #ifdef COMMS_HEARTBEAT - .reset = linux_reset, + .reset = linux_reset, #endif #ifdef XWFEATURE_RELAY - .rstatus = relay_status_curses, - .rconnd = relay_connd_curses, - .rerror = relay_error_curses, + .rstatus = relay_status_curses, + .rconnd = relay_connd_curses, + .rerror = relay_error_curses, #endif - }; + }; - if ( !!params->fileName && file_exists( params->fileName ) ) { - XWStreamCtxt* stream; - stream = streamFromFile( &g_globals.cGlobals, params->fileName, - &g_globals ); + if ( !!params->fileName && file_exists( params->fileName ) ) { + XWStreamCtxt* stream; + stream = streamFromFile( &g_globals.cGlobals, params->fileName, + &g_globals ); - (void)game_makeFromStream( MEMPOOL stream, &g_globals.cGlobals.game, - ¶ms->gi, dict, params->util, - (DrawCtx*)g_globals.draw, - &g_globals.cp, &procs ); + (void)game_makeFromStream( MEMPOOL stream, &g_globals.cGlobals.game, + ¶ms->gi, dict, params->util, + (DrawCtx*)g_globals.draw, + &g_globals.cGlobals.cp, &procs ); - stream_destroy( stream ); + stream_destroy( stream ); - if ( !isServer && params->gi.serverRole == SERVER_ISSERVER ) { - isServer = XP_TRUE; + if ( !isServer && params->gi.serverRole == SERVER_ISSERVER ) { + isServer = XP_TRUE; + } + } else { + game_makeNewGame( MEMPOOL &g_globals.cGlobals.game, ¶ms->gi, + params->util, (DrawCtx*)g_globals.draw, + &g_globals.cGlobals.cp, &procs ); } - } else { - game_makeNewGame( MEMPOOL &g_globals.cGlobals.game, ¶ms->gi, - params->util, (DrawCtx*)g_globals.draw, - &g_globals.cp, &procs ); - } #ifndef XWFEATURE_STANDALONE_ONLY - if ( g_globals.cGlobals.game.comms ) { - CommsAddrRec addr = {0}; + if ( g_globals.cGlobals.game.comms ) { + CommsAddrRec addr = {0}; - if ( 0 ) { + if ( 0 ) { # ifdef XWFEATURE_RELAY - } else if ( params->conType == COMMS_CONN_RELAY ) { - addr.conType = COMMS_CONN_RELAY; - addr.u.ip_relay.ipAddr = 0; /* ??? */ - addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort; - addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom; - addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom; - XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName, - sizeof(addr.u.ip_relay.hostName) - 1 ); - XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite, - sizeof(addr.u.ip_relay.invite) - 1 ); + } else if ( params->conType == COMMS_CONN_RELAY ) { + addr.conType = COMMS_CONN_RELAY; + addr.u.ip_relay.ipAddr = 0; /* ??? */ + addr.u.ip_relay.port = params->connInfo.relay.defaultSendPort; + addr.u.ip_relay.seeksPublicRoom = params->connInfo.relay.seeksPublicRoom; + addr.u.ip_relay.advertiseRoom = params->connInfo.relay.advertiseRoom; + XP_STRNCPY( addr.u.ip_relay.hostName, params->connInfo.relay.relayName, + sizeof(addr.u.ip_relay.hostName) - 1 ); + XP_STRNCPY( addr.u.ip_relay.invite, params->connInfo.relay.invite, + sizeof(addr.u.ip_relay.invite) - 1 ); # endif # ifdef XWFEATURE_SMS - } else if ( params->conType == COMMS_CONN_SMS ) { - addr.conType = COMMS_CONN_SMS; - XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.serverPhone, - sizeof(addr.u.sms.phone) - 1 ); - addr.u.sms.port = params->connInfo.sms.port; + } else if ( params->conType == COMMS_CONN_SMS ) { + addr.conType = COMMS_CONN_SMS; + XP_STRNCPY( addr.u.sms.phone, params->connInfo.sms.serverPhone, + sizeof(addr.u.sms.phone) - 1 ); + addr.u.sms.port = params->connInfo.sms.port; # endif # ifdef XWFEATURE_BLUETOOTH - } else if ( params->conType == COMMS_CONN_BT ) { - addr.conType = COMMS_CONN_BT; - XP_ASSERT( sizeof(addr.u.bt.btAddr) - >= sizeof(params->connInfo.bt.hostAddr)); - XP_MEMCPY( &addr.u.bt.btAddr, ¶ms->connInfo.bt.hostAddr, - sizeof(params->connInfo.bt.hostAddr) ); + } else if ( params->conType == COMMS_CONN_BT ) { + addr.conType = COMMS_CONN_BT; + XP_ASSERT( sizeof(addr.u.bt.btAddr) + >= sizeof(params->connInfo.bt.hostAddr)); + XP_MEMCPY( &addr.u.bt.btAddr, ¶ms->connInfo.bt.hostAddr, + sizeof(params->connInfo.bt.hostAddr) ); # endif + } + comms_setAddr( g_globals.cGlobals.game.comms, &addr ); } - comms_setAddr( g_globals.cGlobals.game.comms, &addr ); - } #endif - model_setDictionary( g_globals.cGlobals.game.model, params->dict ); + model_setDictionary( g_globals.cGlobals.game.model, params->dict ); - positionSizeStuff( &g_globals, width, height ); + positionSizeStuff( &g_globals, width, height ); #ifndef XWFEATURE_STANDALONE_ONLY - /* send any events that need to get off before the event loop begins */ - if ( !isServer ) { - if ( 1 /* stream_open( params->info.clientInfo.stream ) */) { - server_initClientConnection( g_globals.cGlobals.game.server, - mem_stream_make( MEMPOOL - params->vtMgr, - &g_globals, - (XP_PlayerAddr)0, - sendOnClose ) ); - } else { - cursesUserError( &g_globals, "Unable to open connection to server"); - exit( 0 ); + /* send any events that need to get off before the event loop begins */ + if ( !isServer ) { + if ( 1 /* stream_open( params->info.clientInfo.stream ) */) { + server_initClientConnection( g_globals.cGlobals.game.server, + mem_stream_make( MEMPOOL + params->vtMgr, + &g_globals, + (XP_PlayerAddr)0, + sendOnClose ) ); + } else { + cursesUserError( &g_globals, "Unable to open connection to server"); + exit( 0 ); + } } - } #endif - server_do( g_globals.cGlobals.game.server ); + server_do( g_globals.cGlobals.game.server ); - g_globals.menuList = g_boardMenuList; - drawMenuLargeOrSmall( &g_globals, g_boardMenuList ); - board_draw( g_globals.cGlobals.game.board ); + g_globals.menuList = g_boardMenuList; + drawMenuLargeOrSmall( &g_globals, g_boardMenuList ); + board_draw( g_globals.cGlobals.game.board ); - while ( !g_globals.timeToExit ) { - int ch = 0; - if ( blocking_gotEvent( &g_globals, &ch ) ) { - remapKey( &ch ); - if ( + while ( !g_globals.timeToExit ) { + int ch = 0; + if ( blocking_gotEvent( &g_globals, &ch ) ) { + remapKey( &ch ); + if ( #ifdef CURSES_SMALL_SCREEN - handleKeyEvent( &g_globals, g_rootMenuListShow, ch ) || + handleKeyEvent( &g_globals, g_rootMenuListShow, ch ) || #endif - handleKeyEvent( &g_globals, g_globals.menuList, ch ) - || handleKeyEvent( &g_globals, g_sharedMenuList, ch ) - || passKeyToBoard( &g_globals, ch ) ) { - if ( g_globals.doDraw ) { - board_draw( g_globals.cGlobals.game.board ); - g_globals.doDraw = XP_FALSE; + handleKeyEvent( &g_globals, g_globals.menuList, ch ) + || handleKeyEvent( &g_globals, g_sharedMenuList, ch ) + || passKeyToBoard( &g_globals, ch ) ) { + if ( g_globals.doDraw ) { + board_draw( g_globals.cGlobals.game.board ); + g_globals.doDraw = XP_FALSE; + } } } } - } + } if ( !!g_globals.cGlobals.params->fileName ) { XWStreamCtxt* outStream; - outStream = mem_stream_make( - MPPARM(g_globals.cGlobals.params->util->mpool) - g_globals.cGlobals.params->vtMgr, - &g_globals.cGlobals, 0, writeToFile ); + outStream = + mem_stream_make( MPPARM(g_globals.cGlobals.params->util->mpool) + g_globals.cGlobals.params->vtMgr, + &g_globals.cGlobals, 0, writeToFile ); stream_open( outStream ); game_saveToStream( &g_globals.cGlobals.game, diff --git a/xwords4/linux/cursesmain.h b/xwords4/linux/cursesmain.h index fe9984265..62b05506d 100644 --- a/xwords4/linux/cursesmain.h +++ b/xwords4/linux/cursesmain.h @@ -58,7 +58,6 @@ struct CursesAppGlobals { DictionaryCtxt* dictionary; EngineCtxt* engine; - CommonPrefs cp; XP_Bool amServer; /* this process acting as server */ diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index 2a3a485e8..83530042e 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -39,6 +39,8 @@ #endif #include #include +#include +#include #include "main.h" #include "linuxmain.h" @@ -419,7 +421,7 @@ createOrLoadObjects( GtkAppGlobals* globals ) &globals->cGlobals.params->gi, params->dict, params->util, (DrawCtx*)globals->draw, - &globals->cp, &procs ); + &globals->cGlobals.cp, &procs ); stream_destroy( stream ); } @@ -440,7 +442,7 @@ createOrLoadObjects( GtkAppGlobals* globals ) game_makeNewGame( MEMPOOL &globals->cGlobals.game, ¶ms->gi, params->util, (DrawCtx*)globals->draw, - &globals->cp, &procs ); + &globals->cGlobals.cp, &procs ); addr.conType = params->conType; if ( 0 ) { @@ -782,7 +784,7 @@ new_game_impl( GtkAppGlobals* globals, XP_Bool fireConnDlg ) game_reset( MEMPOOL &globals->cGlobals.game, gi, globals->cGlobals.params->util, - &globals->cp, &procs ); + &globals->cGlobals.cp, &procs ); #ifndef XWFEATURE_STANDALONE_ONLY if ( !!globals->cGlobals.game.comms ) { @@ -1878,7 +1880,7 @@ newConnectionInput( GIOChannel *source, GIOCondition condition, gpointer data ) { - gboolean keepSource; + gboolean keepSource = TRUE; int sock = g_io_channel_unix_get_fd( source ); GtkAppGlobals* globals = (GtkAppGlobals*)data; @@ -1886,25 +1888,7 @@ newConnectionInput( GIOChannel *source, /* XP_ASSERT( sock == globals->cGlobals.socket ); */ - if ( (condition & (G_IO_HUP | G_IO_ERR)) != 0 ) { - XP_LOGF( "dropping socket %d", sock ); - close( sock ); -#ifdef XWFEATURE_RELAY - globals->cGlobals.socket = -1; -#endif - if ( 0 ) { -#ifdef XWFEATURE_BLUETOOTH - } else if ( COMMS_CONN_BT == globals->cGlobals.params->conType ) { - linux_bt_socketclosed( &globals->cGlobals, sock ); -#endif -#ifdef XWFEATURE_IP_DIRECT - } else if ( COMMS_CONN_IP_DIRECT == globals->cGlobals.params->conType ) { - linux_udp_socketclosed( &globals->cGlobals, sock ); -#endif - } - keepSource = FALSE; /* remove the event source */ - - } else if ( (condition & G_IO_IN) != 0 ) { + if ( (condition & G_IO_IN) != 0 ) { ssize_t nRead; unsigned char buf[512]; CommsAddrRec* addrp = NULL; @@ -1972,8 +1956,27 @@ newConnectionInput( GIOChannel *source, } else { XP_LOGF( "errno from read: %d/%s", errno, strerror(errno) ); } - keepSource = TRUE; } + + if ( (condition & (G_IO_HUP | G_IO_ERR)) != 0 ) { + XP_LOGF( "dropping socket %d", sock ); + close( sock ); +#ifdef XWFEATURE_RELAY + globals->cGlobals.socket = -1; +#endif + if ( 0 ) { +#ifdef XWFEATURE_BLUETOOTH + } else if ( COMMS_CONN_BT == globals->cGlobals.params->conType ) { + linux_bt_socketclosed( &globals->cGlobals, sock ); +#endif +#ifdef XWFEATURE_IP_DIRECT + } else if ( COMMS_CONN_IP_DIRECT == globals->cGlobals.params->conType ) { + linux_udp_socketclosed( &globals->cGlobals, sock ); +#endif + } + keepSource = FALSE; /* remove the event source */ + } + return keepSource; /* FALSE means to remove event source */ } /* newConnectionInput */ @@ -2144,16 +2147,16 @@ gtkmain( LaunchParams* params, int argc, char *argv[] ) globals.cGlobals.addAcceptor = gtk_socket_acceptor; #endif - globals.cp.showBoardArrow = XP_TRUE; - globals.cp.hideTileValues = params->hideValues; - globals.cp.skipCommitConfirm = params->skipCommitConfirm; - globals.cp.sortNewTiles = params->sortNewTiles; - globals.cp.showColors = params->showColors; - globals.cp.allowPeek = params->allowPeek; - globals.cp.showRobotScores = params->showRobotScores; + globals.cGlobals.cp.showBoardArrow = XP_TRUE; + globals.cGlobals.cp.hideTileValues = params->hideValues; + globals.cGlobals.cp.skipCommitConfirm = params->skipCommitConfirm; + globals.cGlobals.cp.sortNewTiles = params->sortNewTiles; + globals.cGlobals.cp.showColors = params->showColors; + globals.cGlobals.cp.allowPeek = params->allowPeek; + globals.cGlobals.cp.showRobotScores = params->showRobotScores; #ifdef XWFEATURE_SLOW_ROBOT - globals.cp.robotThinkMin = params->robotThinkMin; - globals.cp.robotThinkMax = params->robotThinkMax; + globals.cGlobals.cp.robotThinkMin = params->robotThinkMin; + globals.cGlobals.cp.robotThinkMax = params->robotThinkMax; #endif setupGtkUtilCallbacks( &globals, params->util ); @@ -2259,11 +2262,14 @@ gtkmain( LaunchParams* params, int argc, char *argv[] ) /* | GDK_POINTER_MOTION_HINT_MASK */ ); - gtk_widget_show( window ); + if ( !!params->pipe && !!params->fileName ) { + read_pipe_then_close( &globals.cGlobals ); + } else { + gtk_widget_show( window ); - gtk_main(); - -/* MONCONTROL(1); */ + gtk_main(); + } + /* MONCONTROL(1); */ cleanup( &globals ); diff --git a/xwords4/linux/gtkmain.h b/xwords4/linux/gtkmain.h index cfb02563a..eb5f9c931 100644 --- a/xwords4/linux/gtkmain.h +++ b/xwords4/linux/gtkmain.h @@ -119,8 +119,6 @@ typedef struct GtkAppGlobals { XP_UCHAR stateChar; #endif - CommonPrefs cp; - XP_Bool gridOn; XP_Bool dropIncommingMsgs; XP_Bool mouseDown; diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 2dbed7ddf..776eb5aab 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -185,6 +185,57 @@ strFromStream( XWStreamCtxt* stream ) return buf; } /* strFromStream */ +void +read_pipe_then_close( CommonGlobals* cGlobals ) +{ + LaunchParams* params = cGlobals->params; + XWStreamCtxt* stream = + streamFromFile( cGlobals, params->fileName, cGlobals ); + + XP_Bool opened = game_makeFromStream( MPPARM(cGlobals->params->util->mpool) + stream, &cGlobals->game, + ¶ms->gi, + params->dict, params->util, + NULL /*draw*/, + &cGlobals->cp, NULL ); + XP_ASSERT( opened ); + stream_destroy( stream ); + + XP_Bool handled = XP_FALSE; + int fd = open( params->pipe, O_RDONLY ); + while ( fd >= 0 ) { + unsigned short len; + ssize_t nRead = blocking_read( fd, (unsigned char*)&len, sizeof(len) ); + if ( nRead != 2 ) { + break; + } + len = ntohs( len ); + unsigned char buf[len]; + nRead = blocking_read( fd, buf, len ); + if ( nRead != len ) { + break; + } + stream = mem_stream_make( MPPARM(cGlobals->params->util->mpool) + params->vtMgr, cGlobals, CHANNEL_NONE, NULL ); + stream_putBytes( stream, buf, len ); + + if ( comms_checkIncomingStream( cGlobals->game.comms, + stream, NULL ) ) { + handled = server_receiveMessage( cGlobals->game.server, + stream ) || handled; + } + stream_destroy( stream ); + } + LOG_RETURNF( "%d", handled ); + + /* Write it out */ + /* stream = mem_stream_make( MEMPOOLCG(cGlobals) params->vtMgr, */ + /* cGlobals, 0, writeToFile ); */ + /* stream_open( stream ); */ + /* game_saveToStream( &cGlobals->game, ¶ms->gi, stream ); */ + /* stream_destroy( stream ); */ +} /* read_pipe_then_close */ + typedef enum { CMD_SKIP_GAMEOVER ,CMD_SHOW_OTHERSCORES @@ -216,6 +267,8 @@ typedef enum { ,CMD_HIDEVALUES ,CMD_SKIPCONFIRM ,CMD_VERTICALSCORE + ,CMD_NOPEEK + ,CMD_ADDPIPE #ifdef XWFEATURE_SEARCHLIMIT ,CMD_HINTRECT #endif @@ -283,6 +336,8 @@ static CmdInfoRec CmdInfoRecs[] = { ,{ CMD_HIDEVALUES, false, "hide-values", "show letters, not nums, on tiles" } ,{ CMD_SKIPCONFIRM, false, "skip-confirm", "don't confirm before commit" } ,{ CMD_VERTICALSCORE, false, "vertical", "scoreboard is vertical" } + ,{ CMD_NOPEEK, false, "no-peek", "disallow scoreboard tap changing player" } + ,{ CMD_ADDPIPE, true, "with-pipe", "named pipe to listen on for relay msgs" } #ifdef XWFEATURE_SEARCHLIMIT ,{ CMD_HINTRECT, false, "hintrect", "enable draggable hint-limits rect" } #endif @@ -581,13 +636,28 @@ linux_close_socket( CommonGlobals* cGlobals ) close( socket ); } +int +blocking_read( int fd, unsigned char* buf, int len ) +{ + int nRead = 0; + while ( nRead < len ) { + ssize_t siz = read( fd, buf + nRead, len - nRead ); + if ( siz <= 0 ) { + nRead = -1; + break; + } + nRead += siz; + } + return nRead; +} + int linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize ) { int sock = cGlobals->socket; unsigned short tmp; unsigned short packetSize; - ssize_t nRead = recv( sock, &tmp, sizeof(tmp), 0 ); + ssize_t nRead = blocking_read( sock, (unsigned char*)&tmp, sizeof(tmp) ); if ( nRead != 2 ) { XP_LOGF( "recv => %d, errno=%d (\"%s\")", nRead, errno, strerror(errno) ); linux_close_socket( cGlobals ); @@ -597,7 +667,7 @@ linux_relay_receive( CommonGlobals* cGlobals, unsigned char* buf, int bufSize ) packetSize = ntohs( tmp ); assert( packetSize <= bufSize ); - nRead = recv( sock, buf, packetSize, 0 ); + nRead = blocking_read( sock, buf, packetSize ); if ( nRead < 0 ) { XP_WARNF( "%s: errno=%d (\"%s\")\n", __func__, errno, strerror(errno) ); @@ -1044,10 +1114,15 @@ main( int argc, char** argv ) case CMD_VERTICALSCORE: mainParams.verticalScore = XP_TRUE; break; + case CMD_NOPEEK: + mainParams.allowPeek = XP_FALSE; + case CMD_ADDPIPE: + mainParams.pipe = optarg; + break; #ifdef XWFEATURE_SLOW_ROBOT case CMD_SLOWROBOT: if ( !parsePair( optarg, &mainParams.robotThinkMin, - &mainParams.robotThinkMax ) ) { + &mainParams.robotThinkMax ) ) { usage(argv[0], "bad param" ); } break; diff --git a/xwords4/linux/linuxmain.h b/xwords4/linux/linuxmain.h index a14a9de62..b9ae258d2 100644 --- a/xwords4/linux/linuxmain.h +++ b/xwords4/linux/linuxmain.h @@ -64,6 +64,8 @@ XWStreamCtxt* streamFromFile( CommonGlobals* cGlobals, char* name, void* closure ); void writeToFile( XWStreamCtxt* stream, void* closure ); +int blocking_read( int fd, unsigned char* buf, int len ); + void linux_close_socket( CommonGlobals* cGlobals ); #ifdef KEYBOARD_NAV @@ -72,4 +74,6 @@ XP_Bool linShiftFocus( CommonGlobals* cGlobals, XP_Key key, BoardObjectType* nxtP ); #endif +void read_pipe_then_close( CommonGlobals* cGlobals ); + #endif diff --git a/xwords4/linux/main.h b/xwords4/linux/main.h index b7dabf5e2..fcef37a60 100644 --- a/xwords4/linux/main.h +++ b/xwords4/linux/main.h @@ -48,6 +48,7 @@ typedef struct LaunchParams { DictionaryCtxt* dict; CurGameInfo gi; char* fileName; + char* pipe; VTableMgr* vtMgr; XP_U16 nLocalPlayers; XP_U16 nHidden; @@ -136,6 +137,7 @@ typedef struct _TimerInfo { struct CommonGlobals { LaunchParams* params; + CommonPrefs cp; XWGame game; XP_U16 lastNTilesToUse; diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 55b213fc7..6565c8806 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -128,12 +128,14 @@ launch() { close_device() { ID=$1 MVTO=$2 + REASON="$3" if [ ${PIDS[$ID]} -ne 0 ]; then kill ${PIDS[$ID]} 2>/dev/null wait ${PIDS[$ID]} fi unset PIDS[$ID] unset CMDS[$ID] + echo "closing game: $REASON" >> ${LOGS[$ID]} if [ -n "$MVTO" ]; then [ -f ${FILES[$ID]} ] && mv ${FILES[$ID]} $MVTO mv ${LOGS[$ID]} $MVTO @@ -178,7 +180,7 @@ maybe_resign() { if grep -q XWRELAY_ALLHERE $LOG; then if [ 0 -eq $(($RANDOM % $RESIGN_RATIO)) ]; then echo "making $LOG $(connName $LOG) resign..." - kill_from_log $LOG && close_device $KEY $DEADDIR + kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced" fi fi fi @@ -213,13 +215,13 @@ check_game() { for ID in $OTHERS $KEY; do echo -n "${LOGS[$ID]}, " kill_from_log ${LOGS[$ID]} || true - close_device $ID $DONEDIR + close_device $ID $DONEDIR "game over" done date elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then echo "deleting $LOG $(connName $LOG) b/c another resigned" kill_from_log $LOG || true - close_device $KEY $DEADDIR + close_device $KEY $DEADDIR "other resigned" else maybe_resign $KEY fi @@ -239,7 +241,7 @@ run_cmds() { PIDS[$KEY]=$! else sleep 2 # make sure it's had some time - kill ${PIDS[$KEY]} + kill ${PIDS[$KEY]} || true PIDS[$KEY]=0 check_game $KEY fi @@ -250,7 +252,7 @@ run_cmds() { mkdir -p ${LOGDIR}/not_done echo "processing unfinished games...." for KEY in ${!CMDS[*]}; do - close_device $KEY ${LOGDIR}/not_done + close_device $KEY ${LOGDIR}/not_done "unfinished game" done fi } @@ -266,4 +268,10 @@ run_cmds print_stats wait -echo "*********$0 finished: $(date) (took $(($(date +%s)-$STARTTIME)) seconds)**************" + +SECONDS=$(($(date +%s)-$STARTTIME)) +HOURS=$((SECONDS/3600)) +SECONDS=$((SECONDS%3600)) +MINUTES=$((SECONDS/60)) +SECONDS=$((SECONDS%60)) +echo "*********$0 finished: $(date) (took $HOURS:$MINUTES:$SECONDS)**************" diff --git a/xwords4/relay/Makefile b/xwords4/relay/Makefile index 7d4e94827..1ea89de5f 100644 --- a/xwords4/relay/Makefile +++ b/xwords4/relay/Makefile @@ -41,9 +41,10 @@ OBJ = $(patsubst %.cpp,%.o,$(SRC)) LDFLAGS += -pthread -g $(STATIC) \ -L$(shell pg_config --libdir) -lpq -CPPFLAGS += -DSPAWN_SELF -DDO_HTTP -g -Wall \ +CPPFLAGS += -DSPAWN_SELF -g -Wall \ -I $(shell pg_config --includedir) \ -DSVN_REV=\"$(shell cat $(GITINFO) 2>/dev/null || echo -n $(HASH) )\" +# CPPFLAGS += -DDO_HTTP # turn on semaphore debugging # CPPFLAGS += -DDEBUG_LOCKS diff --git a/xwords4/relay/dbmgr.cpp b/xwords4/relay/dbmgr.cpp index 6fb537e15..6676526b1 100644 --- a/xwords4/relay/dbmgr.cpp +++ b/xwords4/relay/dbmgr.cpp @@ -434,14 +434,15 @@ DBMgr::StoreMessage( const char* const connName, int hid, const unsigned char* buf, int len ) { size_t newLen; - const char* fmt = "INSERT INTO " MSGS_TABLE " (connname, hid, msg)" - " VALUES( '%s', %d, E'%s')"; + const char* fmt = "INSERT INTO " MSGS_TABLE " (connname, hid, msg, msglen)" + " VALUES( '%s', %d, E'%s', %d)"; unsigned char* bytes = PQescapeByteaConn( getThreadConn(), buf, len, &newLen ); assert( NULL != bytes ); char query[newLen+128]; - unsigned int siz = snprintf( query, sizeof(query), fmt, connName, hid, bytes ); + unsigned int siz = snprintf( query, sizeof(query), fmt, connName, hid, + bytes, len ); logf( XW_LOGINFO, "%s: query: %s", __func__, query ); PQfreemem( bytes ); assert( siz < sizeof(query) ); @@ -450,13 +451,14 @@ DBMgr::StoreMessage( const char* const connName, int hid, } bool -DBMgr::GetStoredMessage( const char* const connName, int hid, - unsigned char* buf, size_t* buflen, int* msgID ) +DBMgr::GetNthStoredMessage( const char* const connName, int hid, + int nn, unsigned char* buf, size_t* buflen, + int* msgID ) { - const char* fmt = "SELECT id, msg FROM " MSGS_TABLE - " WHERE connName = '%s' AND hid = %d ORDER BY id LIMIT 1"; + const char* fmt = "SELECT id, msg, msglen FROM " MSGS_TABLE + " WHERE connName = '%s' AND hid = %d ORDER BY id LIMIT 1 OFFSET %d"; char query[256]; - snprintf( query, sizeof(query), fmt, connName, hid ); + snprintf( query, sizeof(query), fmt, connName, hid, nn ); logf( XW_LOGINFO, "%s: query: %s", __func__, query ); PGresult* result = PQexec( getThreadConn(), query ); @@ -465,7 +467,10 @@ DBMgr::GetStoredMessage( const char* const connName, int hid, bool found = nTuples == 1; if ( found ) { - *msgID = atoi( PQgetvalue( result, 0, 0 ) ); + if ( NULL != msgID ) { + *msgID = atoi( PQgetvalue( result, 0, 0 ) ); + } + size_t msglen = atoi( PQgetvalue( result, 0, 2 ) ); /* int len = PQgetlength( result, 0, 1 ); */ const unsigned char* from = @@ -476,11 +481,19 @@ DBMgr::GetStoredMessage( const char* const connName, int hid, memcpy( buf, bytes, to_length ); PQfreemem( bytes ); *buflen = to_length; + assert( 0 == msglen || to_length == msglen ); } PQclear( result ); return found; } +bool +DBMgr::GetStoredMessage( const char* const connName, int hid, + unsigned char* buf, size_t* buflen, int* msgID ) +{ + return GetNthStoredMessage( connName, hid, 0, buf, buflen, msgID ); +} + void DBMgr::RemoveStoredMessage( int msgID ) { diff --git a/xwords4/relay/dbmgr.h b/xwords4/relay/dbmgr.h index 196b13420..03b5975be 100644 --- a/xwords4/relay/dbmgr.h +++ b/xwords4/relay/dbmgr.h @@ -76,6 +76,8 @@ class DBMgr { const unsigned char* const buf, int len ); bool GetStoredMessage( const char* const connName, int hid, unsigned char* buf, size_t* buflen, int* msgID ); + bool GetNthStoredMessage( const char* const connName, int hid, int nn, + unsigned char* buf, size_t* buflen, int* msgID ); void RemoveStoredMessage( int msgID ); private: diff --git a/xwords4/relay/rq.c b/xwords4/relay/rq.c index 53afeea87..1999349b5 100644 --- a/xwords4/relay/rq.c +++ b/xwords4/relay/rq.c @@ -54,7 +54,8 @@ usage( const char * const argv0 ) fprintf( stderr, "usage: %s \\\n", argv0 ); fprintf( stderr, "\t[-p ] # (default %d)\\\n", DEFAULT_PORT ); fprintf( stderr, "\t[-a ] # (default: %s)\\\n", DEFAULT_HOST ); - fprintf( stderr, "\t -r # list open public rooms\\\n" ); + fprintf( stderr, "\t[-r] # list open public rooms\\\n" ); + fprintf( stderr, "\t[-f # fetch message > stdout\\\n" ); fprintf( stderr, "\t[-l ] # language for rooms " "(1=English default)\\\n" ); fprintf( stderr, "\t[-n ] # number of players (2 default)\\\n" ); @@ -118,7 +119,8 @@ do_rooms( int sockfd, int lang, int nPlayers ) } static void -do_msgs( int sockfd, const char** connNames, int nConnNames ) +write_connnames( int sockfd, char cmd, + const char** connNames, int nConnNames ) { unsigned short len, netlen; int ii; @@ -126,7 +128,7 @@ do_msgs( int sockfd, const char** connNames, int nConnNames ) len += 1 + strlen( connNames[ii] ); } - unsigned char hdr[] = { 0, PRX_HAS_MSGS }; + unsigned char hdr[] = { 0, cmd }; unsigned short netNConnNames = htons( nConnNames ); netlen = sizeof(hdr) + sizeof( netNConnNames ) + len; netlen = htons( netlen ); @@ -137,6 +139,12 @@ do_msgs( int sockfd, const char** connNames, int nConnNames ) write( sockfd, connNames[ii], strlen(connNames[ii]) ); write( sockfd, "\n", 1 ); } +} + +static void +do_msgs( int sockfd, const char** connNames, int nConnNames ) +{ + write_connnames( sockfd, PRX_HAS_MSGS, connNames, nConnNames ); fprintf( stderr, "Waiting for response....\n" ); unsigned char reply[1024]; @@ -159,9 +167,57 @@ do_msgs( int sockfd, const char** connNames, int nConnNames ) fprintf( stdout, "%s -- %d\n", connNames[ii], count ); } } - } /* do_msgs */ +static void +do_fetch( int sockfd, const char** connNames, int nConnNames ) +{ + assert( 1 == nConnNames ); + write_connnames( sockfd, PRX_GET_MSGS, connNames, nConnNames ); + + fprintf( stderr, "Waiting for response....\n" ); + unsigned char reply[1024]; + int nRead = read_packet( sockfd, reply, sizeof(reply) ); + if ( nRead > 2 ) { + const unsigned char* bufp = reply; + const unsigned char* const end = bufp + nRead; + + unsigned short count; + memcpy( &count, bufp, sizeof( count ) ); + bufp += sizeof( count ); + count = ntohs( count ); + assert( count == nConnNames ); + fprintf( stderr, "got count: %d\n", count ); + + /* Now we have an array of pairs. Just + write em as long as it makes sense. countPerDev makes no sense as + other than 1 unless the UI is changed so I don't have to write to + STDOUT -- e.g. by passing in named pipes to correspond to each + deviceid provided */ + + while ( bufp < end ) { + unsigned short countPerDev; + memcpy( &countPerDev, bufp, sizeof( countPerDev ) ); + bufp += sizeof( countPerDev ); + countPerDev = ntohs( countPerDev ); + + while ( bufp < end && countPerDev-- > 0 ) { + unsigned short len; + memcpy( &len, bufp, sizeof( len ) ); + len = ntohs( len ) + sizeof( len ); + if ( bufp + len > end ) { + break; + } + write( STDOUT_FILENO, bufp, len ); + bufp += len; + } + } + if ( bufp != end ) { + fprintf( stderr, "error: message not internally as expected\n" ); + } + } +} + static void do_deletes( int sockfd, const char** connNames, int nConnNames ) { @@ -217,12 +273,13 @@ main( int argc, char * const argv[] ) bool doRooms = false; bool doMgs = false; bool doDeletes = false; + bool doFetch = false; const char* host = DEFAULT_HOST; char const* connNames[MAX_CONN_NAMES]; int nConnNames = 0; for ( ; ; ) { - int opt = getopt( argc, argv, "a:d:p:rl:n:m:" ); + int opt = getopt( argc, argv, "a:d:f:p:rl:n:m:" ); if ( opt < 0 ) { break; } @@ -239,6 +296,10 @@ main( int argc, char * const argv[] ) connNames[nConnNames++] = optarg; doDeletes = true; break; + case 'f': + connNames[nConnNames++] = optarg; + doFetch = true; + break; case 'l': lang = atoi(optarg); break; @@ -294,6 +355,12 @@ main( int argc, char * const argv[] ) if ( doDeletes ) { do_deletes( sockfd, connNames, nConnNames ); } + if ( doFetch ) { + if ( nConnNames != 1 ) { + usage( argv[0] ); + } + do_fetch( sockfd, connNames, nConnNames ); + } close( sockfd ); diff --git a/xwords4/relay/tpool.cpp b/xwords4/relay/tpool.cpp index 4f3744123..16514707b 100644 --- a/xwords4/relay/tpool.cpp +++ b/xwords4/relay/tpool.cpp @@ -104,19 +104,19 @@ XWThreadPool::Stop() int ii; for ( ii = 0; ii < m_nThreads; ++ii ) { - enqueue( 0 ); + enqueue( 0, STYPE_UNKNOWN ); } interrupt_poll(); } void -XWThreadPool::AddSocket( int socket ) +XWThreadPool::AddSocket( int socket, SockType stype ) { logf( XW_LOGINFO, "%s(%d)", __func__, socket ); { RWWriteLock ml( &m_activeSocketsRWLock ); - m_activeSockets.push_back( socket ); + m_activeSockets.push_back( pair(socket, stype) ); } interrupt_poll(); } @@ -128,9 +128,9 @@ XWThreadPool::RemoveSocket( int socket ) { RWWriteLock ml( &m_activeSocketsRWLock ); - vector::iterator iter = m_activeSockets.begin(); + vector< pair >::iterator iter = m_activeSockets.begin(); while ( iter != m_activeSockets.end() ) { - if ( *iter == socket ) { + if ( iter->first == socket ) { m_activeSockets.erase( iter ); found = true; break; @@ -171,23 +171,27 @@ XWThreadPool::CloseSocket( int socket ) void XWThreadPool::EnqueueKill( int socket, const char* const why ) { - enqueue( socket, Q_KILL ); + enqueue( socket, STYPE_UNKNOWN, Q_KILL ); } bool -XWThreadPool::get_process_packet( int socket ) +XWThreadPool::get_process_packet( int socket, SockType stype ) { bool success = false; short packetSize; assert( sizeof(packetSize) == 2 ); - unsigned char buf[MAX_MSG_LEN]; + unsigned char buf[MAX_MSG_LEN+1]; int nRead = read_packet( socket, buf, sizeof(buf) ); if ( nRead < 0 ) { EnqueueKill( socket, "bad packet" ); - } else { + } else if ( STYPE_GAME == stype ) { logf( XW_LOGINFO, "calling m_pFunc" ); success = (*m_pFunc)( buf, nRead, socket ); + } else { + buf[nRead] = '\0'; + handle_proxy_packet( buf, nRead, socket ); + CloseSocket( socket ); } return success; } /* get_process_packet */ @@ -230,8 +234,8 @@ XWThreadPool::real_tpool_main() pr.m_socket ); switch ( pr.m_act ) { case Q_READ: - if ( get_process_packet( pr.m_socket ) ) { - AddSocket( pr.m_socket ); + if ( get_process_packet( pr.m_socket, pr.m_type ) ) { + AddSocket( pr.m_socket, pr.m_type ); } break; case Q_KILL: @@ -267,6 +271,7 @@ XWThreadPool::real_listener() int nSocketsAllocd = 1; struct pollfd* fds = (pollfd*)calloc( nSocketsAllocd, sizeof(fds[0]) ); + SockType* stypes = (SockType*)calloc( nSocketsAllocd, sizeof(stypes[0]) ); #ifdef LOG_POLL char* log = (char*)malloc( 4 * nSocketsAllocd ); #endif @@ -282,31 +287,35 @@ XWThreadPool::real_listener() if ( nSockets > nSocketsAllocd ) { fds = (struct pollfd*)realloc( fds, nSockets * sizeof(fds[0]) ); + stypes = (SockType*)realloc( stypes, nSockets * sizeof(stypes[0]) ); #ifdef LOG_POLL log = (char*)realloc( log, nSockets * 4 ); #endif nSocketsAllocd = nSockets; } - struct pollfd* curfd = fds; + int curfd = 0; - curfd->fd = m_pipeRead; - curfd->events = flags; + fds[curfd].fd = m_pipeRead; + fds[curfd].events = flags; #ifdef LOG_POLL - logLen += snprintf( log+logLen, logCapacity - logLen, "%d,", curfd->fd ); + logLen += snprintf( log+logLen, logCapacity - logLen, "%d,", + fds[curfd].fd ); #endif ++curfd; - vector::iterator iter = m_activeSockets.begin(); - while ( iter != m_activeSockets.end() ) { - curfd->fd = *iter++; - curfd->events = flags; + vector< pair >::iterator iter; + for ( iter = m_activeSockets.begin(); iter != m_activeSockets.end(); + ++iter ) { + fds[curfd].fd = iter->first; + stypes[curfd] = iter->second; + fds[curfd].events = flags; #ifdef LOG_POLL if ( logCapacity > logLen ) { logLen += snprintf( log+logLen, logCapacity - logLen, "%d,", - curfd->fd ); + fds[curfd].fd ); } #endif - assert( curfd < fds + nSockets ); + assert( curfd < nSockets ); ++curfd; } pthread_rwlock_unlock( &m_activeSocketsRWLock ); @@ -343,13 +352,13 @@ XWThreadPool::real_listener() if ( nEvents > 0 ) { --nSockets; - curfd = &fds[1]; + curfd = 1; int ii; for ( ii = 0; ii < nSockets && nEvents > 0; ++ii ) { - if ( curfd->revents != 0 ) { - int socket = curfd->fd; + if ( fds[curfd].revents != 0 ) { + int socket = fds[curfd].fd; if ( !RemoveSocket( socket ) ) { /* no further processing if it's been removed while we've been sleeping in poll */ @@ -357,11 +366,12 @@ XWThreadPool::real_listener() continue; } - if ( 0 != (curfd->revents & (POLLIN | POLLPRI)) ) { + if ( 0 != (fds[curfd].revents & (POLLIN | POLLPRI)) ) { logf( XW_LOGINFO, "enqueuing %d", socket ); - enqueue( socket ); + enqueue( socket, stypes[curfd] ); } else { - logf( XW_LOGERROR, "odd revents: %x", curfd->revents ); + logf( XW_LOGERROR, "odd revents: %x", + fds[curfd].revents ); EnqueueKill( socket, "error/hup in poll()" ); } --nEvents; @@ -386,10 +396,10 @@ XWThreadPool::listener_main( void* closure ) } void -XWThreadPool::enqueue( int socket, QAction act ) +XWThreadPool::enqueue( int socket, SockType stype, QAction act ) { MutexLock ml( &m_queueMutex ); - QueuePr pr = { act, socket }; + QueuePr pr = { act, socket, stype }; m_queue.push_back( pr ); logf( XW_LOGINFO, "calling pthread_cond_signal" ); diff --git a/xwords4/relay/tpool.h b/xwords4/relay/tpool.h index 5b2e6a16e..353b32d58 100644 --- a/xwords4/relay/tpool.h +++ b/xwords4/relay/tpool.h @@ -35,6 +35,8 @@ using namespace std; class XWThreadPool { public: + typedef enum { STYPE_UNKNOWN, STYPE_GAME, STYPE_PROXY } SockType; + static XWThreadPool* GetTPool(); typedef bool (*packet_func)( unsigned char* buf, int bufLen, int socket ); typedef void (*kill_func)( int socket ); @@ -46,7 +48,7 @@ class XWThreadPool { void Stop(); /* Add to set being listened on */ - void AddSocket( int socket ); + void AddSocket( int socket, SockType stype ); /* remove from tpool altogether, and close */ void CloseSocket( int socket ); @@ -54,17 +56,18 @@ class XWThreadPool { private: typedef enum { Q_READ, Q_KILL } QAction; - typedef struct { QAction m_act; int m_socket; } QueuePr; + typedef struct { QAction m_act; int m_socket; SockType m_type; } QueuePr; /* Remove from set being listened on */ bool RemoveSocket( int socket ); void enqueue( int socket, QAction act = Q_READ ); + void enqueue( int socket, SockType stype, QAction act = Q_READ ); void release_socket_locked( int socket ); void grab_elem_locked( QueuePr* qpp ); void print_in_use( void ); - bool get_process_packet( int socket ); + bool get_process_packet( int socket, SockType stype ); void interrupt_poll(); void* real_tpool_main(); @@ -74,7 +77,7 @@ class XWThreadPool { static void* listener_main( void* closure ); /* Sockets main thread listens on */ - vector m_activeSockets; + vector< pair >m_activeSockets; pthread_rwlock_t m_activeSocketsRWLock; /* Sockets waiting for a thread to read 'em */ diff --git a/xwords4/relay/xwrelay.conf_tmplate b/xwords4/relay/xwrelay.conf_tmplate index 5c62759a2..3daa1b19d 100644 --- a/xwords4/relay/xwrelay.conf_tmplate +++ b/xwords4/relay/xwrelay.conf_tmplate @@ -12,8 +12,10 @@ HEARTBEAT=60 # Default -- if not set -- is an infinite timeout. # ALLCONN=300 -# How many worker threads in the thread pool? Default is five. -NTHREADS=3 +# How many worker threads in the thread pool? Default is five. Let's +# keep this at 1 until the race condition is fixed. All interaction +# with crefs should be from this one thread, including proxy stuff. +NTHREADS=1 # How many seconds to wait for device to ack new connName DEVACK=3 diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 9181675fa..7da9f14e2 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -77,8 +77,6 @@ #include "dbmgr.h" static int s_nSpawns = 0; -#define MAX_PROXY_LEN 64 -#define MAX_PROXY_COUNT 48 void logf( XW_LogLevel level, const char* format, ... ) @@ -193,7 +191,8 @@ parseRelayID( const char* const in, char* buf, HostID* hid ) } static bool -getNetShort( unsigned char** bufpp, unsigned char* end, unsigned short* out ) +getNetShort( unsigned char** bufpp, const unsigned char* end, + unsigned short* out ) { bool ok = *bufpp + 2 <= end; if ( ok ) { @@ -206,7 +205,8 @@ getNetShort( unsigned char** bufpp, unsigned char* end, unsigned short* out ) } /* getNetShort */ static bool -getNetByte( unsigned char** bufpp, unsigned char* end, unsigned char* out ) +getNetByte( unsigned char** bufpp, const unsigned char* end, + unsigned char* out ) { bool ok = *bufpp < end; if ( ok ) { @@ -683,7 +683,7 @@ read_packet( int sock, unsigned char* buf, int buflen ) nread = recv( sock, &msgLen, sizeof(msgLen), MSG_WAITALL ); if ( nread == sizeof(msgLen) ) { msgLen = ntohs( msgLen ); - if ( msgLen <= buflen ) { + if ( msgLen < buflen ) { nread = recv( sock, buf, msgLen, MSG_WAITALL ); if ( nread == msgLen ) { result = nread; @@ -693,16 +693,82 @@ read_packet( int sock, unsigned char* buf, int buflen ) return result; } -static void* -handle_proxy_tproc( void* closure ) +static void +pushShort( vector& out, unsigned short num ) { - blockSignals(); - int sock = (int)closure; + num = htons( num ); + out.insert( out.end(), (unsigned char*)&num, ((unsigned char*)&num) + 2 ); +} - unsigned char buf[MAX_PROXY_MSGLEN]; - int len = read_packet( sock, buf, sizeof(buf)-1 ); +static void +pushMsgs( vector& out, DBMgr* dbmgr, const char* connName, + HostID hid, int msgCount ) +{ + int ii; + for ( ii = 0; ii < msgCount; ++ii ) { + unsigned char buf[1024]; + size_t buflen = sizeof(buf); + if ( !dbmgr->GetNthStoredMessage( connName, hid, ii, buf, + &buflen, NULL ) ) { + logf( XW_LOGERROR, "%s: %dth message not there", __func__, ii ); + break; + } + pushShort( out, buflen ); + out.insert( out.end(), buf, buf + buflen ); + } +} + +static void +handleMsgsMsg( int sock, bool sendFull, + unsigned char* bufp, const unsigned char* end ) +{ + unsigned short nameCount; + if ( getNetShort( &bufp, end, &nameCount ) ) { + char* in = (char*)bufp; + DBMgr* dbmgr = DBMgr::Get(); + unsigned short count; + /* This is wrong now */ + /* reply format: PRX_GET_MSGS case: []* + * PRX_HAS_MSGS case: * + */ + vector out(4); /* space for len and n_msgs */ + assert( out.size() == 4 ); + + char* saveptr; + for ( count = 0; ; ++count ) { + char* name = strtok_r( in, "\n", &saveptr ); + if ( NULL == name ) { + break; + } + HostID hid; + char connName[MAX_CONNNAME_LEN+1]; + if ( !parseRelayID( name, connName, &hid ) ) { + break; + } + + /* For each relayID, write the number of messages and then each + message (in the getmsg case) */ + int msgCount = dbmgr->PendingMsgCount( connName, hid ); + pushShort( out, msgCount ); + if ( sendFull ) { + pushMsgs( out, dbmgr, connName, hid, msgCount ); + } + + in = NULL; + } + + unsigned short tmp = htons( out.size() - sizeof(tmp) ); + memcpy( &out[0], &tmp, sizeof(tmp) ); + tmp = htons( count ); + memcpy( &out[2], &tmp, sizeof(tmp) ); + write( sock, &out[0], out.size() ); + } +} + +void +handle_proxy_packet( unsigned char* buf, int len, int sock ) +{ if ( len > 0 ) { - buf[len] = '\0'; /* so can use strtok */ unsigned char* bufp = buf; unsigned char* end = bufp + len; if ( (0 == *bufp++) ) { /* protocol */ @@ -729,42 +795,12 @@ handle_proxy_tproc( void* closure ) } break; case PRX_HAS_MSGS: + case PRX_GET_MSGS: if ( len >= 2 ) { - unsigned short nameCount; - if ( getNetShort( &bufp, end, &nameCount ) ) { - char* in = (char*)bufp; - char* saveptr; - vector ids; - for ( ; ; ) { - char* name = strtok_r( in, "\n", &saveptr ); - if ( NULL == name ) { - break; - } - HostID hid; - char connName[MAX_CONNNAME_LEN+1]; - if ( parseRelayID( name, connName, &hid ) ) { - ids.push_back( DBMgr::Get()-> - PendingMsgCount( connName, hid ) ); - } - in = NULL; - } - - unsigned short len = - (ids.size() * sizeof(unsigned short)) - + sizeof( unsigned short ); - len = htons( len ); - write( sock, &len, sizeof(len) ); - len = htons( nameCount ); - write( sock, &len, sizeof(len) ); - vector::const_iterator iter; - for ( iter = ids.begin(); iter != ids.end(); ++iter ) { - unsigned short num = *iter; - num = htons( num ); - write( sock, &num, sizeof(num) ); - } - } + handleMsgsMsg( sock, PRX_GET_MSGS == cmd, bufp, end ); } - break; + break; /* PRX_HAS_MSGS */ + case PRX_DEVICE_GONE: logf( XW_LOGINFO, "%s: got PRX_DEVICE_GONE", __func__ ); if ( len >= 2 ) { @@ -799,24 +835,11 @@ handle_proxy_tproc( void* closure ) } len = 0; /* return a 0-length message */ write( sock, &len, sizeof(len) ); - break; + break; /* PRX_DEVICE_GONE */ } } } - sleep( 2 ); - close( sock ); - return NULL; -} /* handle_proxy_tproc */ - -static void -handle_proxy_connect( int sock ) -{ - pthread_t thread; - if ( 0 == pthread_create( &thread, NULL, handle_proxy_tproc, - (void*)sock ) ) { - pthread_detach( thread ); - } -} /* handle_proxy_connect */ +} /* handle_proxy_packet */ int main( int argc, char** argv ) @@ -839,17 +862,13 @@ main( int argc, char** argv ) /* Verify sizes here... */ assert( sizeof(CookieID) == 2 ); - /* Read options. Options trump config file values when they conflict, but the name of the config file is an option so we have to get that first. */ for ( ; ; ) { - int opt = getopt(argc, argv, "h?c:p:n:i:f:l:t:" -#ifdef DO_HTTP - "w:s:" -#endif + int opt = getopt(argc, argv, "h?c:p:n:i:f:l:t:s:w:" "DF" ); if ( opt == -1 ) { @@ -870,6 +889,11 @@ main( int argc, char** argv ) case 's': cssFile = optarg; break; +#else + case 'w': + case 's': + fprintf( stderr, "option -%c disabled and ignored\n", opt ); + break; #endif case 'D': doDaemon = false; @@ -1120,15 +1144,14 @@ main( int argc, char** argv ) int newSock = accept( listener, (sockaddr*)&newaddr, &siz ); - if ( perGame ) { - logf( XW_LOGINFO, - "accepting connection from %s on socket %d", - inet_ntoa(newaddr.sin_addr), newSock ); + logf( XW_LOGINFO, + "accepting connection from %s on socket %d", + inet_ntoa(newaddr.sin_addr), newSock ); + + tPool->AddSocket( newSock, + perGame ? XWThreadPool::STYPE_GAME + : XWThreadPool::STYPE_PROXY ); - tPool->AddSocket( newSock ); - } else { - handle_proxy_connect( newSock ); - } --retval; } } diff --git a/xwords4/relay/xwrelay.h b/xwords4/relay/xwrelay.h index fa2c45f7e..1b726b04d 100644 --- a/xwords4/relay/xwrelay.h +++ b/xwords4/relay/xwrelay.h @@ -154,6 +154,7 @@ enum { PRX_NONE /* 0 is an illegal value */ ,PRX_PUB_ROOMS /* list all public rooms for lang/nPlayers */ ,PRX_HAS_MSGS /* return message counts for connName/devid array */ ,PRX_DEVICE_GONE /* return message counts for connName/devid array */ + ,PRX_GET_MSGS /* return full messages for connName/devid array */ } #ifndef CANT_DO_TYPEDEF XWPRXYCMD diff --git a/xwords4/relay/xwrelay.sh b/xwords4/relay/xwrelay.sh index 48644b47c..1116ffda4 100755 --- a/xwords4/relay/xwrelay.sh +++ b/xwords4/relay/xwrelay.sh @@ -54,6 +54,7 @@ id SERIAL ,hid INTEGER ,ctime TIMESTAMP DEFAULT CURRENT_TIMESTAMP ,msg BYTEA +,msglen INTEGER ,UNIQUE ( connName, hid, msg ) ); EOF diff --git a/xwords4/relay/xwrelay_priv.h b/xwords4/relay/xwrelay_priv.h index 44f989967..5226609a2 100644 --- a/xwords4/relay/xwrelay_priv.h +++ b/xwords4/relay/xwrelay_priv.h @@ -30,6 +30,7 @@ int GetNSpawns(void); int make_socket( unsigned long addr, unsigned short port ); int read_packet( int sock, unsigned char* buf, int buflen ); +void handle_proxy_packet( unsigned char* buf, int bufLen, int socket ); const char* cmdToStr( XWRELAY_Cmd cmd );