Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}