int main( int argc, char **argv ) { int i, listenSocket[ MAX_PLAYERS ], v, longOpt; int fixedSeats, quiet, append; int seatFD[ MAX_PLAYERS ]; FILE *file, *logFile, *transactionFile; ReadBuf *readBuf[ MAX_PLAYERS ]; Game *game; rng_state_t rng; ErrorInfo errorInfo; struct sockaddr_in addr; socklen_t addrLen; char *seatName[ MAX_PLAYERS ]; int useLogFile, useTransactionFile; uint64_t maxResponseMicros, maxUsedHandMicros, maxUsedPerHandMicros; int64_t startTimeoutMicros; uint32_t numHands, seed, maxInvalidActions; uint16_t listenPort[ MAX_PLAYERS ]; struct timeval startTime, tv; char name[ MAX_LINE_LEN ]; static struct option longOptions[] = { { "t_response", 1, 0, 0 }, { "t_hand", 1, 0, 0 }, { "t_per_hand", 1, 0, 0 }, { "start_timeout", 1, 0, 0 }, { 0, 0, 0, 0 } }; /* set defaults */ /* game error conditions */ maxInvalidActions = DEFAULT_MAX_INVALID_ACTIONS; maxResponseMicros = DEFAULT_MAX_RESPONSE_MICROS; maxUsedHandMicros = DEFAULT_MAX_USED_HAND_MICROS; maxUsedPerHandMicros = DEFAULT_MAX_USED_PER_HAND_MICROS; /* use random ports */ for( i = 0; i < MAX_PLAYERS; ++i ) { listenPort[ i ] = 0; } /* use log file, don't use transaction file */ useLogFile = 1; useTransactionFile = 0; /* print all messages */ quiet = 0; /* by default, overwrite preexisting log/transaction files */ append = 0; /* players rotate around the table */ fixedSeats = 0; /* no timeout on startup */ startTimeoutMicros = -1; /* parse options */ while( 1 ) { i = getopt_long( argc, argv, "flLp:qtTa", longOptions, &longOpt ); if( i < 0 ) { break; } switch( i ) { case 0: /* long option longOpt */ switch( longOpt ) { case 0: /* t_response */ if( sscanf( optarg, "%"SCNu64, &maxResponseMicros ) < 1 ) { fprintf( stderr, "ERROR: could not get response timeout from %s\n", optarg ); exit( EXIT_FAILURE ); } /* convert from milliseconds to microseconds */ maxResponseMicros *= 1000; break; case 1: /* t_hand */ if( sscanf( optarg, "%"SCNu64, &maxUsedHandMicros ) < 1 ) { fprintf( stderr, "ERROR: could not get player hand timeout from %s\n", optarg ); exit( EXIT_FAILURE ); } /* convert from milliseconds to microseconds */ maxUsedHandMicros *= 1000; break; case 2: /* t_per_hand */ if( sscanf( optarg, "%"SCNu64, &maxUsedPerHandMicros ) < 1 ) { fprintf( stderr, "ERROR: could not get average player hand timeout from %s\n", optarg ); exit( EXIT_FAILURE ); } /* convert from milliseconds to microseconds */ maxUsedPerHandMicros *= 1000; break; case 3: /* start_timeout */ if( sscanf( optarg, "%"SCNd64, &startTimeoutMicros ) < 1 ) { fprintf( stderr, "ERROR: could not get start timeout %s\n", optarg ); exit( EXIT_FAILURE ); } /* convert from milliseconds to microseconds */ if( startTimeoutMicros > 0 ) { startTimeoutMicros *= 1000; } break; } break; case 'f': /* fix the player seats */; fixedSeats = 1; break; case 'l': /* no transactionFile */; useLogFile = 0; break; case 'L': /* use transactionFile */; useLogFile = 1; break; case 'p': /* port specification */ if( scanPortString( optarg, listenPort ) < 0 ) { fprintf( stderr, "ERROR: bad port string %s\n", optarg ); exit( EXIT_FAILURE ); } break; case 'q': quiet = 1; break; case 't': /* no transactionFile */ useTransactionFile = 0; break; case 'T': /* use transactionFile */ useTransactionFile = 1; break; case 'a': append = 1; break; default: fprintf( stderr, "ERROR: unknown option %c\n", i ); exit( EXIT_FAILURE ); } } if( optind + 4 > argc ) { printUsage( stdout, 0 ); exit( EXIT_FAILURE ); } /* get the game definition */ file = fopen( argv[ optind + 1 ], "r" ); if( file == NULL ) { fprintf( stderr, "ERROR: could not open game definition %s\n", argv[ optind + 1 ] ); exit( EXIT_FAILURE ); } game = readGame( file ); if( game == NULL ) { fprintf( stderr, "ERROR: could not read game %s\n", argv[ optind + 1 ] ); exit( EXIT_FAILURE ); } fclose( file ); /* save the seat names */ if( optind + 4 + game->numPlayers > argc ) { printUsage( stdout, 0 ); exit( EXIT_FAILURE ); } for( i = 0; i < game->numPlayers; ++i ) { seatName[ i ] = argv[ optind + 4 + i ]; } /* get number of hands */ if( sscanf( argv[ optind + 2 ], "%"SCNu32, &numHands ) < 1 || numHands == 0 ) { fprintf( stderr, "ERROR: invalid number of hands %s\n", argv[ optind + 2 ] ); exit( EXIT_FAILURE ); } /* get random number seed */ if( sscanf( argv[ optind + 3 ], "%"SCNu32, &seed ) < 1 ) { fprintf( stderr, "ERROR: invalid random number seed %s\n", argv[ optind + 3 ] ); exit( EXIT_FAILURE ); } init_genrand( &rng, seed ); srandom( seed ); /* used for random port selection */ if( useLogFile ) { /* create/open the log */ if( snprintf( name, MAX_LINE_LEN, "%s.log", argv[ optind ] ) < 0 ) { fprintf( stderr, "ERROR: match file name too long %s\n", argv[ optind ] ); exit( EXIT_FAILURE ); } if (append) { logFile = fopen( name, "a+" ); } else { logFile = fopen( name, "w" ); } if( logFile == NULL ) { fprintf( stderr, "ERROR: could not open log file %s\n", name ); exit( EXIT_FAILURE ); } } else { /* no log file */ logFile = NULL; } if( useTransactionFile ) { /* create/open the transaction log */ if( snprintf( name, MAX_LINE_LEN, "%s.tlog", argv[ optind ] ) < 0 ) { fprintf( stderr, "ERROR: match file name too long %s\n", argv[ optind ] ); exit( EXIT_FAILURE ); } if (append) { transactionFile = fopen( name, "a+" ); } else { transactionFile = fopen( name, "w" ); } if( transactionFile == NULL ) { fprintf( stderr, "ERROR: could not open transaction file %s\n", name ); exit( EXIT_FAILURE ); } } else { /* no transaction file */ transactionFile = NULL; } /* set up the error info */ initErrorInfo( maxInvalidActions, maxResponseMicros, maxUsedHandMicros, maxUsedPerHandMicros * numHands, &errorInfo ); /* open sockets for players to connect to */ for( i = 0; i < game->numPlayers; ++i ) { listenSocket[ i ] = getListenSocket( &listenPort[ i ] ); if( listenSocket[ i ] < 0 ) { fprintf( stderr, "ERROR: could not create listen socket for player %d\n", i + 1 ); exit( EXIT_FAILURE ); } } /* print out the final port assignments */ for( i = 0; i < game->numPlayers; ++i ) { printf( i ? " %"PRIu16 : "%"PRIu16, listenPort[ i ] ); } printf( "\n" ); fflush( stdout ); /* print out usage information */ printInitialMessage( argv[ optind ], argv[ optind + 1 ], numHands, seed, &errorInfo, logFile ); /* wait for each player to connect */ gettimeofday( &startTime, NULL ); for( i = 0; i < game->numPlayers; ++i ) { if( startTimeoutMicros >= 0 ) { uint64_t startTimeLeft; fd_set fds; gettimeofday( &tv, NULL ); startTimeLeft = startTimeoutMicros - (uint64_t)( tv.tv_sec - startTime.tv_sec ) * 1000000 - ( tv.tv_usec - startTime.tv_usec ); if( startTimeLeft < 0 ) { startTimeLeft = 0; } tv.tv_sec = startTimeLeft / 1000000; tv.tv_usec = startTimeLeft % 1000000; FD_ZERO( &fds ); FD_SET( listenSocket[ i ], &fds ); if( select( listenSocket[ i ] + 1, &fds, NULL, NULL, &tv ) < 1 ) { /* no input ready within time, or an actual error */ fprintf( stderr, "ERROR: timed out waiting for seat %d to connect\n", i + 1 ); exit( EXIT_FAILURE ); } } addrLen = sizeof( addr ); seatFD[ i ] = accept( listenSocket[ i ], (struct sockaddr *)&addr, &addrLen ); if( seatFD[ i ] < 0 ) { fprintf( stderr, "ERROR: seat %d could not connect\n", i + 1 ); exit( EXIT_FAILURE ); } close( listenSocket[ i ] ); v = 1; setsockopt( seatFD[ i ], IPPROTO_TCP, TCP_NODELAY, (char *)&v, sizeof(int) ); readBuf[ i ] = createReadBuf( seatFD[ i ] ); } /* play the match */ if( gameLoop( game, seatName, numHands, quiet, fixedSeats, &rng, &errorInfo, seatFD, readBuf, logFile, transactionFile ) < 0 ) { /* should have already printed an error message */ exit( EXIT_FAILURE ); } //fflush( stderr ); //fflush( stdout ); // Otherwise the last line or two of the log file // won't be written sometimes when run through a // Ruby interface. fflush(NULL); if( transactionFile != NULL ) { fclose( transactionFile ); } if( logFile != NULL ) { fclose( logFile ); } free( game ); return EXIT_SUCCESS; }
int main( int argc, char **argv ) { int sock, i; pid_t childPID; uint16_t port; ReadBuf *fromServer; fd_set readfds; char line[ READBUF_LEN ]; if( argc < ARG_MIN_ARGS ) { printUsage( stderr ); exit( EXIT_FAILURE ); } /* connect to the server */ if( sscanf( argv[ ARG_SERVERPORT ], "%"SCNu16, &port ) < 1 ) { fprintf( stderr, "ERROR: invalid port %s\n", argv[ ARG_SERVERPORT ] ); exit( EXIT_FAILURE ); } sock = connectTo( argv[ ARG_SERVERNAME ], port ); if( sock < 0 ) { exit( EXIT_FAILURE ); } // EJ additions 9/3/2012 // Turn on keep-alive for socket connection with more frequent checking // than the Linux default. What I've observed is that if a socket // connection is idle for long enough it gets dropped. This only // happens for some users. int on = 1; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) == -1) { fprintf( stderr, "ERROR: setsockopt failed; errno %i\n", errno ); exit( EXIT_FAILURE ); } // Not sure what this should be int num_before_failure = 2; if (setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &num_before_failure, sizeof(num_before_failure)) == -1) { fprintf( stderr, "ERROR: setsockopt failed; errno %i\n", errno ); exit( EXIT_FAILURE ); } // First check after 60 seconds int initial_secs = 60; if (setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &initial_secs, sizeof(initial_secs)) == -1) { fprintf( stderr, "ERROR: setsockopt failed; errno %i\n", errno ); exit( EXIT_FAILURE ); } // Thereafter, also check every 60 seconds int interval_secs = 60; if (setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &interval_secs, sizeof(interval_secs)) == -1) { fprintf( stderr, "ERROR: setsockopt failed; errno %i\n", errno ); exit( EXIT_FAILURE ); } /* set up read buffers */ fromServer = createReadBuf( sock ); /* write to server */ line[0] = 0; for( i = 3; i < argc; ++i ) { strcat( line, argv[i] ); if ( i < argc - 1 ) { strcat( line, " " ); } } strcat( line, "\n" ); int len = strlen(line); if( write( sock, line, len ) < 0 ) { fprintf( stderr, "ERROR: failed while sending to server\n" ); exit( EXIT_FAILURE ); } /* main loop */ while( 1 ) { /* clean up any children */ while( waitpid( -1, NULL, WNOHANG ) > 0 ); /* wait for input */ FD_ZERO( &readfds ); FD_SET( sock, &readfds ); i = select( sock + 1, &readfds, NULL, NULL, NULL ); if( i < 0 ) { fprintf( stderr, "ERROR: select failed\n" ); exit( EXIT_FAILURE ); } if( i == 0 ) { /* nothing ready - shouldn't happen without timeout */ continue; } /* handle server messages */ if( FD_ISSET( sock, &readfds ) ) { /* get the input */ while( ( i = getLine( fromServer, READBUF_LEN, line, 0 ) ) >= 0 ) { if( i == 0 ) { /* This could be an error or could just signify successful completion of all matches */ fprintf( stderr, "Server closed connection\n" ); exit( EXIT_SUCCESS ); } /* check for server commands */ if( strncasecmp( line, "run ", 4 ) == 0 ) { /* split the rest of the line into name ' ' port */ for( i = 4; line[ i ]; ++i ) { if( line[ i ] == ' ' ) { /* found the separator */ line[ i ] = 0; break; } } printf( "starting match %s:%s", &line[ 4 ], &line[ i + 1 ] ); fflush( stdout ); /* run `command machine port` */ childPID = fork(); if( childPID < 0 ) { fprintf( stderr, "ERROR: fork() failed\n" ); exit( EXIT_FAILURE ); } if( childPID == 0 ) { /* child runs the command */ execl( argv[ ARG_BOT_COMMAND ], argv[ ARG_BOT_COMMAND ], &line[ 4 ], &line[ i + 1 ], NULL ); fprintf( stderr, "ERROR: could not run %s\n", argv[ ARG_BOT_COMMAND ] ); exit( EXIT_FAILURE ); } } else { /* just a message, print it out */ if( fwrite( line, 1, i, stdout ) < 0 ) { fprintf( stderr, "ERROR: failed while printing server message\n" ); exit( EXIT_FAILURE ); } fflush( stdout ); if( ! strcmp( line, "Matches finished\n") ) { exit( EXIT_SUCCESS ); } } } } } return EXIT_SUCCESS; }
int main( int argc, char **argv ) { int sock, i; pid_t childPID; uint16_t port; ReadBuf *fromUser, *fromServer; fd_set readfds; char line[ READBUF_LEN ]; if( argc < ARG_NUM_ARGS ) { printUsage( stderr ); exit( EXIT_FAILURE ); } /* connect to the server */ if( sscanf( argv[ ARG_SERVERPORT ], "%"SCNu16, &port ) < 1 ) { fprintf( stderr, "ERROR: invalid port %s\n", argv[ ARG_SERVERPORT ] ); exit( EXIT_FAILURE ); } sock = connectTo( argv[ ARG_SERVERNAME ], port ); if( sock < 0 ) { exit( EXIT_FAILURE ); } /* set up read buffers */ fromUser = createReadBuf( 0 ); fromServer = createReadBuf( sock ); printf( "Log in with 'user password'\n" ); fflush( stdout ); /* main loop */ while( 1 ) { /* clean up any children */ while( waitpid( -1, NULL, WNOHANG ) > 0 ); /* wait for input */ FD_ZERO( &readfds ); FD_SET( 0, &readfds ); FD_SET( sock, &readfds ); i = select( sock + 1, &readfds, NULL, NULL, NULL ); if( i < 0 ) { fprintf( stderr, "ERROR: select failed\n" ); exit( EXIT_FAILURE ); } if( i == 0 ) { /* nothing ready - shouldn't happen without timeout */ continue; } /* handle user input by passing it directly to server */ if( FD_ISSET( 0, &readfds ) ) { /* get the input */ while( ( i = getLine( fromUser, READBUF_LEN, line, 0 ) ) >= 0 ) { if( i == 0 ) { /* Done! */ exit( EXIT_SUCCESS ); } /* write to server */ if( write( sock, line, i ) < 0 ) { fprintf( stderr, "ERROR: failed while sending to server\n" ); exit( EXIT_FAILURE ); } } } /* handle server messages */ if( FD_ISSET( sock, &readfds ) ) { /* get the input */ while( ( i = getLine( fromServer, READBUF_LEN, line, 0 ) ) >= 0 ) { if( i == 0 ) { fprintf( stderr, "ERROR: server closed connection?\n" ); exit( EXIT_FAILURE ); } /* check for server commands */ if( strncasecmp( line, "run ", 4 ) == 0 ) { /* split the rest of the line into name ' ' port */ for( i = 4; line[ i ]; ++i ) { if( line[ i ] == ' ' ) { /* found the separator */ line[ i ] = 0; break; } } printf( "starting match %s:%s", &line[ 4 ], &line[ i + 1 ] ); /* run `command machine port` */ childPID = fork(); if( childPID < 0 ) { fprintf( stderr, "ERROR: fork() failed\n" ); exit( EXIT_FAILURE ); } if( childPID == 0 ) { /* child runs the command */ execl( argv[ ARG_BOT_COMMAND ], argv[ ARG_BOT_COMMAND ], &line[ 4 ], &line[ i + 1 ], NULL ); fprintf( stderr, "ERROR: could not run %s\n", argv[ ARG_BOT_COMMAND ] ); exit( EXIT_FAILURE ); } } else { /* just a message, print it out */ if( fwrite( line, 1, i, stdout ) < 0 ) { fprintf( stderr, "ERROR: failed while printing server message\n" ); exit( EXIT_FAILURE ); } } } } } return EXIT_SUCCESS; }