Changeset - eb391af42d62
[Not reviewed]
default
0 13 4
Nathan Brink (binki) - 15 years ago 2010-07-21 21:18:20
ohnobinki@ohnopublishing.net
Non-working tabletennis, better poll() resiliency, some fixups to the slave, etc.
17 files changed with 712 insertions and 93 deletions:
0 comments (0 inline, 0 general)
Makefile.am
Show inline comments
 
@@ -20,34 +20,32 @@ libdistrencommon_la_SOURCES = src/common
 
	src/common/execio.c src/common/execio.h \
 
	src/common/misc.c src/common/misc.h \
 
	src/common/multiio.c src/common/multiio.h \
 
	src/common/options.c src/common/options.h \
 
	src/common/protocol.c src/common/protocol.h \
 
	src/common/remoteio.h \
 
	src/common/remoteio.c src/common/libremoteio.h
 
	src/common/remoteio.c src/common/libremoteio.h \
 
	src/common/request.c src/common/request.h
 
#see http://sources.redhat.com/autobook/autobook/autobook_91.html
 
# either increase the revision number or the interface number each release!
 
libdistrencommon_la_LDFLAGS = $(AM_LDFLAGS) -version-info 0:0:0
 

	
 
# shared server sources:
 
SERVER_SOURCES = src/server/slavefuncs.c \
 
	src/server/slavefuncs.h \
 
	src/server/distrenjob.c \
 
	src/server/distrenjob.h
 
SERVER_SOURCES = \
 
	src/server/slavefuncs.c	src/server/slavefuncs.h \
 
	src/server/distrenjob.c	src/server/distrenjob.h
 
# distrend:
 
distrend_CFLAGS = $(AM_CFLAGS) $(MYSQL_CFLAGS)
 
distrend_LDFLAGS = $(AM_LDFLAGS) $(MYSQL_LDFLAGS)
 
distrend_SOURCES = $(SERVER_SOURCES) \
 
	src/server/distrend.c \
 
	src/server/distrend.h \
 
	src/server/user_mgr.c \
 
	src/server/user_mgr.h \
 
	src/server/listen.h \
 
	src/server/listen.c \
 
	src/server/mysql.h \
 
	src/server/mysql.c
 
	src/server/distrend.c	src/server/distrend.h \
 
	src/server/listen.h	src/server/listen.c \
 
	src/server/mysql.h	src/server/mysql.c \
 
	src/server/tabletennis.c	src/server/tabletennis.h \
 
	src/server/user_mgr.c	src/server/user_mgr.h
 

	
 
distrend_LDADD = libdistrencommon.la
 
# distrenslave:
 
distrenslave_SOURCES = $(SERVER_SOURCES) \
 
	src/server/slave.c
 
distrenslave_LDADD = libdistrencommon.la
 

	
src/common/libremoteio.h
Show inline comments
 
@@ -87,12 +87,14 @@ struct remoteio
 
  int sock;
 
#endif
 

	
 
  remoteio_read_handle_func_t read_handler;
 
  /* for the read_handler */
 
  void *read_handler_data;
 
  /* so that read_handler_data can be cleaned up */
 
  remoteio_close_handle_func_t close_handler;
 

	
 
  /**
 
   * Store a buffer of data waiting to be processed.
 
   */
 
  struct remoteio_packet inbuf;
 

	
src/common/multiio.c
Show inline comments
 
@@ -176,24 +176,32 @@ int multiio_poll_invoke_handlers(struct 
 

	
 
int multiio_poll(multiio_context_t context)
 
{
 
  size_t counter;
 
  struct multiio_poll_travinfo travinfo;
 

	
 
  poll(context->pollfds, context->nfds, -1);
 
  int ret;
 

	
 
  ret = poll(context->pollfds, context->nfds, -1);
 
  if(ret == -1)
 
    {
 
      perror("poll");
 
    }
 

	
 
  for(counter = 0; counter < context->nfds; counter ++)
 
    if(context->pollfds[counter].revents)
 
      {
 
	travinfo.multiio = context;
 
	travinfo.socket_info = &context->socket_infos[counter];
 
	travinfo.pollfd = &context->pollfds[counter];
 
	list_traverse(context->socket_types[context->socket_infos[counter].socket_type].type_handlers,
 
		      &travinfo,
 
		      (list_traverse_func_t)&multiio_poll_invoke_handlers,
 
		      LIST_FRNT | LIST_SAVE);
 

	
 
	context->pollfds[counter].revents = 0;
 
      }
 

	
 
  return 0;
 
}
 

	
 
int multiio_socket_type_register(multiio_context_t context, multiio_socket_type_t *new_type)
 
@@ -290,12 +298,13 @@ int multiio_socket_add(multiio_context_t
 
      context->pollfds_len = new_pollfds_len;
 
    }
 

	
 
  pollfds = context->pollfds;
 
  socket_infos = context->socket_infos;
 

	
 
  memset(&pollfds[context->nfds], 0, sizeof(struct pollfd));
 
  pollfds[context->nfds].fd = fd;
 
  pollfds[context->nfds].events = events;
 
  socket_infos[context->nfds].socket_type = socket_type;
 
  socket_infos[context->nfds].socket_data = socket_data;
 

	
 
  context->nfds ++;
src/common/protocol.c
Show inline comments
 
@@ -21,13 +21,13 @@
 
#include "remoteio.h"
 

	
 
#include <malloc.h>
 
#include <stdio.h>
 
#include <string.h>
 

	
 
#define DISTREN_REQUEST_MAGIC (0x32423434)
 
#define DISTREN_REQUEST_MAGIC ((uint32_t)0x32423434)
 

	
 
int distren_request_new(struct distren_request **req, uint32_t len, enum distren_request_type type)
 
{
 
  struct distren_request *newreq;
 

	
 
  newreq = malloc(sizeof(struct distren_request));
src/common/protocol.h
Show inline comments
 
@@ -91,12 +91,13 @@ database */
 

	
 
  };
 

	
 
struct distren_request
 
{
 
  uint32_t magic;
 
  /* the length of the data associated with the packet excluding the header */
 
  uint32_t len;
 
  /** treat type as an enum distren_request_type using casting */
 
  uint32_t /* enum distren_request_type */ type;
 
};
 

	
 
/**
 
@@ -117,12 +118,14 @@ struct remoteio;
 
 */
 
int distren_request_send(struct remoteio *rem, struct distren_request *req, void *data);
 

	
 
/**
 
 * initializes and allocates request based on raw input data
 
 * which includes the headers of the request.
 
 *
 
 * @return 0 on success, 1 on failure
 
 */
 
int distren_request_new_fromdata(struct distren_request **req, void *data, size_t len);
 

	
 
/**
 
 * frees request
 
 */
src/common/remoteio.c
Show inline comments
 
@@ -176,13 +176,14 @@ int remoteio_generic_data_set(struct rem
 
}
 

	
 
int remoteio_open_common(struct remoteio **remoteio,
 
			 enum remoteio_method method,
 
			 struct remoteio_opts *opts,
 
			 remoteio_read_handle_func_t read_handler,
 
			 void *read_handler_data)
 
			 void *read_handler_data,
 
			 remoteio_close_handle_func_t close_handler)
 
{
 
  struct remoteio *rem;
 

	
 
  rem = malloc(sizeof(struct remoteio));
 
  if(!rem)
 
    {
 
@@ -197,12 +198,13 @@ int remoteio_open_common(struct remoteio
 
  rem->opts = opts;
 
  rem->inbuf.data = NULL;
 
  rem->inbuf.len = 0;
 
  rem->outmsgs = q_init();
 
  rem->read_handler = read_handler;
 
  rem->read_handler_data = read_handler_data;
 
  rem->close_handler = close_handler;
 
  /*
 
   * the following initialization is very important... though the
 
   * others are too, I suppose :-p. See remoteio_close()
 
   */
 
  rem->careful_free = 0;
 

	
 
@@ -210,16 +212,17 @@ int remoteio_open_common(struct remoteio
 
}
 

	
 
int remoteio_open_socket(struct remoteio **remoteio,
 
			 struct remoteio_opts *opts,
 
			 remoteio_read_handle_func_t read_handler,
 
			 void *read_handler_data,
 
			 remoteio_close_handle_func_t close_handler,
 
			 int fd)
 
{
 
  struct remoteio *rem;
 
  if(remoteio_open_common(remoteio, REMOTEIO_METHOD_SOCKET, opts, read_handler, read_handler_data))
 
  if(remoteio_open_common(remoteio, REMOTEIO_METHOD_SOCKET, opts, read_handler, read_handler_data, close_handler))
 
    return 1;
 
  rem = *remoteio;
 

	
 
  rem->sock = fd;
 
  multiio_socket_add(opts->multiio, rem->sock, opts->socket_type, rem, POLLRDNORM);
 

	
 
@@ -227,12 +230,13 @@ int remoteio_open_socket(struct remoteio
 
}
 

	
 
int remoteio_open_server(struct remoteio **remoteio,
 
			 struct remoteio_opts *opts,
 
			 remoteio_read_handle_func_t read_handler,
 
			 void *read_handler_data,
 
			 remoteio_close_handle_func_t close_handler,
 
			 const char *servername)
 
{
 
  struct remoteio_server *theserver;
 
  struct remoteio *rem;
 

	
 
  int tmp;
 
@@ -254,13 +258,13 @@ int remoteio_open_server(struct remoteio
 
     || theserver->method < 0)
 
    {
 
      fprintf(stderr, "%s:%d: Unsupported remoteio method %d\n\tThis is a bug, probably indicating memory corruption. This is, of course, probably my fault (not your hardware's) ;-)\n", __FILE__, __LINE__, theserver->method);
 
      return 1;
 
    }
 

	
 
  if(remoteio_open_common(remoteio, theserver->method, opts, read_handler, read_handler_data))
 
  if(remoteio_open_common(remoteio, theserver->method, opts, read_handler, read_handler_data, close_handler))
 
    return 1;
 
  rem = *remoteio;
 

	
 
  tmp = funcmap[theserver->method].open_func(rem, theserver);
 
  if(tmp)
 
    {
 
@@ -481,12 +485,16 @@ int remoteio_close(struct remoteio *rem)
 
  if(rem->careful_free)
 
    {
 
      rem->careful_free = 2;
 
      return 0;
 
    }
 

	
 
  /* call close handler */
 
  if(rem->close_handler)
 
    (*rem->close_handler)(rem->opts->generic_handler_data, rem->read_handler_data);
 

	
 
  /* cleanup multiiio stuff */
 
  multiio_socket_del(rem->opts->multiio, rem->sock);
 

	
 
  /* backend-specific cleanup */
 
  rtn = funcmap[rem->method].close_func(rem);
 

	
src/common/remoteio.h
Show inline comments
 
@@ -46,18 +46,31 @@ struct remoteio;
 
 * you may call remoteio_close() from inside of this function.
 
 *
 
 * @param rem the associated remoteio handle
 
 * @param generic_data a pointer that is stored in remoteio's struct remoteio_opts which isn't client-specific
 
 * @param buf a pointer to the buffer containing data waiting to be processed
 
 * @param len the size of buf that may be accessed
 
 * @param data the pointer passed to remoteio_open. _NOT_ the data just received on the socket.
 
 * @param data the pointer passed to remoteio_open(). _NOT_ the data just received on the socket.
 
 * @return the number of bytes that the function accepted and thus should be removed from the rem handle.
 
 */
 
typedef size_t(*remoteio_read_handle_func_t)(struct remoteio *rem, void *generic_data, void *buf, size_t len, void *data);
 

	
 
/**
 
 * asynchronous close handler which is called whenever remoteio_close() is called.
 
 *
 
 * As reading is now event-oriented and as libremoteio may itself call
 
 * remoteio_close(), you need a way to be informed that a socket is
 
 * being closed. This is particularly important if you have to clean
 
 * up your read_handler_data.
 
 *
 
 * @param generic_data a pointer set by a call to remoteio_generic_data_set().
 
 * @param data the same pointer passed to remoteio_open().
 
 */
 
typedef void (*remoteio_close_handle_func_t)(void *generic_data, void *data);
 

	
 
/**
 
 * Determines the value of generic_data which is passed to
 
 * remoteio_read_handle_func_t
 
 *
 
 * @param opts the remoteio runtime options
 
 */
 
int remoteio_generic_data_set(struct remoteio_opts *opts, void *generic_data);
 
@@ -73,12 +86,13 @@ int remoteio_generic_data_set(struct rem
 
   From this, information about how to make the outgoing connection is derived.
 
 */
 
int remoteio_open_server(struct remoteio **rem,
 
			 struct remoteio_opts *opts,
 
			 remoteio_read_handle_func_t read_handler,
 
			 void *read_handler_data,
 
			 remoteio_close_handle_func_t close_handler,
 
			 const char *servername);
 

	
 
/**
 
 * Initializes a remoteio instance for a socket that's already been floating
 
 * around for a while. I.e., this socket probably came from accept().
 
 *
 
@@ -89,12 +103,13 @@ int remoteio_open_server(struct remoteio
 
 * @param opts self explanatory.
 
 */
 
int remoteio_open_socket(struct remoteio **rem,
 
			 struct remoteio_opts *opts,
 
			 remoteio_read_handle_func_t read_handler,
 
			 void *read_handler_data,
 
			 remoteio_close_handle_func_t close_handler,
 
			 int fd);
 

	
 
/**
 
 * Queue bytes to be written to the remote host.
 
 *
 
 * @param rem the remoteio handle
src/common/request.c
Show inline comments
 
new file 100644
 
/*
 
 * Copyright 2010 Nathan Phillip Brink
 
 *
 
 * This file is a part of DistRen.
 
 *
 
 * DistRen is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * DistRen 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 Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#include "common/protocol.h"
 

	
 
#include <stdlib.h>
 
#include <string.h>
 

	
 
int distren_request_free_with_data(struct distren_request *req, void *data)
 
{
 
  free(data);
 
  return distren_request_free(req);
 
}
 

	
 
uint32_t distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len)
 
{
 
  enum distren_request_type type;
 

	
 
  if(is_ping)
 
    type = DISTREN_REQUEST_PING;
 
  else
 
    type = DISTREN_REQUEST_PONG;
 
  distren_request_new(req, poing_data_len, type);
 
  (*data) = malloc(poing_data_len);
 
  memcpy(*data, poing_cookie, poing_data_len);
 

	
 
  return (uint32_t)poing_data_len;
 
}
src/common/request.h
Show inline comments
 
new file 100644
 
/*
 
 * Copyright 2010 Nathan Phillip Brink
 
 *
 
 * This file is a part of DistRen.
 
 *
 
 * DistRen is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * DistRen 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 Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#ifndef _DISTREN_REQUEST_H
 
#define _DISTREN_REQUEST_H
 

	
 
/**
 
 * @file functions to initialize various requests that the server and
 
 * client may both use.
 
 */
 

	
 

	
 
/**
 
 * Free any request made with one of the functions below
 
 */
 
int distren_request_free_with_data(struct distren_request *req, void *data);
 

	
 
/**
 
 * Initialize a PING or PONG request.
 
 *
 
 * @param data a place to allocate storage for the data associated with this request
 
 * @param is_ping 1 if this is a DISTREN_REQUEST_PING or 0 if this is a DISTREN_REQUEST_PONG
 
 * @param poing_cookie chocolate chip, chocolate chunk, or oatmeal chocolate chip
 
 * @param poing_data_len bytes in the poing_cookie
 
 * @return the length of the data allocated for this request
 
 */
 
uint32_t distren_request_poing(struct distren_request **req, void **data, short is_ping, const void *poing_cookie, size_t poing_data_len);
 

	
 
#endif /* _DISTREN_REQUEST_H */
src/server/distrend.c
Show inline comments
 
@@ -86,13 +86,12 @@ int distrend_config_free(struct distrend
 
int distrend_handle_request(struct distrend_listens *listens, struct distrend_client *client, struct distren_request *req, void *reqdata, struct general_info *geninfo);
 

	
 
/**
 
   client request handlers
 
 */
 
int distrend_handle_version(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data);
 
int distrend_handle_ping(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data);
 

	
 
/* **************XML Functions**************** */
 
void update_general_info(struct general_info *geninfo);
 
int import_general_info(struct general_info *general_info);
 
int update_xml_joblist(struct general_info *geninfo);
 

	
 
@@ -177,23 +176,23 @@ int main(int argc, char *argv[])
 
	  fprintf(stderr, "Error listening on port %d\n", general_info.config->listen_ports[counter]);
 
	  return 1;
 
	}
 
    }
 

	
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_VERSION, &distrend_handle_version);
 
  distrend_listen_handler_add(general_info.config->listens, DISTREN_REQUEST_PING, &distrend_handle_ping);
 

	
 
  int slaveKey = 0; // Remotio should set me on a per-slave basis
 
  /* Main Loop */
 
  general_info.config->die = 0;
 
  while(!general_info.config->die)
 
    {
 
      int clientrequest = 0; /*< temporary example variable, will be replaced when we can handle messages */
 

	
 
      multiio_poll(multiio);
 
      /*      distrend_accept(general_info.config->listens); */
 

	
 
      tabletennis_serve(general_info.config->listens->tabletennis);
 

	
 
      /* Run the watchdog, @TODO: like every 10 mins or something */
 
      frame_watchdog(general_info.conn);
 

	
 
      struct frameset frame;
 
      struct distrenjob *job;
 
@@ -274,30 +273,12 @@ int distrend_handle_version(struct gener
 
      distrend_send_disconnect(client, tmp_str);
 
    }
 

	
 
  return 0;
 
}
 

	
 
int distrend_handle_ping(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data)
 
{
 
  struct distren_request *pong_req;
 

	
 
  if(req_len > 32)
 
    distrend_send_disconnect(client, "You have tried to send a PING packet with a length longer than 32 bytes.");
 

	
 
  /**
 
      respond to the client using the data he sent in his PONG
 
      command.
 
   */
 
  distren_request_new(&pong_req, req_len, DISTREN_REQUEST_PONG);
 
  distrend_client_write_request(client, pong_req, req_data);
 
  distren_request_free(pong_req);
 

	
 
  return 0;
 
}
 

	
 
/**
 
   Performs command stored in a client's request. @TODO: Fill stub
 
*/
 
int distrend_do()
 
{
 
  return 0;
 
@@ -596,12 +577,13 @@ int interactiveTest(int test, multiio_co
 
       break;
 

	
 
     case 5:
 
       while(1)
 
	 {
 
	   multiio_poll(multiio);
 
	   tabletennis_serve(geninfo->config->listens->tabletennis);
 
	 }
 
       break;
 
       
 
     case 0:
 
       test = 0;
 
       break;
src/server/listen.c
Show inline comments
 
@@ -43,13 +43,14 @@ struct distrend_request_handler_info
 
struct distrend_client *distrend_client_new(struct distrend_listens *listens,
 
					    enum distrend_client_state state,
 
					    struct remoteio *rem);
 
int distrend_client_free(struct distrend_client *client);
 
int distrend_dispatch_request(struct distrend_listens *listens, struct remoteio *rem, struct distrend_client *client, struct distren_request *req, void *reqdata);
 
struct distrend_polled_sock *distrend_polled_sock_get_by_offset(struct distrend_listens *listens, size_t pollfds_offset);
 
size_t distrend_listen_read_handle(struct remoteio *rem, struct distrend_listens *listens, void *buf, size_t len, struct distrend_client *client);
 
static size_t distrend_listen_read_handle(struct remoteio *rem, struct distrend_listens *listens, void *buf, size_t len, struct distrend_client *client);
 
static void distrend_listen_remoteio_handle_close(struct distrend_listens *listens, struct distrend_client *client);
 

	
 
int listen_handle_accept(multiio_context_t multiio,
 
			 int fd,
 
			 short revent,
 
			 struct distrend_listens *listens,
 
			 int *port);
 
@@ -91,12 +92,15 @@ struct distrend_listens *distrend_listen
 
  listens->options = opts;
 
  listens->geninfo = geninfo;
 

	
 
  /* multiio */
 
  listens->multiio = multiio;
 

	
 
  /* tabletennis */
 
  listens->tabletennis = tabletennis_new(listens, 32, 16);
 

	
 
  /* This type is used for accepting connections with accept() */
 
  multiio_socket_type_register(multiio, &listens->socket_type);
 

	
 
  multiio_event_handler_register(multiio,
 
				 listens->socket_type,
 
				 POLLERR | POLLHUP | POLLNVAL,
 
@@ -285,22 +289,24 @@ int listen_handle_accept(multiio_context
 

	
 
   newclientsock = accept(fd, (struct sockaddr *)NULL, (socklen_t *)NULL);
 
   /* used to call int distrend_client_add(struct distrend_listens *listens, int sock, DISTREND_CLIENT_PREVERSION)*/
 

	
 
   newclient = distrend_client_new(listens, DISTREND_CLIENT_PREVERSION, NULL);
 

	
 
   if(remoteio_open_socket(&rem, listens->options->remoteio, (remoteio_read_handle_func_t)&distrend_listen_read_handle, newclient, newclientsock))
 
   if(remoteio_open_socket(&rem, listens->options->remoteio, (remoteio_read_handle_func_t)&distrend_listen_read_handle, newclient, (remoteio_close_handle_func_t)&distrend_listen_remoteio_handle_close, newclientsock))
 
     {
 
       fprintf(stderr, "error allocating/adding client struct\n");
 
       return 1;
 
     }
 
   newclient->rem = rem;
 

	
 
   fprintf(stderr, __FILE__ ":%d: This client SHOULD be registered in the table tennis system here\n", __LINE__);
 
   fprintf(stderr, "accepted new connection; fd=%d\n", newclientsock);
 

	
 
   /* tabletennis */
 
   tabletennis_add_client(listens->tabletennis, newclient);
 

	
 
   /**
 
    * For those using netcat/telnet to debug their internets.
 
    */
 
 #ifndef PACKAGE_URL
 
 #define PACKAGE_URL "http://ohnopub.net/distren/"
 
 #endif
 
@@ -414,14 +420,30 @@ size_t distrend_listen_read_handle(struc
 
      return req->len + distrend_listen_read_handle(rem, listens, buf + req->len, len - req->len, client);
 
    }
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * Handle cleaning up after remoteio_close() has been called. This includes cleaning up the struct distrend_client and stuffs
 
 */
 
void distrend_listen_remoteio_handle_close(struct distrend_listens *listens, struct distrend_client *client)
 
{
 
  /*
 
   * remoteio handles removing itself from multiio for us. We just
 
   * have to clean up tabletennis and the struct itself.
 
   */
 

	
 
  tabletennis_del_client(listens->tabletennis, client);
 

	
 
  free(client);
 
}
 

	
 
int distrend_listen_free(struct distrend_listens *listens)
 
{
 
  tabletennis_free(listens->tabletennis);
 
  fprintf(stderr, "%s:%d: I am a stub that needn't be implemented 'til later\n", __FILE__, __LINE__);
 

	
 
  return 1;
 
}
 
/**
 
   This is probably just NOT a placeholder for remotio
 
@@ -453,27 +475,12 @@ struct distrend_client *distrend_client_
 
  client->expectlen = 0;
 
  client->rem = rem;
 

	
 
  return client;
 
}
 

	
 
/**
 
   This function shall only be called after the appropriate
 
   distrend_listen_poll_deletefd() has been called
 

	
 
   @deprecated blah
 
   @todo kill this func?
 
 */
 
int distrend_client_free(struct distrend_client *client)
 
{
 
  fprintf(stderr, " Kaaaaaaa bOOOOOOOOMNMMMM!\n");
 
  abort();
 
  
 
  return 0;
 
}
 

	
 
int distrend_client_write_request(struct distrend_client *client, const struct distren_request *req, const void *data)
 
{
 
  char *towrite;
 
  int ret;
 
  size_t msglen;
 

	
src/server/listen.h
Show inline comments
 
@@ -30,12 +30,13 @@ struct distrend_listens;
 
struct distrend_client;
 

	
 
#ifndef _DISTREN_LISTEN_H
 
#define _DISTREN_LISTEN_H
 

	
 
#include "distrend.h"
 
#include "tabletennis.h"
 

	
 
#include "common/options.h"
 
#include "common/multiio.h"
 
#include "common/protocol.h"
 
#include "common/remoteio.h"
 

	
 
@@ -86,12 +87,14 @@ struct distrend_listens
 
  list_t request_handlers;
 
  /* the data to pass on to all request handlers */
 
  struct general_info *geninfo;
 
  /* the distrend config */
 
  struct options_common *options;
 

	
 
  tabletennis_t tabletennis;
 

	
 
  /* of type (struct distrend_client)  (multiio stores a pointer per socket, we'll store each strut distrend_client as that pointer instead of using this list) */
 
  /* list_t clients; */
 

	
 
  /* the multiio context the listening interface should use/initialize */
 
  multiio_context_t multiio;
 
  /*
 
@@ -118,12 +121,14 @@ struct distrend_client
 
   */
 
  time_t cleanup_time;
 

	
 
  size_t inlen; /*< number of bytes waiting to be processed */
 
  size_t expectlen; /*< number of bytes that inlen has to be for a complete request to be had, 0 when waiting on header */
 

	
 
  struct tabletennis_client tabletennis_client;
 

	
 
  /**
 
   * Yes, even though the remoteio will have a void *pointer to this
 
   * struct, we need a reverse pointer for something like
 
   * distrend_client_write() to work.
 
   */
 
  struct remoteio *rem;
src/server/mysql.c
Show inline comments
 
@@ -199,13 +199,13 @@ distrend_mysql_result_t mysqlQuery(distr
 
    {
 
      fprintf(stderr, "Expected %d columns, got %d for ``%s''\n", expected_columns, num_columns, query);
 
      
 
      mysql_free_result(result);
 
      return NULL;
 
    }
 
  fprintf(stderr,"Done!\n");
 
  fprintf(stderr, "Done!\n");
 

	
 
  /**
 
     Prepare data for return.
 
   */
 
  distrenresult = malloc(sizeof(struct distrend_mysql_result));
 
  if(!distrenresult)
src/server/slave.c
Show inline comments
 
@@ -22,26 +22,49 @@
 

	
 
#include "common/asprintf.h"
 
#include "common/multiio.h"
 
#include "common/options.h"
 
#include "common/protocol.h"
 
#include "common/remoteio.h"
 
#include "common/request.h"
 

	
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <sys/stat.h>
 
#include <unistd.h>
 

	
 
#define DEBUG 0
 

	
 
struct slave_state
 
{
 
  /* whether or not we've gotten past the copyright line */
 
  short copyright_done;
 

	
 
  /* number of bytes that we need to read before we have a whole packet */
 
  size_t expectlen;
 
  /* whether or not we should exit */
 
  int quit;
 

	
 
  /* the server's remoteio handle */
 
  struct remoteio *rem;
 
};
 

	
 
static void distren_slave_remoteio_close_handle(void *blah, void *data);
 
static size_t distren_slave_remoteio_read_handle(struct remoteio *rem,
 
						 void *blah,
 
						 void *buf,
 
						 size_t len,
 
						 void *data);
 

	
 
int main(int argc, char *argv[])
 
{
 
  multiio_context_t multiio;
 

	
 
  struct slave_state slave_state;
 

	
 
  char *datadir;
 
  char *server;
 
  char *username;
 
  char *password;
 
  char *hostname;
 

	
 
@@ -56,14 +79,12 @@ int main(int argc, char *argv[])
 
  cfg_t * my_cfg;
 

	
 
  struct options_common *commonopts;
 

	
 
  // struct distrenjob *myjob; /* Structure to hold data gathered from the XML file - not needed anymore? */
 

	
 
  struct remoteio *comm_slave;
 

	
 
  /**
 
     initializations
 
  */
 
  datadir = NULL;
 
  server = NULL;
 
  username = NULL;
 
@@ -142,20 +163,12 @@ int main(int argc, char *argv[])
 
  if(!strncmp(password, "!password",10))
 
    {
 
      fprintf(stderr, "You haven't specified a password. Please edit distrenslave.conf!\n");
 
      return 1;
 
    }
 

	
 
  fprintf(stderr, "Connecting to server...\n");
 
  if(remoteio_open_server(&comm_slave, commonopts->remoteio, NULL, NULL, server))
 
    {
 
      fprintf(stderr, "Error connecting to server; exiting\n");
 
      return 1;
 
    }
 
  greet_server(comm_slave);
 

	
 
  // Variables needed for main loop
 
  int jobnum = 0;
 
  int framenum = 0;
 
  int slavekey = atoi(username); // @TODO: Make this more friendly
 

	
 
  char *urltoTar;      /* Full URL to the server-side location of job#.tgz */
 
@@ -171,19 +184,31 @@ int main(int argc, char *argv[])
 

	
 
  char *pathtoJob;     /* Full path to job data folder */
 
  char *pathtoJobfile; /* Full path to the job's main file */
 
  char *outputExt = "jpg";     /* Output Extension (e.g., JPG) */
 

	
 
  int haveWork = 0;
 
  int quit = 0;
 

	
 
  fprintf(stderr,"\nDistRen Slave Pre-Alpha %s\n- Experimental build: Use at your own risk!\n\n", PACKAGE_VERSION);
 

	
 
  int benchmarkTime = 0;
 
  int renderPower = 0;
 

	
 
  slave_state.copyright_done = 0;
 
  slave_state.expectlen = 0;
 
  slave_state.quit = 0;
 

	
 
  fprintf(stderr, "Connecting to server...\n");
 
  if(remoteio_open_server(&slave_state.rem, commonopts->remoteio, &distren_slave_remoteio_read_handle, &slave_state, &distren_slave_remoteio_close_handle, server))
 
    {
 
      fprintf(stderr, "Error connecting to server; exiting\n");
 
      return 1;
 
    }
 
  greet_server(slave_state.rem);
 

	
 

	
 
  fprintf(stderr,"\nDistRen Slave Pre-Alpha %s\n- Experimental build: Use at your own risk!\n\n", PACKAGE_VERSION);
 

	
 
  // @TODO: add call to function to force recalc if $render_power == ""
 
  if(runBenchmark)
 
    {
 
      if(slaveBenchmark(datadir, &benchmarkTime, &renderPower))
 
        {
 
          fprintf(stderr,"Benchmark failed! Exiting.\n");
 
@@ -200,18 +225,22 @@ int main(int argc, char *argv[])
 

	
 
  if(!DEBUG)
 
    fprintf(stderr, "Running..");
 

	
 

	
 
  // Main loop
 
  while(!quit)
 
  while(!slave_state.quit)
 
    {
 
      multiio_poll(multiio);
 

	
 
      if(slave_state.quit)
 
	break;
 

	
 
    // request work
 
    fprintf(stderr, "Waiting...\n");
 
    haveWork = getwork(comm_slave, &jobnum, &framenum);
 
    haveWork = getwork(slave_state.rem, &jobnum, &framenum);
 

	
 
    /* If we got a frame */
 
    if(haveWork)
 
      {
 
        fprintf(stderr,"Got work from server...\n");
 
        /* @TODO: Add remotio hooks */
 
@@ -220,19 +249,18 @@ int main(int argc, char *argv[])
 
        // outputExt = remotio)read(outputExt); /* Set output extension from remotio */
 

	
 
        if(DEBUG)
 
          fprintf(stderr, "Preparing to render frame %d in job %d\n", framenum, jobnum);
 

	
 
        prepareJobPaths(jobnum, framenum, outputExt, datadir, &urltoTar, &pathtoTar,&pathtoTardir,&pathtoJob, &pathtoJobfile, &urltoJobfile, &urltoOutput, &pathtoOutput, &pathtoRenderOutput, &pathtoOutdir);
 
        free(outputExt);
 

	
 
        int dlret = downloadTar(urltoTar, pathtoTar);
 
        if(dlret == 0)
 
          fprintf(stderr,"Data retrieved successfully!\n");
 
        else if(dlret == 3){
 
          resetframe(comm_slave, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
          resetframe(slave_state.rem, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
          return 0; // ouput dir doesn't exist
 
        }
 
        else
 
          if(DEBUG)
 
            fprintf(stderr,"Using existing tarball %s...\n", pathtoTar);
 

	
 
@@ -242,13 +270,13 @@ int main(int argc, char *argv[])
 
        if(jstatus == -1){
 
          if(DEBUG)
 
            fprintf(stderr,"Main job file does not exist, extracting...\n");
 

	
 
          // If error unpacking tarball
 
          if(unpackJob(pathtoJob, pathtoTar)){
 
            resetframe(comm_slave, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
            resetframe(slave_state.rem, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
            fprintf(stderr,"Error decompressing tarball! Exiting.\n");
 
            return 1;
 
          }
 
        }
 

	
 
        /* ignore return because directory may exist already */
 
@@ -267,23 +295,23 @@ int main(int argc, char *argv[])
 
        }
 

	
 
        /* Execute blender */
 
        if(exec_blender(pathtoJobfile, pathtoOutput, framenum))
 
          {
 
            fprintf(stderr,"Error running Blender. Check your installation and/or your PATH.\n");
 
            resetframe(comm_slave, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
            resetframe(slave_state.rem, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
            return 1;
 
          }
 
        free(pathtoJobfile);
 
        pathtoJobfile = NULL;
 

	
 
        struct stat buffer;
 
        int fstatus = stat(pathtoOutput, &buffer);
 
        if(fstatus == -1){
 
          fprintf(stderr,"*** %s doesn't exist! Scene may not have camera, or your blender installation is not working.\n", pathtoOutput);
 
          resetframe(comm_slave, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
          resetframe(slave_state.rem, jobnum, framenum);  // Unassign the frame on the server so other slaves can render it
 
          return 1;
 
        }
 
        else{
 
          /* Post-execution */
 
          if(DEBUG)
 
            fprintf(stderr, "Finished frame %d in job %d, uploading...\n", framenum, jobnum);
 
@@ -294,13 +322,24 @@ int main(int argc, char *argv[])
 
          free(urltoOutput);
 
          free(pathtoOutput);
 
          urltoOutput = NULL;
 
          pathtoOutput = NULL;
 

	
 
          // Tell the server that rendering and upload are complete of "jobjum.framenum"
 
          finishframe(comm_slave, jobnum, framenum);
 
          finishframe(slave_state.rem, jobnum, framenum);
 

	
 
  free(urltoTar);
 
  free(pathtoTar);
 
  free(pathtoTardir);
 
  free(pathtoJob);
 
  free(pathtoJobfile);
 
  free(urltoJobfile);
 
  free(urltoOutput);
 
  free(pathtoRenderOutput);
 
  free(pathtoOutdir);
 

	
 
        }
 
     }
 
    else{
 
      if(DEBUG)
 
        fprintf(stderr,"Nothing to do. Idling...\n");
 
      else
 
@@ -320,23 +359,141 @@ int main(int argc, char *argv[])
 
        delete_jobdata(jobnum, datadir);
 
      }
 

	
 
    sleep(5); // Poll 5 seconds. @TODO: Remove all polling
 
  }
 

	
 
  fprintf(stderr,"Closing connection to server...\n");
 
  remoteio_close(comm_slave);
 
  fprintf(stderr, "Quitting...\n");
 

	
 
  free(my_cfg);
 
  free(outputExt);
 
  free(datadir);
 
  free(urltoTar);
 
  free(pathtoTar);
 
  free(pathtoTardir);
 
  free(pathtoJob);
 
  free(pathtoJobfile);
 
  free(urltoJobfile);
 
  free(urltoOutput);
 
  free(pathtoRenderOutput);
 
  free(pathtoOutdir);
 
  fprintf(stderr,"Goodbye!\n");
 
  fprintf(stderr, "Goodbye!\n");
 
  return 0;
 
}
 

	
 
static void distren_slave_remoteio_close_handle(void *blah, void *data)
 
{
 
  struct slave_state *slave_state = (struct slave_state *)data;
 

	
 
  fprintf(stderr, "Lost connection to server\n");
 

	
 
  slave_state->quit = 1;
 
}
 

	
 
static size_t distren_slave_remoteio_read_handle(struct remoteio *rem,
 
						 void *blah,
 
						 void *buf,
 
						 size_t len,
 
						 void *data)
 
{
 
  struct slave_state *slave_state = (struct slave_state *)data;
 

	
 
  struct distren_request *req, *my_req;
 
  void *req_data, *my_req_data;
 

	
 
  size_t to_return;
 

	
 
  size_t counter;
 

	
 
  /* to_return shall record how much of buf we've eaten already */
 
  to_return = 0;
 

	
 
  if(!slave_state->copyright_done)
 
    {
 
      putchar('\n');
 
      /* we have to flush through data until we hit a newline */
 
      for(counter = 0;
 
	  counter + slave_state->expectlen < 256 && counter < len && ((char *)buf)[counter] != '\n';
 
	  counter ++)
 
	{
 
	  putchar(((char *)buf)[counter]);
 
	}
 
      slave_state->expectlen += counter - 1;
 
      if(slave_state->expectlen == 256)
 
	{
 
	  fprintf(stderr, "\nThe server's greeting is too long. Maybe it speaks a foreign language\n");
 
	  slave_state->quit = 1;
 
	}
 
      if(counter < len && ((char *)buf)[counter] == '\n')
 
	{
 
	  putchar('\n');
 
	  counter ++;
 
	  slave_state->expectlen = 0;
 
	  slave_state->copyright_done = 1;
 
	  to_return += counter;
 
	  buf += counter;
 
	  len -= counter;
 
	}
 
      if(!slave_state->copyright_done)
 
	return to_return;
 
    }
 

	
 
  while(1)
 
    {
 
      /* if we haven't read a full header yet: */
 
      if(!slave_state->expectlen)
 
	{
 
	  if(len < sizeof(struct distren_request))
 
	    return 0;
 

	
 
	  /* figure out how much we need to read in before we can get anywhere */
 
	  if(distren_request_new_fromdata(&req, buf, len))
 
	    {
 
	      fprintf(stderr, "Failing to interpret data from server, exiting\n");
 
	      slave_state->quit = 1;
 
	      return 0;
 
	    }
 
	  slave_state->expectlen = sizeof(struct distren_request) + req->len;
 
	  distren_request_free(req);
 
	}
 

	
 
      if(slave_state->expectlen
 
	    && slave_state->expectlen <= len)
 
	{
 
	  distren_request_new_fromdata(&req, buf, len);
 
	  req_data = buf + sizeof(struct distren_request);
 

	
 
	  switch((enum distren_request_type)req->type)
 
	    {
 
	    case DISTREN_REQUEST_VERSION:
 
	      fprintf(stderr, "The server runs ");
 
	      for(counter = 0; counter < req->len; counter ++)
 
		putc(((char *)req_data)[counter], stderr);
 
	      putc('\n', stderr);
 
	      break;
 

	
 
	    case DISTREN_REQUEST_PING:
 
	      fprintf(stderr, "PONG ! :-D\n");
 

	
 
	      distren_request_poing(&my_req, &my_req_data, 0, req_data, req->len);
 
	      remoteio_write(slave_state->rem, &my_req, sizeof(struct distren_request));
 
	      remoteio_write(slave_state->rem, &my_req_data, req->len);
 
	      distren_request_free_with_data(my_req, my_req_data);
 
	      break;
 

	
 
	    case DISTREN_REQUEST_DISCONNECT:
 
	      /* hopefully this ends up being a useful message... */
 
	      printf("You have been disconnected: \"");
 
	      for(counter = 0; counter < req->len; counter ++)
 
		putchar(((char *)buf)[counter]);
 
	      putchar('"');
 
	      putchar('\n');
 
	      break;
 

	
 
	    default:
 
	      fprintf(stderr, "something\n");
 
	      break;
 
	    }
 

	
 
	  distren_request_free(req);
 
	  slave_state->expectlen = 0;
 

	
 
	  counter = req->len + sizeof(struct distren_request);
 

	
 
	  len -= counter;
 
	  buf += counter;
 
	  to_return += counter;
 
	}
 
    }
 

	
 
  return to_return;
 
}
src/server/slavefuncs.c
Show inline comments
 
@@ -841,12 +841,15 @@ int login_user(char *username)
 
 *
 
 * @deprecated THIS FUNCTION SHOULD DIE VERY, VERY SOON!
 
 * (and painfully :-p)
 
*/
 
int sendSignal(struct remoteio *rem, char signal)
 
{
 

	
 
  fprintf(stderr, __FILE__ ":%d: Ignoring call to DEPRECATED and WRONG sendSignal() function. See similar message for sendExtSignal() for details\n", __LINE__);
 
  return 1;
 
  remoteio_write(rem, &signal, 1);
 

	
 
  /**
 
     if remoteio_write returned 1, the connection
 
     is probably dead or there was a real error
 
   */
 
@@ -861,12 +864,16 @@ int sendSignal(struct remoteio *rem, cha
 
 * @deprecated lol
 
 */
 
int sendExtSignal(struct remoteio *rem, char signal, char *data)
 
{
 
  size_t len;
 
  char *ssignal;
 

	
 
  fprintf(stderr, __FILE__ ":%d: Ignoring call to DEPRECATED and WRONG sendExtSignal() function. First of all, this function's name is camelCase. Secondly, it doesn't use protocol.h properly. I'll show how later. Thirdly, when protocol.h and request.h (still being written) are used properly, you don't need sendExtSignal() at all.\n", __LINE__);
 
  return 1;
 

	
 
  /**
 
   * Just append the data FIXME: We should do this differently
 
   */
 
  _distren_asprintf(&ssignal, "%c%s", signal, data);
 
  len = strlen(ssignal);
 
  remoteio_write(rem, ssignal, len);
src/server/tabletennis.c
Show inline comments
 
new file 100644
 
/*
 
 * Copyright 2010 Nathan Phillip Brink
 
 *
 
 * This file is a part of DistRen.
 
 *
 
 * DistRen is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * DistRen 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 Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#include "distrend.h"
 
#include "listen.h"
 
#include "tabletennis.h"
 

	
 
#include "common/request.h"
 
#include "common/protocol.h"
 

	
 
#include <queue.h>
 
#include <stdlib.h>
 
#include <string.h>
 
#include <time.h>
 

	
 
struct tabletennis
 
{
 
  unsigned int ping_interval;
 
  unsigned int pong_time;
 

	
 
  /* of type (struct distrend_client *) */
 
  queue_t clients_to_ping;
 

	
 
  /* of type (struct distrend_client *) */
 
  queue_t clients_need_pong;
 

	
 
  struct timespec time_last_check;
 
};
 

	
 
static int tabletennis_pong_request_handle(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data);
 
static int tabletennis_ping_request_handle(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data);
 

	
 
tabletennis_t tabletennis_new(struct distrend_listens *listens, unsigned int ping_interval, unsigned int pong_time)
 
{
 
  tabletennis_t tabletennis;
 

	
 
  tabletennis = malloc(sizeof(struct tabletennis));
 

	
 
  tabletennis->ping_interval = ping_interval;
 
  tabletennis->pong_time = pong_time;
 
  tabletennis->clients_to_ping = q_init();
 
  tabletennis->clients_need_pong = q_init();
 
  clock_gettime(CLOCK_MONOTONIC, &tabletennis->time_last_check);
 

	
 
  distrend_listen_handler_add(listens, DISTREN_REQUEST_PING, &tabletennis_ping_request_handle);
 
  distrend_listen_handler_add(listens, DISTREN_REQUEST_PONG, &tabletennis_pong_request_handle);
 

	
 
  return tabletennis;
 
}
 

	
 
int tabletennis_add_client(tabletennis_t tabletennis, struct distrend_client *client)
 
{
 
  client->tabletennis_client.state = TABLETENNIS_NEED_PING;
 
  client->tabletennis_client.time_next_check = tabletennis->time_last_check.tv_sec
 
    + tabletennis->ping_interval;
 

	
 
  q_enqueue(tabletennis->clients_to_ping, client, 0);
 

	
 
  return 0;
 
}
 

	
 
int tabletennis_serve(tabletennis_t tabletennis)
 
{
 
  struct timespec time_now;
 
  struct distrend_client *client;
 
  time_t time_next_check;
 

	
 
  struct distren_request *req;
 
  void *req_data;
 
  size_t req_len;
 

	
 
  clock_gettime(CLOCK_MONOTONIC, &time_now);
 

	
 
  time_next_check = time_now.tv_sec + tabletennis->pong_time;
 
  for(client = q_front(tabletennis->clients_to_ping);
 
      client && client->tabletennis_client.time_next_check < time_now.tv_sec;
 
      client = q_front(tabletennis->clients_to_ping))
 
    {
 
      q_dequeue(tabletennis->clients_to_ping);
 

	
 
      /* use time_next_check as the ping data */
 
      req_len = distren_request_poing(&req, &req_data, 1, &time_next_check, sizeof(time_next_check));
 
      distrend_client_write_request(client, req, req_data);
 
      distren_request_free_with_data(req, req_data);
 

	
 
      client->tabletennis_client.state = TABLETENNIS_NEED_PONG;
 

	
 
      client->tabletennis_client.time_next_check = time_next_check;
 

	
 
      q_enqueue(tabletennis->clients_need_pong, client, 0);
 
    }
 

	
 
  time_next_check = time_now.tv_sec + tabletennis->ping_interval;
 
  for(client = q_front(tabletennis->clients_need_pong);
 
      client && client->tabletennis_client.time_next_check < time_now.tv_sec;
 
      client = q_front(tabletennis->clients_need_pong))
 
    {
 
      q_dequeue(tabletennis->clients_need_pong);
 

	
 
      if(client->tabletennis_client.state == TABLETENNIS_NEED_PONG)
 
	{
 
	  distrend_send_disconnect(client, "You failed to respond to a PING packet after some seconds!");
 
	  client->tabletennis_client.state = TABLETENNIS_DELINQUENT;
 
	}
 
      else /* state must be TABLETENNIS_HAS_PONG */
 
	{
 
	  client->tabletennis_client.time_next_check = time_next_check;
 
	  client->tabletennis_client.state = TABLETENNIS_NEED_PING;
 

	
 
	  q_enqueue(tabletennis->clients_to_ping, client, 0);
 
	}
 
    }
 

	
 
  /* for tabletennis_add_client() */
 
  memcpy(&tabletennis->time_last_check, &time_now, sizeof(struct timespec));
 

	
 
  return 0;
 
}
 

	
 
/**
 
 * helper for tabletennis_del_client() which just looks for a
 
 * particular pointer in the queue (a.k.a., list).
 
 */
 
static int tabletennis_del_client_traverse(void *trav_data, void *client)
 
{
 
  if(trav_data == client)
 
    return FALSE;
 

	
 
  return TRUE;
 
}
 

	
 
int tabletennis_del_client(tabletennis_t tabletennis, struct distrend_client *client)
 
{
 
  list_t thelist;
 

	
 
  thelist = NULL;
 
  switch(client->tabletennis_client.state)
 
    {
 
    case TABLETENNIS_NEED_PING:
 
      thelist = tabletennis->clients_to_ping;
 
      break;
 

	
 
    case TABLETENNIS_NEED_PONG:
 
    case TABLETENNIS_HAS_PONG:
 
      thelist = tabletennis->clients_need_pong;
 
      break;
 

	
 
    case TABLETENNIS_DELINQUENT:
 
      thelist = NULL;
 
      break;
 
    }
 

	
 
  if(!thelist)
 
    return 0;
 

	
 
  list_traverse(thelist, (void *)client, (list_traverse_func_t)&tabletennis_del_client_traverse,
 
		LIST_FORW | LIST_FRNT | LIST_SAVE);
 
  if(list_curr(thelist) == client)
 
    list_remove_curr(thelist);
 

	
 
  return 0;
 
}
 

	
 
void tabletennis_free(tabletennis_t tabletennis)
 
{
 
  q_free(tabletennis->clients_to_ping, LIST_NODEALLOC);
 
  q_free(tabletennis->clients_need_pong, LIST_NODEALLOC);
 

	
 
  free(tabletennis);
 
}
 

	
 
/* implementations of locals */
 

	
 
/**
 
 * Handles a DISTREN_REQUEST_PING from a client.
 
 *
 
 * @todo throttling?
 
 */
 
static int tabletennis_ping_request_handle(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data)
 
{
 
  struct distren_request *pong_req;
 

	
 
  if(req_len > 32)
 
    distrend_send_disconnect(client, "You have tried to send a PING packet with a length longer than 32 bytes.");
 

	
 
  /**
 
      respond to the client using the data he sent in his PONG
 
      command.
 
   */
 
  distren_request_new(&pong_req, req_len, DISTREN_REQUEST_PONG);
 
  distrend_client_write_request(client, pong_req, req_data);
 
  distren_request_free(pong_req);
 

	
 
  return 0;
 
}
 

	
 
static int tabletennis_pong_request_handle(struct general_info *geninfo, struct distrend_client *client, size_t req_len, void *req_data)
 
{
 
  fprintf(stderr, "got pong\n");
 

	
 
  /*
 
   * The only place that sends PINGs from distrend is
 
   * tabletennis_serve() and it uses
 
   * client->tabletennis_client.time_next_check as the cookie.
 
   */
 
  if(client->tabletennis_client.state == TABLETENNIS_NEED_PONG
 
     && req_len == sizeof(client->tabletennis_client.time_next_check)
 
     && !memcmp(req_data, &client->tabletennis_client.time_next_check, sizeof(client->tabletennis_client.time_next_check)))
 
    {
 
      /* valid match */
 
      client->tabletennis_client.state = TABLETENNIS_HAS_PONG;
 

	
 
      return 0;
 
    }
 
  /* no match or invalid state */
 
  distrend_send_disconnect(client, "You tried to send a PONG packet without first receiving a PING packet.");
 
  return 0;
 
}
src/server/tabletennis.h
Show inline comments
 
new file 100644
 
/*
 
 * Copyright 2010 Nathan Phillip Brink
 
 *
 
 * This file is a part of DistRen.
 
 *
 
 * DistRen is free software: you can redistribute it and/or modify
 
 * it under the terms of the GNU Affero General Public License as published by
 
 * the Free Software Foundation, either version 3 of the License, or
 
 * (at your option) any later version.
 
 *
 
 * DistRen 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 Affero General Public License for more details.
 
 *
 
 * You should have received a copy of the GNU Affero General Public License
 
 * along with DistRen.  If not, see <http://www.gnu.org/licenses/>.
 
 */
 

	
 
#ifndef _DISTREN_TABLETENNIS_H
 
#define _DISTREN_TABLETENNIS_H
 

	
 
/**
 
 * @file For managing the PINGs and PONGs of our present life.
 
 *
 
 * Do not call ping pong ping pong! Any professional ping-ponger will
 
 * be immediately offended. Use tabletennis instead and prosper.
 
 */
 

	
 
#include "distrend.h"
 

	
 
#include <queue.h>
 
#include <time.h>
 

	
 
struct tabletennis;
 
typedef struct tabletennis *tabletennis_t;
 

	
 
/**
 
 * this struct should be embedded into struct distrend_client
 
 */
 
struct tabletennis_client
 
{
 
  /*
 
   * the time that the client should be processed from the
 
   * clients_to_ping or clients_need_pong queues.
 
   */
 
  time_t time_next_check;
 

	
 
  enum tabletennis_client_state
 
    {
 
      /* in clients_to_ping */
 
      TABLETENNIS_NEED_PING,
 
      /* in clients_need_pong */
 
      TABLETENNIS_NEED_PONG,
 
      TABLETENNIS_HAS_PONG,
 
      /* not in any queue */
 
      TABLETENNIS_DELINQUENT,
 
    } state;
 
};
 

	
 
/**
 
 * Initializes the context data for tabletennis.
 
 *
 
 * @param listens to register tabletennis's PING and PONG handlers
 
 * @param ping_interval The number of seconds to wait before sending a client a PING packet.
 
 * @param pong_time The number of seconds to wait before checking if a client sent a PONG packet.
 
 */
 
tabletennis_t tabletennis_new(struct distrend_listens *listens, unsigned int ping_interval, unsigned int pong_time);
 

	
 
/**
 
 * Add a client to the tabletennis game.
 
 *
 
 * @param tabletennis the tabletennis context.
 
 * @param client the client
 
 */
 
int tabletennis_add_client(tabletennis_t tabletennis, struct distrend_client *client);
 

	
 
/**
 
 * Sends PING packets to clients that need them and checks that
 
 * clients have sent PONG packets on time.
 
 *
 
 * @param clients_to_ping a queue initialized
 
 */
 
int tabletennis_serve(tabletennis_t tabletennis);
 

	
 
/**
 
 * Remove a client to the tabletennis game.
 
 *
 
 * @param tabletennis the tabletennis context.
 
 * @param client the client
 
 */
 
int tabletennis_del_client(tabletennis_t tabletennis, struct distrend_client *client);
 

	
 
/**
 
 * Deallocates and frees the tabletennis context data.
 
 *
 
 * Does not free the distren_client structs
 
 *
 
 * @param tabletennis the tabletennis context.
 
 */
 
void tabletennis_free(tabletennis_t tabletennis);
 

	
 
#endif
0 comments (0 inline, 0 general)